/**
 * @fileoverview southprojects PreloadImages class definition. Preloads one or several
 * images at once.
 *
 * <pre>
 * Copyright (c) 2006 by SouthProjects
 * http://www.southprojects.com
 * Santiago, Chile,
 * Version 1.2 Dec 2006.
 * All rights reserved.
 * </pre>
 */

/* $Id: preloadimages.js 4726 2006-10-12 14:21:15Z shacka $ */

/**
 * southprojects PreloadImages class.
 *
 * @constructor
 * @param {object} objArgs Object properies
 */
southprojects.ImagePreloader = function(objArgs) {
  // southprojects.PreloadImages object
  this.job = null;
  // Image object
  this.image = null;
  // Initialize
  if (arguments.length > 0) this.init(objArgs);
};

/**
 * Initializes object.
 *
 * <pre>
 * Takes in following object:
 * {
 *   job: [object] southprojects.PreloadImages object,
 *   url: [string] image URL,
 *   timeout: [number, optional] number of milliseconds to wait for onload
 *    event before forcing it
 * }
 * </pre>
 *
 * @private
 * @param {object} objArgs Object properies
 */
southprojects.ImagePreloader.prototype.init = function(objArgs) {
  // Check arguments
  if (!objArgs || !objArgs.job) {
    return;
  }
  // Attach to PreloadImages
  this.job = objArgs.job;
  // Create new Image
  this.image = new Image();
  this.job.images.push(this.image);
  var objPreloader = this;
  this.image.onload = function() {
    objPreloader.job.loadedUrls.push(objArgs.url);
    // IE needs timeout to set image dimensions correctly
    setTimeout(function() {
      objPreloader.onLoad();
    }, 0);
  };
  this.image.onerror = function() {
    objPreloader.job.invalidUrls.push(objArgs.url);
    objPreloader.onLoad();
  };
  this.image.onabort = function() {
    objPreloader.job.abortedUrls.push(objArgs.url);
    objPreloader.onLoad();
  };
  this.image.src = objArgs.url;
  if (typeof objArgs.timeout == 'number') {
    setTimeout(function() {
      if (objPreloader.job) {
        // Onload didn't fire
        if (objPreloader.image.complete) {
          objPreloader.job.loadedUrls.push(objArgs.url);
        } else {
          objPreloader.job.abortedUrls.push(objArgs.url);
        }
        objPreloader.onLoad();
      }
    }, objArgs.timeout);
  }
};

/**
 * Image onload event handler.
 * @private
 */
southprojects.ImagePreloader.prototype.onLoad = function() {
  // May be called more than once
  if (!this.job) {
    return;
  }
  // Remove handlers to prevent further calls
  this.image.onload = null;
  this.image.onerror = null;
  this.image.onabort = null;
  // Detach from PreloadImages
  var objJob = this.job;
  this.job = null;
  // Reduce counter
  objJob.leftToLoad--;
  // If this was last image
  if (objJob.leftToLoad == 0 && typeof objJob.onLoad == 'function') {
    // We don't need onload handler any more after last image was loaded
    var funcOnLoad = objJob.onLoad;
    objJob.onLoad = null;
    // onLoad callback
    funcOnLoad(objJob);
  }
};

/**
 * @constructor
 * @param {object} objArgs Object properies
 */
southprojects.PreloadImages = function(objArgs) {
  // Array of Image objects
  this.images = [];
  // Counter to know when all images are loaded
  this.leftToLoad = 0;
  // Array of successfully loaded URLs
  this.loadedUrls = [];
  // Array of invalid URLs
  this.invalidUrls = [];
  // Array of aborted URLs
  this.abortedUrls = [];
  // Onload event handler
  this.onLoad = null;
  // Initialize
  if (arguments.length > 0) this.init(objArgs);
};

/**
 * Initializes object.
 *
 * <pre>
 * Takes in following object:
 * {
 *   urls: [object] array of absolute or relative image URLs to preload,
 *   onLoad: [function, optional] onload event handler,
 *   timeout: [number, optional] number of milliseconds to wait for onload
 *    event before forcing it
 * }
 *
 * onLoad callback function will be called in any case, even if errors occured
 * during loading or loading process was aborted. onLoad callback function
 * receives southprojects.Transport.PreloadImages object. Its various properies can
 * be used:
 * {
 *   images: [object] array of Image objects,
 *   loadedUrls: [object] array of successfully loaded URLs,
 *   invalidUrls: [object] array of URLs that were not loaded due to errors,
 *   abortedUrls: [object] array of URLs that were not loaded due to abort,
 *   leftToLoad: [number] how many images left to load if event was forced
 * }
 *
 * If onLoad event doesn't fire during long period of time, it can be forced
 * using "timeout" argument.
 * </pre>
 *
 * @private
 * @param {object} objArgs Object properies
 */
southprojects.PreloadImages.prototype.init = function(objArgs) {
  // Check arguments
  if (!objArgs) {
    return;
  }
  if (!objArgs.urls || !objArgs.urls.length) {
    if (typeof objArgs.onLoad == 'function') {
      // onLoad callback
      objArgs.onLoad(this);
    }
    return;
  }
  // Run job
  this.images = [];
  this.leftToLoad = objArgs.urls.length;
  this.loadedUrls = [];
  this.invalidUrls = [];
  this.abortedUrls = [];
  this.onLoad = objArgs.onLoad;
  // Go through URLs array
  for (var iUrl = 0; iUrl < objArgs.urls.length; iUrl++) {
    // Preload URL
    new southprojects.ImagePreloader({
      job: this,
      url: objArgs.urls[iUrl],
      timeout: objArgs.timeout
    });
  }
};
