/* slidefade.js :: given a container (preferably a <div>) surrounding a single
 * image element and a list of additional image source URLs, this makes a 
 * rotating 'slide show' that fades new images in.
 *
 * Initially based on 'Cross browser BlendTrans Filter javascript' source, 
 * v1.3 [1], and HTML Slide Show code from Brian Cryer [2].
 *
 * Rewritten to take advantage of rlist.Cycler to handle rotating endlessly
 * through the list of images, and to take advantage of MochiKit.Style's
 * cross-browser style and opacity handling.
 *
 * [1] http://www.brainerror.net/scripts_js_blendtrans.php
 * [2] http://www.cryer.co.uk/resources/javascript/script12slideshow.htm
 *
 * Example usage:: 
 * 
 *    slidefade.makeSlideShow('homephoto', 'homephoto-image', [
 *        'http://example.com/image/2.jpg',
 *        'http://example.com/image/3.jpg'
 *    ], 10);
 *
 * if the result is only a single image, no slide-fader will be created. If
 * there are multiple images, however, then a new SlideFade object is created,
 * started, and returned.
 */
var slidefade = {
  // Generates a basic DOM image object and gives it a source, useful for
  // preloading. Use with MochiKit.Base.map, for example::
  // 
  //     var preloadcache = MochiKit.Base.Map(slidefade.makeImage, imagelinks);
  makeImage: function(source) {
    var img = new Image();
    img.src = source;
    return img;
  },
  
  // Set opacity with a 0 - 100 value. This is just a wrapper around
  // MochiKit.Style.setOpacity(elem, o/100), but is useful for the kind of
  // fading loops used by this module.
  setOpacity: function(elem, o) {
    o = o / 100;
    MochiKit.Style.setOpacity(elem, o);
  },
  
  // Sets the background image on an element.
  setBackground: function(elem, url) {
    var style = MochiKit.Style;
    style.setStyle(elem, {'background-image': 'url('+url+')'});
  },
  
  // Given an image element, `elem`, and a list of URL (or a single URL, or
  // null/undefined), this returns a list of imagelinks. The element's source
  // is included. If it was already in the list of links, it's moved to the 
  // end. (This helps the cycler by ensuring that the first slide is shown 
  // at the proper interval, instead of waiting twice as long if the element's
  // source were at the beginning of the list). 
  //
  // This is mostly an internal helper.
  getImageLinks: function(elem, links) {
    var m = MochiKit.Base;
    var elemsrc = elem.src;
    var imagelinks = [];
    
    if(m.isArrayLike(links)) {
      m.extend(imagelinks, links);
    } else if (!m.isUndefinedOrNull(links)) {
      imagelinks.push(links);
    }
    
    // If the image element is in the list of links, move it to the end to
    // ensure there's no extra delay at the beginning. If it's not in the 
    // list, add it to the end.
    var elemInList = m.findValue(imagelinks, elemsrc);
    if(elemInList >= 0) {
      imagelinks.splice(elemInList, 1);   // Remove the element from the array
    }
    imagelinks.push(elemsrc);
    
    return imagelinks;
  },
  
  // The main function used for making a slide show. if `imageLinks` is null or
  // empty, no slideshow will be started and nothing will be returned.
  // `displaySecs` defaults to 5.
  makeSlideShow: function(container, imageElement, imageLinks, displaySecs) {
    var m = MochiKit.Base, md = MochiKit.DOM;

    imageElement = md.getElement(imageElement);
    container = md.getElement(container);
    displaySecs = displaySecs || 5;
  
    imageLinks = slidefade.getImageLinks(imageElement, imageLinks);
    if(imageLinks.length <= 1) {
      return;
    }
  
    return new slidefade.SlideFade(container, imageElement, imageLinks, displaySecs);
  }
};


// Constructor for SlideFade objects. This should not be called directly - use
// ``slidefade.makeSlideShow(...)`` instead.
slidefade.SlideFade = function(container, imageElement, imageLinks, displaySecs) {
  var m = MochiKit.Base, md = MochiKit.DOM;
  
  this.imageElement = md.getElement(imageElement);
  this.container = md.getElement(container);
  this.seconds = displaySecs;
  this.imageLinks = imageLinks;
  this.lastSource = this.imageElement.src;
  
  this.preloadCache = m.map(slidefade.makeImage, imageLinks);

  m.bindMethods(this);

  this.cycler = new rlist.PeriodicCycler(this.preloadCache, this.seconds, this.blend);
  this.cycler.start();
  
  MochiKit.Signal.connect(window, 'onunload', this, this.onWindowUnload);
};

MochiKit.Base.update(slidefade.SlideFade.prototype, {
  __repr__: function () {
    return "[slidefade.SlideFade]";
  },

  toString: function () { return this.__repr__(); },

  blend: function(nextimage) {
    var m = MochiKit.Base;
    var image = this.imageElement, container = this.container;
    
    // set the current image as background
    slidefade.setBackground(container, image.src);
    
    // Make image element (current image) transparent;
    slidefade.setOpacity(image, 0);
    
    // I've found it best to delay the start of the blend function just a 
    // little bit so that above background and opacity settings have a
    // chance to run their course.
    setTimeout(m.method(this, function(){
      this._innerBlend(nextimage.src);
    }), 50);
  },
  
  _innerBlend: function(src) {
    var speed = 10, timer = 0;
    var image = this.imageElement;
    
    // Copy the next image's source to the main image element
    image.src = src;
    
    // Fade in image
    for(var i=0; i<=100; i++) {
      setTimeout("slidefade.setOpacity('"+image.id+"', "+i+")",(timer * speed));
      timer++;
    }
  },
  
  onWindowUnload: function(evt) {
    this.cycler.stop();
    this.preloadCache = null;
  }
});
