// Globals
var _g_image_cache = new ImageCache();
var _g_image_group = null;


// Top-level methods to provide cache access without
// the need to refer explicitly to global variables
function LoadGroup(group)
{
   _g_image_cache.LoadGroup(group);
}

function GetImage(name)
{
   return _g_image_cache.GetImage(name);
}
		
function loadImage_callback()
{
   // The loading state of some image has just updated
   var obj = event.srcElement;
   var group = _g_image_group;

   // If the image has just loaded, update the count
   if (obj.readyState == "complete")
      group.numLoaded ++;
	
   // Call imageupdate handler on group, if specified;
   // otherwise call imageupdate handler on document
   if (group.imageupdate)
      eval("group.imageupdate(group)");
   else if (document.imageupdate)
      eval("document.imageupdate(group)");

   // If there are still images to load, schedule another callback
   // (condition isn't strictly necessary, since "complete" is the
   // final stage of loading, therefore when an image is "complete"
   // it won't fire further readystatechange events)
   if (group.numLoaded != group.numToLoad)
      obj.onreadystatechange = loadImage_callback;
}

function ImageGroup()
{
   this.AddImage = _addImage;
   this.IsComplete = _isComplete;
   this.imageupdate = null;

   this.items = new Array();
   this.numLoaded = 0;
   this.numToLoad = 0;

   return this;
}

function _addImage(path)
{
   this.items[ this.items.length ] = path;
}

function _isComplete()
{
   return this.numLoaded == this.numToLoad;
}

function ImagePair( _file )
{
   // The ImagePair object's purpose is to
   // keep track of the image and the file that identifies it;
   // we do this because the image's src attribute will read back
   // as uuencoded if interrogated, meaning that, to see if a
   // particular image exists in the cache, we need to uuencode
   // again. It seems easier just to store the path in unencoded
   // form, along with the image associated with it.
   this.file = _file;
   this.image = new Image();
	
   // Add the image to the document; if you don't, readystatechange
   // events won't know where they originated, meaning that
   // event.srcElement will be null
   document.appendChild(this.image);
}

function ImageCache()
{
   this.LoadGroup = _loadGroup;
   this.GetImage = _getImage;

   this.items = new Array();
   return this;
}
	
function _loadGroup( group )
{
   // If there aren't any images to load, do nothing
   if (group.items.length == 0)
      return;
	
   // Set the active group
   _g_image_group = group;
	
   // Reset the count of loaded images and images to load
   group.numLoaded = 0;
   group.numToLoad = group.items.length;

   // num_loaded, like group.numLoaded, is the count of images
   // in the group that have completed loading. However, we
   // only update group.numLoaded after having kicked off all
   // the new images we want to load. Otherwise race conditions
   // can lead to imageupdate getting called more than once after
   // all the images are loaded.
   var num_loaded = 0;
	
   // Add any of the images to the cache if they don't yet exist
   for (i = 0; i < group.numToLoad; i ++)
   {
      var count = this.items.length;
	
      for (j = 0; j < count; j++)
      {
         if (this.items[j].file.indexOf(group.items[i]) != -1)
         {
            break;
         }
      }
		
      // Did we find it?
      if (j == count)
      {
         // No, so add it and start loading. Assignment order is
         // critical in the next few lines. Ensure the image is
         // added to the cache before setting its .src attribute;
         // otherwise for a short time an image group may affirm
         // itself "loaded" before all its elements exist in
         // the cache
         var pair = new ImagePair(group.items[i]);
         this.items[this.items.length] = pair;
         (pair.image).onreadystatechange = loadImage_callback;
         pair.image.src = group.items[i];
      }
      else
      {
         // Yes, but check whether it's finished loading
         if (this.items[j].image.readyState == "complete")
         {
            num_loaded ++;
         }
      }
   }
	
   // Check whether the group has already completely loaded
   if (num_loaded == group.numToLoad)
   {
      // Yes it has; it's safe to set group.numLoaded now,
      // because we know, since all the images are fuly loaded,
      // there won't be any more readystatechange events
      group.numLoaded = num_loaded;

      // Invoke the group's update handler, if specified;
      // otherwise invoke the document's update handler
      if (group.imageupdate)
         eval("group.imageupdate(group)");
      else if (document.imageupdate)
         eval("document.imageupdate(group)");
   }
	
   // We can now throw away num_loaded; its only purpose was to
   // see if everything was already loaded, since, if it was, no
   // readystatechange events would be generated, and therefore
   // no imageupdate events, and therefore no notification that
   // loading was already complete. We do NOT want to update
   // group.numLoaded, since num_loaded may already be out of date
}

function _getImage( name )
{
   // Method to retrieve an existing image from the cache;
   // returns null if the image could not be found
   for (i = 0; i < this.items.length; i ++)
   {
      if (this.items[i].file.indexOf(name) != -1)
         return this.items[i].image;
   }

   return null;
}


 
