/*
 * Class Slideshow
 * Accepts two image tags, positioned one on top of the other,
 * and creates a rotating slideshow using an array of images.
 *
 * Requires Prototype.
 *
 * @param options - object hash of:
 *      interval:       Float  - the length of time, in seconds, each slide is to remain on screen
 *      duration:       Float  - the length of the transition between slides, in seconds
 *      slide1:         String - the ID of the first image tag, on the "top" (highest z-index)
 *      slide2:         String - the ID of the second image tag, on the "bottom" (lowest z-index)
 *      images:         Array  - the array containing the images
 *
 * Useage:
 *      Place the image tags in the markup. I recommend placing a default image in the first img,
 *      and a display:none on the second img. This will prevent the empty img tags from appearing on the page.
 *      The script will call a show() on the second img to remove the display:none      
 *
 *          <img id="slide1" src="/path/to/optional/default/image.jpg" alt="optional alt" title="optional title" style="z-index:900" />
 *          <img id="slide2" src="" alt="" title="" style="position:absolute;z-index:900" />
 *
 *      (Style attributes do not need to be inline)
 *
 *      Define a javascript array of the images, with the src, alt, and title properties,
 *      either inline on the page, or in an external script
 *
 *          var mySlides = new Array();
 *              mySlides[i] = new Array();
 *              mySlides[i]['src'] = '/path/to/image';
 *              mySlides[i]['alt'] = 'Alt Text';
 *              mySlides[i]['title'] = 'Title Text';
 *      
 *      Initialize the slideshow object
 *
 *          mySlideshow = new slideshow({interval: 10, duration: 1.5, target1: 'slide1', target2: 'slide2', images: 'mySlides'})
 */

if (!Prototype.K)
{
    throw('Slideshow.class.js requires the Prototype library.');
}

if (Effect == "undefined")
{
    throw('Slideshow.class.js requires the Scriptaculous Effect library.');
}
    
var Slideshow = Class.create(
{
    initialize: function(options)
    {
		this.options		= Object.extend
		(
			{
				interval: 		10,
				duration: 		1.5,
				visible_slide: 	null,
				hidden_slide:	null,
				images:			null
			},
			options || {}
		);
        
        this.images				= new Array();
        this.num_images_loaded 	= 0
        this.currentImage   	= 1;
        this.num_images			= this.options.images.length;
        this.visible_slide 		= $(this.options.visible_slide);
        this.hidden_slide 		= $(this.options.hidden_slide);
        
        if (!this.visible_slide)
        {
            throw('Slideshow.class.js :: Missing visible slide element.');
        }
        
        if (!this.hidden_slide)
        {
            throw('Slideshow.class.js :: Missing hidden slide element.');
        }
        
        if (this.options.duration > this.options.interval)
        {
        	throw('Slideshow.class.js :: Transition duration is greater than interval!');
        }
        
        this.setup();
    },
    
    dropImageFromSlideshow: function()
    {
        // If for some reason the image will not load (invalid URL, etc), drop it from the slideshow
        var i = $A(arguments)[1];
        this.images.splice(i, 1);
        this.num_images--;
    },
    
    setup: function()
    {
    	for(i = 0; i < this.num_images; i++)
        {    
            this.images[i]  = new Image();
            var im			= this.images[i];
            im.src			= this.options.images[i].src;
            im.title  		= this.options.images[i].title;
            im.alt    		= this.options.images[i].alt;
            im.loaded 		= false;
            im.id     		= 'slideshowImage_'+i;
            
            // After each image has loaded, call checkImageLoadedState()
            Event.observe(im, 'load', this.checkImageLoadedState.bindAsEventListener(this, im));
            
            // If the image returned an error, call dropImageFromSlideshow()
            Event.observe(im, 'error', this.dropImageFromSlideshow.bindAsEventListener(this, im));
        };

    },
    
    checkImageLoadedState: function()
    {
        var im		= arguments[1];
        im.loaded 	= true;
        this.num_images_loaded++;
        
		if(this.num_images_loaded == this.num_images-1)
        {
           this.start();
        }
    },
    
    changeSlides: function()
    {
    	new Effect.Parallel
    	(
			[
				new Effect.Appear(this.hidden_slide, { sync: true }),
				new Effect.Fade(this.visible_slide, { sync: true })
			],
			{
				duration: this.options.duration,
				afterFinish: this.updateVisible.bind(this)
			}
    	);
    },
    
    updateVisible: function()
    {
        // Swap the image depths, and change the variable assignment so this.visible_slide always
        // refers to the image on the top.
    	var oldSlide = this.visible_slide;
    	var oldZ     = this.visible_slide.getStyle('z-index');
        this.hidden_slide.setStyle({zIndex: oldZ});
        this.visible_slide.setStyle({zIndex: oldZ - 1});
        this.visible_slide = this.hidden_slide;
        this.hidden_slide  = oldSlide;
        
        if(++this.currentImage >= this.num_images)
        {
            this.currentImage = 0;
        }
        
        this.hidden_slide.writeAttribute
        (
    		{	
    			src:    this.images[this.currentImage].src,
		        alt:    this.images[this.currentImage].alt,
		        title:	this.images[this.currentImage].title
    		}
		);
    },
    
    start: function()
    {
    	// Load the image data into the second img, and call this.changeSlides() every [this.interval] seconds.
        this.hidden_slide.writeAttribute
        (
    		{
    			src:	this.images[1].src,
				alt:   	this.images[1].alt,
    			title: 	this.images[1].title
    		}
		);

        new PeriodicalExecuter(this.changeSlides.bind(this), this.options.interval);
    }
});
