// $Id: slideshow.js 6992 2007-04-23 11:41:18Z andrew $
/**
 *
 * Copyright (c) 2004-2006 by Zapatec, Inc.
 * http://www.zapatec.com
 * 1700 MLK Way, Berkeley, California,
 * 94709, U.S.A.
 * All rights reserved.
 */

/**
 * Zapatec.SlideShow is a tool for creating slideshows.
 *
 * @param params [HashMap] -- array with properties.
 *		cycling [boolean] - if true, then after showing last slide slideshow
 * 			would start from the beginning.
 *		speed [int] - how much time each slide would be visible on screen(can
 *			be overrided for each slide).
 *		animSpeed [int] - animation speed(can be overrided for each slide).
 * 		effect [string] - visual effect for displaying slides(can be overrided
 *			for each slide).
 *		slides [Array] - ids or references to HTML elements.
 *		callback [function] - callback function, called during show and hide
 *        callback(bShow, intSlide)
 *        bShow - pass in boolean true if show, false if hide
 *        intSlide - currentSlide
 *		callback_endcycle [function] - callback function, called at end of cycle or start
 *        callback_endcycle(bEnd)
 *        bEnd - true if at end, false if at start
 *		display_endcycle [string] - ID of element to display at end of cycle
 * Event listeners:
 * beforeSlideHide: is fired before slide gets hidden. Receives slide ID as param
 * afterSlideHide:  is fired after slide gets hidden. Receives slide ID as param.
 * beforeSlideShow: is fired before slide gets visible. Receives slide ID as param
 * afterSlideShow: is fired after slide gets visible. Receives slide ID as param.
 * endCycle: is fired when last slide gets hidden.
 * finishCycle: is fired when slideshow is finished - last slide gets hidden and cycling turned off.
 * restartCycle: is fired when last slide fets hidden and slideshow jumps to first slide
 * stop - is fired when slideshow stop() method is called
 * start - is fired when slideshow start() method is called
 */
Zapatec.SlideShow = function(objArgs){
	Zapatec.SlideShow.SUPERconstructor.call(this, objArgs);
};

/**
 * Unique static id of the widget class. Gives ability for Zapatec#inherit to
 * determine and store path to this file correctly when it is included using
 * Zapatec#include. When this file is included using Zapatec#include or path
 * to this file is gotten using Zapatec#getPath, this value must be specified
 * as script id.
 * @private
 */
Zapatec.SlideShow.id = "Zapatec.SlideShow";

// Inherit SuperClass
Zapatec.inherit(Zapatec.SlideShow, Zapatec.Widget);

Zapatec.Transport.include(Zapatec.zapatecPath + "../zpeffects/src/effects.js", "Zapatec.Effects");

/**
 * @private
 * Initializes widget.
 * @param {object} objArgs User configuration
 */
Zapatec.SlideShow.prototype.init = function(objArgs){
	// internal parameters
	this.slides = [];
	this.effects = [];
	this.animSpeeds = [];
	this.speeds = [];
	this.currentSlideNumber = -1;
	this.stopped = false;

	Zapatec.SlideShow.SUPERclass.init.call(this, objArgs);

	if(this.config.slides != null) {
		for(var ii = 0; ii < this.config.slides.length; ii++){
			this.addSlide(this.config.slides[ii]);
		}
	}
};

/**
 * Configures widget. Gets called from parent init method.
 *
 * @private
 * @param {object} objArgs User configuration
 */
Zapatec.SlideShow.prototype.configure = function(objArgs) {
	this.defineConfigOption('theme', ""); // What effect to use
	this.defineConfigOption('cycling', false); // 
	this.defineConfigOption('speed', 1000); // 
	this.defineConfigOption('animSpeed', 5); // 
	this.defineConfigOption('effect', "fade"); // 
	this.defineConfigOption('slides', []); // 
	this.defineConfigOption('callback'); // 
	this.defineConfigOption('callback_endcycle'); // 
	this.defineConfigOption('display_endcycle'); // 

	// super call
	Zapatec.SlideShow.SUPERclass.configure.call(this, objArgs);
};

/**
 * Reconfigures the widget with new config options after it was initialized.
 * May be used to change look or behavior of the widget after it has loaded
 * the data. In the argument pass only values for changed config options.
 * There is no need to pass config options that were not changed.
 *
 * @param objArgs {object} Changes to user configuration
 */
Zapatec.SlideShow.prototype.reconfigure = function(objArgs){
	// Call parent method
	Zapatec.SlideShow.SUPERclass.reconfigure.call(this, objArgs);
};

/**
 * @private
 * Hides slide that is currently visible.
 */
Zapatec.SlideShow.prototype.hideCurrentSlide = function(){
	if(this.stopped){
		return;
	}
	
	this.fireEvent("beforeSlideHide", this.currentSlideNumber);

	if(this.currentSlideNumber >= 0){
		if(
			this.config.callback && 
			typeof(this.config.callback) == 'function'
		){
			this.config.callback(false, this.currentSlideNumber);
		}

		var self = this;
		
		Zapatec.Effects.hide(
			this.slides[this.currentSlideNumber], 
			this.animSpeeds[this.currentSlideNumber], 
			this.effects[this.currentSlideNumber], 
			function(){
				self.fireEvent("afterSlideHide", self.currentSlideNumber);
				self.showNext();
			}
		);
	} else {
		this.showNext();
	}
};

Zapatec.SlideShow.prototype.showPrev = function(){
	// hide the current slide
	if(
		this.currentSlideNumber >= 0 && 
		this.currentSlideNumber < slideshow.slides.length
	){
		this.slides[this.currentSlideNumber].style.display = 'none';
	}

	// Adjust current slide index and call showNext
	// For Previous, Basically move back 2 slides and call showNext
	if (this.currentSlideNumber <= 0){
		// slide before last
		this.currentSlideNumber = this.slides.length - 2
	} else {
		// 2 slides back
		this.currentSlideNumber -= 2;
	}

	if (this.currentSlideNumber <= -1 || this.currentSlideNumber >= this.slides.length){
		// make sure withing range, -1 is the state before 1st slide
		this.currentSlideNumber -= 1
	}
	
	this.showNext();
}

/**
 * @private
 * Shows next slide
 */
Zapatec.SlideShow.prototype.showNext = function(){
	// Make sure end cycle display is turned off during show
	if (this.display_endcycle && typeof this.display_endcycle== 'string'){
		document.getElementById(this.display_endcycle).style.display='none';
	}
	
	// Make sure within range, callback could call this when state is out of sync
	if(
		this.currentSlideNumber >= this.slides.length || 
		this.currentSlideNumber < 0
	){
		this.currentSlideNumber = -1;
	}

	if(this.currentSlideNumber >= 0) {
		this.slides[this.currentSlideNumber].style.display = 'none';
	}

	this.currentSlideNumber++;
	
	// Slideshow finished
	if(this.currentSlideNumber == this.slides.length){
		this.fireEvent("endCycle");

		if(!this.config.cycling){
			if (this.config.callback_endcycle && typeof this.config.callback_endcycle == 'function'){
				this.config.callback_endcycle(true);
			}

			if(this.config.display_endcycle && typeof(this.config.display_endcycle) == 'string'){
				document.getElementById(this.config.display_endcycle).style.display = 'block';
			}
			
			this.stop();
			
			this.fireEvent("finishCycle");
			
			return;
		} else {
			if (this.config.callback_endcycle && typeof this.config.callback_endcycle == 'function'){
				this.config.callback_endcycle(false);
			}

			this.currentSlideNumber = 0;
			
			this.fireEvent("restartCycle");
		}
	}

	this.fireEvent("beforeSlideShow", this.currentSlideNumber);

	this.slides[this.currentSlideNumber].style.display = this.slides[this.currentSlideNumber].origdisplay;

	var self = this;
	
	if(
		this.config.callback && 
		typeof(this.config.callback) == 'function'
	){
		this.config.callback(true, this.currentSlideNumber);
	}

	Zapatec.Effects.show(
		this.slides[this.currentSlideNumber], 
		this.animSpeeds[this.currentSlideNumber], 
		this.effects[this.currentSlideNumber], 
		function(){
			setTimeout(function(){
					self.fireEvent("afterSlideShow", self.currentSlideNumber);
					self.hideCurrentSlide();
				}, self.speeds[self.currentSlideNumber]
			);
		}
	);
};

/**
 * Stops slideshow.
 */
Zapatec.SlideShow.prototype.stop = function(){
	this.stopped = true;
	
	this.fireEvent("stop");
};

/**
 * Starts slideshow
 */
Zapatec.SlideShow.prototype.start = function(){
	this.stopped = false;
	
	this.fireEvent("start");

	this.hideCurrentSlide();
};

/**
 * method for adding new slide to slideshow.
 * @param param {Object} Configuration object for slide of following structure:
 *	slides [HTMLElement or string] - id or reference to HTML element.
 *	speed [int] - how much time this slide would be visible on screen.
 *	animSpeed [int] - animation speed.
 * 	effect [string] - visual effect for displaying slides.
 */
Zapatec.SlideShow.prototype.addSlide = function(params){
	var slideRef = null;

	if(typeof(params) == "string"){
		slideRef = document.getElementById(params);
	} else {
		slideRef = document.getElementById(params["elementRef"]);
	}

	if(slideRef == null){
		return;
	}

	slideRef.origdisplay = slideRef.style.display;
	slideRef.style.display = 'none';

	this.slides[this.slides.length] = slideRef;
	this.effects[this.effects.length] = (params['effect'] != null ? params['effect'] : this.config.effect);
	this.animSpeeds[this.animSpeeds.length] = (params['animSpeed'] ? params['animSpeed'] : this.config.animSpeed);
	this.speeds[this.speeds.length] = (params['speed'] ? params['speed'] : this.config.speed);
};

/**
 * returns array of slides in slideshow
 */

Zapatec.SlideShow.prototype.getSlides = function(){
	return this.slides;
};

Zapatec.Utils.addEvent(window, 'load', Zapatec.Utils.checkActivation);

