var hiresAttribute = "resizable=1,location=0,scrollbars=1,menubar=0,status=0,toolbar=0,top=0,left=0,width="+(screen.width-10)+",height="+(screen.height-72);

if (navigator.appName == "Microsoft Internet Explorer")
{
   //alert ("ie");
   // Create a fake console, just to keep IE from complaining
   function consoletype() { this.log = function(text) {  }; }
   var console = new consoletype();
};


// TODO: unload images that are no longer needed to save memory?

// a Slide constructor
function slide() {
  // Create an image object for the slide
  if (document.images) {
    this.image = new Image();
  }
}

   // Define some common slide properties
   slide.prototype.movieflag = false;
   slide.prototype.loaded = false;
   slide.prototype.index = -1; // a handy unique id for debugging
   // These properties are set by caller:
   //  timestamp, caption, src, credit, description

   //--------------------------------------------------
   slide.prototype.load = function()
   {
      // This method loads the image for the slide
      if (!document.images) { return; }

      if (!this.loaded && this.src != undefined && !this.movieflag) 
      {
        ip = new ImagePreloader(this.src, bind(this, "setLoaded"));
        this.image.src = this.src;
        //5 console.log("[img " + this.index + "] preloading");
      }
      //else
      //{
      //   //4 console.log("[img " + this.index + "] preload skipped: loaded["+this.loaded+"]  movie["+this.movieflag+"]");
      //}
   };

   //--------------------------------------------------
   slide.prototype.saveHires = function() 
   {
      // This method uses the slides link, except places it inside the context of
      // a CGI script that forces the browser to download it instead of opening
      // it.

      // If this slide does not have a link, do nothing
      if (!this.hires) return;

      // Build a path to the image, since it's locally scoped and our script lives
      // in cgi-bin
      path = window.location.pathname;
      path = path.substring(0, path.lastIndexOf('/'));

      saveurl = this.hires;
      
      location.href = window.location.protocol + "//" + window.location.host +
                      "/cgi-bin/download.cgi?file=" + path + "/" + saveurl;
      return ;
   };

   //--------------------------------------------------
   slide.prototype.showHires = function() 
   {
      var mywindow = window.open(this.hires, "_blank", hiresAttribute);

      // Pop the window to the front
      if (mywindow && window.focus) mywindow.focus();
   };


   //--------------------------------------------------
   slide.prototype.setMovie = function(movieURL) 
   {
      path = window.location.pathname;
      path = path.substring(0, path.lastIndexOf('/'));
      this.movie     = movieURL;
      this.movieURL  = path + "/" + movieURL;
      this.movieflag = true;
      this.src       = "";
      this.loaded = true; // there is no preloading of flash
   };

   slide.prototype.setLoaded = function()
   {
      //5 console.log("[img " + this.index + "] LOADED");
      this.loaded = true;
   };
   
   slide.prototype.isLoaded = function()
   {
      //4 console.log("[img " + this.index + "] isLoaded:" + this.loaded);
      return this.loaded;
      // return false;
   };

////////////////////////////////////////////////////////////////////////////////

//==================================================
// slideshow object
//==================================================
function slideshow( slideshowname ) {
  // This is the constructor function for the slideshow object.
  // It is called automatically when you create a new object.
  // For example:
  // ss = new slideshow("ss");

  // Name of this object
  // (required if you want your slideshow to auto-play)
  // For example, "SLIDES1"
  this.name = slideshowname;

  this.cleanMode = false;
  this.sizeDown = false;

  // These are private variables
  this.slides = new Array();

  // Elements to update
  this.elemState    = getElementById("slideshow_state");  // slideshow state
  this.elemSlidenum = getElementById("photoPage_count");    // the current element to update
  this.fadeState    = getElementById("slideshow_fade"); 

  // this.elemSlidenum = this.getElementById("slideshow_state");  // slide number

  this.displayElements = new Array(getElementById('photo_image_1'),
                                 getElementById('photo_image_2'));
}

// Slideshow Object variables
  // Number of images to pre-fetch.
  // -1 = preload all images.
  //  0 = load each image is it is used.
  //  n = pre-fetch n images ahead of the current image.
  slideshow.prototype.prefetch = 3;

  // seconds to pause between slides.
  slideshow.prototype.timeout = 4;

  // milleseconds to poll for the next image to load
  slideshow.prototype.loadTimer = 250;
  slideshow.prototype.maxLoadTime = 8000; // wait for N ms before forcing an advance to the next image
  slideshow.prototype.currentLoadTime = 0; // a counter used by the load poller

  // Is the slideshow paused?  If so, set to 1
  slideshow.prototype.paused = 1;

  slideshow.prototype.enable_fade = 1;
  slideshow.prototype.crossfade_duration = 500;
  slideshow.prototype.fade_interval = 50;

  slideshow.prototype.current = 0;
  slideshow.prototype.timeoutid = 0;

  slideshow.prototype.displayHTML = "";

  // repeat mode
  slideshow.prototype.repeat = 0;

  // Image sizing tips
  slideshow.prototype.defaultHeight = "";

  slideshow.prototype.targetHeight = 420;
  slideshow.prototype.targetWidth = 560;



   //--------------------------------------------------
   // Public methods
   //--------------------------------------------------
   slideshow.prototype.add_slide = function(slide) 
   {
     // Add a slide to the slideshow.
     var i = this.slides.length;
 
     // Prefetch the slide image if necessary
     if (this.prefetch == -1) { slide.load(); }
     this.slides[i] = slide;
 
     // Add an index to the slide so we can find it easier
     this.slides[i].index = i;
   };
 
   //--------------------------------------------------
 
   ////////////////////////////////////////////////////////////////////////////////
   // User interactions - these are what the html enable
   ////////////////////////////////////////////////////////////////////////////////
   slideshow.prototype.play = function(timeoutArg)
   {
      // Are we already playing?  If so, don't do anything unless timeoutArg is different than before
      if ( timeoutArg && this.timeout != timeoutArg )
      {
         this.timeout = timeoutArg;
      }

      // if we are paused, call the play function and advance the slide
      if ( this.paused && (this.current < this.slides.length - 1))
      {
         this.playfcn(1);
      }
   };

   slideshow.prototype.pause = function()
   {
      this.pausefcn();
   };

   slideshow.prototype.next = function()
   {
      this.nextfcn();
   };
   slideshow.prototype.previous = function()
   {
      this.previousfcn();
   };

   slideshow.prototype.restart = function() { this.current = -1; this.playfcn(1); } ;

   //--------------------------------------------------
   slideshow.prototype.playfcn = function(advanceSlide) 
   {
      this.paused = 0;
      if ( advanceSlide ) {
         this.nextfcn();
            // the update fcn, called inside next, will pause if a video
      } else {
         this.setSlideTimer();
      }
   };
 
   //--------------------------------------------------------------------------------
   // Set the slide timer to advance to the next slide.  This script is based on
   // singular timer events, rather than a set interval, allowing more user
   // control
   slideshow.prototype.setSlideTimer = function()
   {
      // Just to be certain, clear out the previous slide timer
      this.clearSlideTimer();

      // before setting the timer - ensure the current image is loaded first.  If it's not, poll it for awhile
      // - once it's loaded, set the timer for the next image...or did we just run out of time
      if ( !this.slides[this.current].isLoaded() && ( this.currentLoadTime < this.maxLoadTime ) )
      {
         //5 console.log("[img " + this.current + "] still not loaded");
         // if we're here, just set the timeout to come around again
         this.currentLoadTime += this.loadTimer;
        
         // Update the slideshow status
         dotstring = "";
         for ( i = 0; i <= (this.currentLoadTime/1000); i++ ) { dotstring += "."; }
         if ( this.elemState != undefined ) {
            this.elemState.innerHTML = dotstring + "waiting for the image to load" + dotstring;
         }

         this.timeoutid = setTimeout( bind(this, "setSlideTimer"), this.loadTimer);
      }
      else
      {
         //5 console.log("[img " + this.current + "] IS NOW LOADED or ran out of time - setting normal slide timer");

         // if we're in play mode, set the clock for the next slide update
         if ( !this.paused ) {
            this.setSlideAdvanceTimer();
         } else {
            this.pausefcn();
         }
      
         // Now that we've loaded the current image, preload any future images
         this.prefetchImages();
       }
   };

   slideshow.prototype.setSlideAdvanceTimer = function()
   {
      // If the current slide has a custom timeout, use it;
      // otherwise use the default timeout, unless an arg was passed in
      var timeout = this.timeout;
      if (typeof this.slides[ this.current ].timeout != 'undefined') {
        timeout = this.slides[ this.current ].timeout;
      } 

      // are we at the end of the slideshow?  if so, don't schedule another event
      if ( (this.current >= this.slides.length - 1) && !this.repeat )
      {
         // do nothing
      }
      else
      {
         // After the timeout, call this.next(), indicating that it is auto advanced
         //4 console.log("timeout set " + this.current);
         this.timeoutid = setTimeout( this.name + ".nextfcn()", timeout * 1000);

         // Update the slideshow status
         if ( this.elemState != undefined ) {



            this.elemState.innerHTML = "Slideshow is Playing";
         }

      }
   };

   //--------------------------------------------------------------------------------
   slideshow.prototype.clearSlideTimer = function()
   {
     if (this.timeoutid != 0)
     {
       clearTimeout(this.timeoutid);
       this.timeoutid = 0;
     }
   };

   //--------------------------------------------------------------------------------
   // if the user advances a slide, it should clear the timeout for the current image
   slideshow.prototype.clearLoadWait = function()
   {  
     if ( this.currentLoadTime != 0 ) { 
        this.clearSlideTimer(); 
        this.currentLoadTime = 0;
     }
   };
 
   //--------------------------------------------------------------------------------
   slideshow.prototype.pausefcn = function() 
   {
     // This method stops the slideshow if it is automatically running.
     this.clearSlideTimer();
 
     // Update the slideshow status
     if ( this.elemState != undefined ) {
        this.elemState.innerHTML = "<font color='yellow'><b>Slideshow is Paused</b></font>";
     }
     this.paused = 1;
   };
 
   slideshow.prototype.prefetchImages = function()
   {
      if (this.prefetch > 0) 
      {
          //4 console.log("--beginning prefetch of other images--");
          var next, prev, count;
 
          // Pre-fetch the next slide image(s)
          next = this.current;
          prev = this.current;
          count = 0;
          do {
             // Get the next and previous slide number
             // Loop past the ends of the slideshow if necessary
             if (++next >= this.slides.length) next = 0;
             if (--prev < 0) prev = this.slides.length - 1;
 
             // Preload the slide image
             this.slides[next].load();
             this.slides[prev].load();
 
             // Keep going until we have fetched
             // the designated number of slides
          } while (++count < this.prefetch);
      } // if prefetch
   };

   //--------------------------------------------------
   slideshow.prototype.update = function() 
   {
     // This method updates the slideshow image on the page and will pause the
     // show if a video is displayed
 
     // Convenience variable for the current slide
     var slide = this.slides[ this.current ];
 
     slide.load();
     // if ( !slide.movieflag ) 
 
     // Update the image.
     // this.image.src = slide.image.src;
 
 
     // Build up the HTML around this image
     var slideCount = "";
     var creditNoun = "Photo by ";
     var closeLink = "";
 
 
     this.displayHTML = "";
 
     // Don't check for cleanMode, we'll assume no hires
     if ( slide.hires ) 
     {
        this.displayHTML += "<p id=photoPage_clickToSave>";
        // If this is a movie, display slightly different text
        if ( slide.movieflag )
        {
           this.displayHTML += "<a href='javascript:" +
                               this.name + ".saveHires();'>" + 
                               "Click here to download this video</a>"; 
        }
        else
        {
           // Update the text
           this.displayHTML += "Click on the photo to view it larger, " + 
                               "or <a href='javascript:" + 
                               this.name + ".saveHires();'>" + 
                               "click here to download the image.</a>" + 
                               "The slideshow will pause automatically.";
        }
        this.displayHTML += "</p>";
     }
 
     // Is this a movie?  If so, add the movie display text
     if ( slide.movieflag )
     {
        this.displayHTML += flashEmbedCode(slide);
        this.pausefcn(); //pause the video
        creditNoun = "Video by ";
     }
     else
     {
        var linkOpen = "";
        var linkClose = "";
        if ( slide.hires ) 
        {
           linkOpen  = "<a href=\"javascript:" + this.name + ".showHires()\">";
           linkClose = "</a>";
        }
        this.displayHTML += "<p id=\"photoPage_photoBlock\">" +
                            linkOpen +
                            "<img id=\"photoPage_image_local\" src=\"" +
                            slide.image.src + "\"" + this.defaultHeight +
                            "/>" + linkClose + "</p>";

     }
     // End movie checkout section
 
     if ( this.cleanMode != true ) 
     {
        if ( slide.credit != undefined ) 
        {
          this.displayHTML += "<p id=photoPage_credit>" + creditNoun +
                                slide.credit + "</p>";
        }
    
        this.displayHTML += "<p id=photoPage_caption>" + slide.caption + "</p>";
    
        if ( slide.timestamp != undefined ) {
           this.displayHTML += "<p id=photoPage_timeStamp>" + slide.timestamp + "</p>";
        }
    
        if ( slide.description != undefined ) {
           this.displayHTML += "<p id=photoPage_desc>" + slide.description + "</p>";
        }
 
        // Add the "Get Flash" link to the text block:
        if ( slide.movieflag )
        {
           this.displayHTML += '<br><br><p id=photoPage_getFlash>Video Not Working?  Download the free Adobe Flash Player<br><a href="http://www.macromedia.com/go/getflashplayer" target="_blank" ><img src="/images/get_flash_player.gif" width=88 height=31 border="0"></a></p>';
    
           // set the flag to draw this after the fade completes
           this.postFadeUpdate = 1;
        }
       
        // update the slide number to the new slide
        if ( this.elemSlidenum != undefined ) {
           this.elemSlidenum.innerHTML = (this.current + 1) + " of " + this.slides.length;
        }
     }
 
     this.displayItem();
   };
 
   //--------------------------------------------------
   // The HTML next button
   slideshow.prototype.nextfcn = function() 
   {
      this.current++;
      if ( this.current < this.slides.length ) 
      {
         this.showSlide();
      }
      else if ( this.current == this.slides.length  )
      {
         if ( !this.repeat ) 
         {
            // Pause before we update the status, because pause will change the status too...
            this.pausefcn();
            this.current = this.slides.length - 1;
            this.showSlide();
            if ( this.elemState != undefined ) {
               this.elemState.innerHTML = "<b>Slideshow is complete</b><br>" + 
                                       "<a href='javascript:" + this.name + ".restart()" +
                                       "'>Click here to restart the slideshow</a>";
            }

         }
         else
         {
            this.current = 0;
            this.showSlide();
         }
      }
      else
      {
         this.current = this.slides.length - 1;
      }
   };
 
   //--------------------------------------------------
   slideshow.prototype.previousfcn = function() 
   {
      this.current--;
      if ( this.current >= 0 )
      {
         this.showSlide();
      }
      else // if ( this.current < 0 )
      {
         if ( this.repeat ) {
            this.current = this.slides.length - 1;
            this.showSlide();
         } else {
            this.current = 0;
         }
      }
   };

  
   slideshow.prototype.showSlide = function()
   {
     this.clearLoadWait();

     // update may pause the show, if it's a video, so it must come before the setSlideTimer method
     this.update();
     this.setSlideTimer();
   };
 
   slideshow.prototype.showHires = function() 
   {
      // Pause the show and pop up the hires image
 
     this.pausefcn();
     this.slides[ this.current ].showHires();
 
   };
 
   //--------------------------------------------------
   slideshow.prototype.saveHires = function() 
   {
     // This method calls the savehires() method for the current slide.
   
     this.pausefcn();
     this.slides[ this.current ].saveHires();
   };
 
 
 
////////////////////////////////////////////////////////////////////////////////
//  Crossfading code heavily adapted from
//  Timothy Groves - http://www.brandspankingnew.net
////////////////////////////////////////////////////////////////////////////////

slideshow.prototype.initialize = function()
{

   // we know from the calling script what the approximate window dimensions are relative to the screen size
   // - use those as an assumption, since each browser handles document/window dimensions differently

   // determine the maximum image height that this users browser can support
   var winHeight;
   if (navigator.appName == "Microsoft Internet Explorer") {
      winHeight = document.body.clientHeight;
   } else {
      winHeight = window.innerHeight;
   }
   // 780 is for a 1440x900 display
   // 632 is about what it works out to for a 1024x768 screen
   // upper navigation is 109 pixels high, new image height is 500 px
   // lower text (date and text) is 50 px = 659 px
   // to squeeze an image in on a 1024x768, it should be 450 high = 182 less than the window size
   if ( this.sizeDown == true ) {
      this.defaultHeight = " height='" + this.targetHeight + "'";
   } else if ( winHeight < (this.targetHeight + 182) ) {
      var def = winHeight - 182;
      this.defaultHeight = " height='" + def + "'";
      // console.log("Target image size: " + this.targetHeight + " too big.  Resizing img to " + def);
   }


   // setup the HTML div elements to be displayed properly
   //
	this.currentElement = -1;
	
	for (var i=0;i<this.displayElements.length;i++)
	{
		this.displayElements[i].style.opacity = 0;
		this.displayElements[i].style.position = "absolute";
		this.displayElements[i].style.filter = "alpha(opacity=0)";
		this.displayElements[i].style.visibility = "hidden";
	}
		
   // If this URL was called with a web query specifying an image, jump to it
   // and don't start playing
   formData = createRequestObject();

     // Was "image" or "play" set in the URL?  If so, don't restore the position
     // from the cookie, but rather jump to the image
	  var image    = formData['image'];
	  var playFlag = formData['play'];
     
     if ( image != null ) {
        this.current = parseInt(image - 1);
        this.pausefcn(); // call this to set the status text
     }

    if (playFlag) { 
       this.playfcn(0); 
       this.update();
    }
    else
    {
       this.pausefcn();
       this.update();
       this.setSlideTimer(); // use this to prefetch the next images
    }

    this.displayFade();

};

slideshow.prototype.displayFade = function()
{
    var toggleVar = 0;
    var toggleText = "off";
    if ( !this.enable_fade ) 
    {
       toggleVar = 1;
       toggleText = "on";
    }
    if ( this.fadeState != undefined ) {
       this.fadeState.innerHTML = "<a href='javascript:" +
                                  this.name + ".toggleFade(" + toggleVar + ");'>" +
                                  "Turn " + toggleText + " slide cross-fading";
    }
}

slideshow.prototype.toggleFade = function(mode)
{
   this.enable_fade = mode;
   if ( this.enable_fade ) {
      slideshow.prototype.crossfade_duration = 500;
   } else {
      slideshow.prototype.crossfade_duration = 10;
   }
   console.log(mode);
   this.displayFade();
}
////////////////////////////////////////////////////////////////////////////////
// fade routines
////////////////////////////////////////////////////////////////////////////////
slideshow.prototype.displayItem = function()
{
   // take this.displayHTML, render it into the next available element, and 
   // Trigger the fade...
	
	this.prevElement = this.currentElement;
	this.currentElement++;
	if (!this.displayElements[this.currentElement])	this.currentElement = 0;
	
   //before displaying the current element, replace its HTML with the
   //displayHTML
   if ( !this.postFadeUpdate ) {
      this.displayElements[this.currentElement].innerHTML = this.displayHTML;
   }
   if ( this.preFadeUpdate && (this.prevElement > -1)) {
      this.displayElements[this.prevElement].innerHTML = "";
      // TODO: improve the preFadeUpdate logic...it seems a little sketchy
      this.preFadeUpdate = 0;
   }

   this.displayElements[this.currentElement].style.visibility = "visible";
   
   this.fadeClock = 0;
	
	var p=this;
   // Be sure to clear the fadeTimer before we display another element...
   clearInterval( this.fadeTimer );
	this.fadeTimer = setInterval(function() { p._fade(); }, this.fade_interval );
};


// Internal fade function that will be called every fade_interval msec by the
// displayItem function
slideshow.prototype._fade = function()
{
	this.fadeClock += this.fade_interval;
	
	var ieop = Math.round( this._easeInOut(this.fadeClock, 0, 1, this.crossfade_duration) * 100 );
	var op = ieop / 100;

   // CurrentElement and PrevElement shorthand
   var ce = this.displayElements[this.currentElement];
   var pe;

   // fade in the new element
	ce.style.opacity = op;
	ce.style.filter = "alpha(opacity="+ieop+")";
	
	
   // fade out the old element
	if (this.prevElement > -1)
	{
      pe = this.displayElements[this.prevElement];
      pe.style.opacity = 1 - op;
		pe.style.filter = "alpha(opacity="+(100 - ieop)+")";
	}
	
   // If we've reached the clock boundary, stop the interval
	if (this.fadeClock >= this.crossfade_duration)
	{
		clearInterval( this.fadeTimer );
		
      // hide the previous element
		if (this.prevElement > -1) 
      {
			pe.style.visibility = "hidden";	
         pe.style.opacity = 0;
	    	pe.style.filter = "alpha(opacity=0)";

         // replace the innerHTML with nothing, which is especially important if
         // a video was playing...
         pe.innerHTML = "";
      }

      if ( this.postFadeUpdate ) { 
         ce.innerHTML = this.displayHTML;
         this.postFadeUpdate = 0;
         this.preFadeUpdate = 1;
      }
	}
};

slideshow.prototype._easeInOut = function(t,b,c,d) { return c/2 * (1 - Math.cos(Math.PI*t/d)) + b; };

// END SLIDESHOW OBJECT --------------------------------------------------






function flashEmbedCode(slideObj)
{
   var width = 320; // + 10 = 330
   var height = 240; // + 30 = 270
   // autoPlay=true&flvPath=/photos/2007/flash/lowres/flash_exp-3.flv&flvTitle=&bgColor=0x000066&startFrame=1

   //   //4 console.log(slideObj);
   if ( slideObj.width  != undefined &&
        slideObj.height != undefined )
   {
      // //4 console.log("width = " + width);
      width = parseInt(slideObj.width);
      height = parseInt(slideObj.height);
   }

   width += 10;
   height += 30;

   var FlashVars = 'autoPlay=true&flvPath=' + slideObj.movieURL + 
                   '&flvTitle=' + slideObj.caption + 
                   '&bgColor=0x000066&startFrame=1';

   //var FlashVars = "autoPlay=true&flvPath=/photos/2007/flash/lowres/flash_exp-3.flv&flvTitle=&bgColor=0x000066&startFrame=1";

   var flashText = '<p id=photoPage_photoBlock>' +
          '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'  +
          width + '" height="'+height+'">'  +
          '<PARAM NAME="width"   VALUE="' + width + '"/>' +
          '<PARAM NAME="height"  VALUE="'+height+'"/>' +
          '<PARAM NAME="align"   VALUE="middle"/>' +
          '<PARAM NAME="src"     VALUE="/styles/flvplayer.swf"/>' +
          '<PARAM NAME="name"    VALUE="' + slideObj.caption + '"/>' +
          '<PARAM NAME="quality" VALUE="high"/>' +
          '<PARAM NAME="bgColor" VALUE="#000066"/>' +
          '<PARAM NAME="type" VALUE="application/x-shockwave-flash"/>' +
          '<PARAM NAME="allowScriptAccess" VALUE="sameDomain"/>' +
          '<PARAM NAME="pluginspage" VALUE="http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash"/>' +
          '<PARAM NAME="FlashVars" VALUE="' + FlashVars + '"/>' +
          '<embed width="' + width + '" height="'+height+'" align="middle" src="/styles/flvplayer.swf" ' +
          'name="' + slideObj.caption + '" ' +
          'FlashVars="'+ FlashVars + '"' +
          'bgColor="#000066"' +
          'allowScriptAccess="sameDomain"' +
          'quality="high"' +
          'type="application/x-shockwave-flash"' +
          'pluginspage="http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash"/>'  +
          '</object></p>';

   return flashText;

}

//================================================================================
//================================================================================
//================================================================================

/*
Webmonkey GET Parsing Module
Language: JavaScript 1.0
The parsing of GET queries is fundamental
to the basic functionality of HTTP/1.0.
This module parses GET with JavaScript 1.0.
Source: Webmonkey Code Library
(http://www.hotwired.com/webmonkey/javascript/code_library/)
Author: Patrick Corcoran
Author Email: patrick@taylor.org
*/

function createRequestObject() {
  FORM_DATA = new Object();
    // The Object ("Array") where our data will be stored.
  separator = ',';
    // The token used to separate data from multi-select inputs
  query = '' + this.location;
    // Get the current URL so we can parse out the data.
    // Adding a null-string '' forces an implicit type cast
    // from property to string, for NS2 compatibility.
  query = query.substring((query.indexOf('?')) + 1);
    // Keep everything after the question mark '?'.
  if (query.length < 1) { return false; }  // Perhaps we got some bad data?
  keypairs = new Object();
  numKP = 1;
    // Local vars used to store and keep track of name/value pairs
    // as we parse them back into a usable form.
  while (query.indexOf('&') > -1) {
    keypairs[numKP] = query.substring(0,query.indexOf('&'));
    query = query.substring((query.indexOf('&')) + 1);
    numKP++;
      // Split the query string at each '&', storing the left-hand side
      // of the split in a new keypairs[] holder, and chopping the query
      // so that it gets the value of the right-hand string.
  }
  keypairs[numKP] = query;
    // Store what's left in the query string as the final keypairs[] data.<
  for (i in keypairs) {
    keyName = keypairs[i].substring(0,keypairs[i].indexOf('='));
      // Left of '=' is name.
    keyValue = keypairs[i].substring((keypairs[i].indexOf('=')) + 1);
      // Right of '=' is value.
    while (keyValue.indexOf('+') > -1) {
      keyValue = keyValue.substring(0,keyValue.indexOf('+')) + ' ' + keyValue.substring(keyValue.indexOf('+') + 1);
        // Replace each '+' in data string with a space.
    }
    keyValue = unescape(keyValue);
      // Unescape non-alphanumerics
    if (FORM_DATA[keyName]) {
      FORM_DATA[keyName] = FORM_DATA[keyName] + separator + keyValue;
        // Object already exists, it is probably a multi-select input,
        // and we need to generate a separator-delimited string
        // by appending to what we already have stored.
    } else {
      FORM_DATA[keyName] = keyValue;
        // Normal case: name gets value.
    }
  }
  return FORM_DATA;
}

////////////////////////////////////////////////////////////////////////////////
// Image preloader
//
// adapted from
// http://www.webreference.com/programming/javascript/gr/column3/
function ImagePreloader(image, callback)
{
   // store the callback
   this.callback = callback;
   this.preload(image);
}

ImagePreloader.prototype.preload = function(image)
{
   // create new Image object and add to array
   var oImage = new Image;

   //4 console.log("   preload() called: " + image);
   // set up event handlers for the Image object
   oImage.onload = ImagePreloader.prototype.onload;
   oImage.onerror = ImagePreloader.prototype.onerror;
   oImage.onabort = ImagePreloader.prototype.onabort;

   // assign pointer back to this.
   oImage.oImagePreloader = this;
   oImage.bLoaded = false;

   // assign the .src property of the Image object
   oImage.src = image;
};

ImagePreloader.prototype.onComplete = function()
{
   //4 console.log("   oncomplete called");
   this.callback();
};

ImagePreloader.prototype.onload = function()
{
   //4 console.log("   onload called for " + this);
   this.bLoaded = true;
   this.oImagePreloader.nLoaded++;
   this.oImagePreloader.onComplete();
};

ImagePreloader.prototype.onerror = function()
{
   this.bError = true;
   this.oImagePreloader.onComplete();
};

ImagePreloader.prototype.onabort = function()
{
   this.bAbort = true;
   this.oImagePreloader.onComplete();
};

// Create a function to bind an object and a method together for the ImagePreloader callback
// http://bitstructures.com/2007/11/javascript-method-callbacks
function bind(toObject, methodName){
   return function(){toObject[methodName]();};
}


