/*
             Subroutines to display a series of pictures (a "slideshow")

   1 11/28/05 First major modification
   2 12/25/05 Add "Omit"
   3  1/06/06 Fix path in constructor
   4  1/09/06 Use new fade based on opacity
   5  1/15/06 Minor revision to setTimeout loop
   6  1/20/06 Make slideShowInit() instantaneous.
   6  1/25/06 Change holdTimes to seconds
   7  2/02/06 New simplification
   8  5/24/06 Add handling of split captions using "|"
   9 10/04/06 Add optional refreshFunction call
  10 10/10/06 Don't change images if only one image
  11 10/19/06 Revise setStopStart() and add startStopAction() optional call
  12 10/29/06 Add fadeEndFunction() call
  13 10/01/07 Add "Last" to slideShowRefresh()
  14 10/10/07 Correct makeCrossFade to call changeEndFunction only once
  15 11/01/07 Various corrections and additions
  16 10/13/08 Allow putting image at top of box; add refresh("R") (reload)
  17 12/07/08 Eliminate need to define parameters "isStopStartAction",
				  "isChangeImagePointer", "isChangeStartFunction" & "isChangeEndFunction"
  18 12/23/08 Redo slideShowRefresh to handle omitted pictures correctly. Add
  				  vector 'obj.virtual' and scalar 'obj.indexVirtual'.

Each slideshow is identified as an object and is defined and initiated separately.

<script src="slideShow.js" language="JavaScript"></script>
//Define the following for each separate slideshow, where "name" is an appropriate name
<script language="JavaScript">
name=new slideShow (name,path,random);
name.images=new Array(list of image names, starting with first to show);
name.captions=new Array(list of caption texts, starting with first to show);
name.holdTimes=new Array(list of delays between changes); OR =value; //seconds
name.fadeTimes=new Array(list of individual fade times); OR =value; //seconds
name.widths=array or value (optional if given in image HTML)
name.heights=array or value      "             "
</script>

name - Name of the image (as in name="name") in the <img> tag in the HTML. Although the
       new object may have a different name, it would be confusing not to have them the
       same.
path - path to the images from the top level. Must end in "/";
		 default: "SlideShows/name/"
name.images - an array of names of images to be shown.
name.fadeTimes - The time it takes for each picture to fade to the next. If an array,
                 must be the same length as name.images. If a scalar, the same value
                 will be used for all images.
name.holdTimes - The time each picture stays before the next fade. If an array,
                 must be the same length as name.images. If a scalar, the same value
                 will be used for all images.

If the slide show is to commence upon loading of the page, the <body> tag must contain
a call to slideShowInit for each slide show on the page:
   name.slideShowInit(param);
param - >=0       Starts slideshow in param milliseconds
        undefined Displays first picture immediately but does not init slide show. This
                  should be made if the user wants to display the first picture
                  immediately but has not encoded it in the HTML.
        <0        Stops slide show

Calls to name.Refresh (param):
   Value of param   Effect
   ==============   ======
    undefined       Changes picture to next in order (or random)
   'B' (Back)       Changes picture to previous in order; stops slideshow
   'H' (Home)       Changes to first picture; stops slideshow
   'F' (Forward)    Changes picture to next in order; stops slideshow
   'L' (Last)		  Changes to last picture; stops slideshow
   'R' (Refresh)	  Refreshes current picture
    >=0             Changes to picture number param; stops slideshow

*/
function slideShow (name,path,random) {
	//Used as object constructor
	this.name=name;
	if (path != undefined) this.path = path;
	else {this.path = "SlideShows/"+name+"/"} //Default
	if (random != undefined) this.random = random;
	//index used for sequential (non-random) progression of images
	this.index = -1;
	this.interval = 50; //default # of milliseconds that each image shows in a fade
	this.frontBackFlag = 1; //Means main image starts in "back" position
	this.loaded = 0;
	//Define functions as object functions
	this.slideShowInit = slideShowInit;
	this.setStopStart = setStopStart;
	this.refresh = slideShowRefresh;
	this.stopStartSlideShow = stopStartSlideShow;
	this.generateVirtual = generateVirtual;
}

//Generate this.virtual and this.indexVirtual, properly reflecting re-sort and omitted images if appropriate
function generateVirtual () {
	if (this.specialSequence) {
		//Create a virtual vector reflecting the appropriate sequence of images
		//This will come only if editing images
		this.virtual = this.specialSequence.slice(); //copy array
		if (this.omit != undefined) removeOmittedFromVirtual(this);
	}
	else {
		//Create a virtual vector that is an identity; some elements will be removed later
		this.virtual = new Array(this.images.length);
		for (var i = 0; i < this.images.length; i++) this.virtual[i] = i;
		this.indexVirtual = this.index;
		if (this.omit == undefined) return;
		//Remove omitted from this.virtual
		removeOmittedFromVirtual(this);
	}
	//Set this.indexVirtual if there is a current picture
	if (this.index >= 0) {
		//Find index in this.virtual and set this.indexVirtual
		this.indexVirtual = -1; //Might not find it if it was omitted
		for (var i = 0; i < this.virtual.length; i++) {
			if (this.index != this.virtual[i]) continue; //not found
			//found
			this.indexVirtual = i;
			break;
		}
	}
}
function removeOmittedFromVirtual (obj) {
	var newVirtual = new Array();
	for (var i = 0; i < obj.virtual.length; i++) {
		if (obj.omit[i] == 0) newVirtual.push(obj.virtual[i]);
	}
	obj.virtual = newVirtual;
}
/* object.stopStartSlideShow();
		If show is running, stop it; if not, start at current picture with zero delay
*/
function stopStartSlideShow() {
	if (this.runShow == 1) this.slideShowInit(-1); //Stop the show
	else						  this.slideShowInit(0);	//start the show with zero delay
}

/* object.slideShowInit (param)
		param =	undefined: put up first picture and do not start show
					-1: stop show
					-2: stop show and refresh current picture
					else: start show at current picture with "param" ms. delay
*/
function slideShowInit (param) {
	if (this.images==undefined || this.images.length==0) return;
	this.generateVirtual(); //Generate virtual vector and index if anything is omitted
	if (param==undefined) {
		//Put up first picture without starting show
		this.setStopStart('stop');
		var temp=this.fadeMode;
		this.fadeMode='none';
		this.refresh(0);
		this.fadeMode=temp;
		return;
	}
	else if (param == -1) {
		//Stop the show
		this.setStopStart('stop');
		clearTimeout(this.timeoutCode);
	}
	else if (param == -2) {
		//Stop the show and refresh the current index
		this.setStopStart('stop');
		clearTimeout(this.timeoutCode);
		this.refresh(this.index);
	}
	else {
		//Start the show with the given initial delay
		this.setStopStart('start');
		this.timeoutCode=setTimeout(this.name + ".refresh()",param);
	}
}

/* object.setStopStart(action)
		Change HTML that indicates "start show" or "stop show" depending on "action"
		action = 'start':		start show
					'stop':  	stop show
					undefined:	toggle start/stop
		Call external function startStopAction(action) if it exists
		Text of HTML must be in element "objectname.startText" and "objectname.stopText"
		HTML text will be placed in object "objectname.startStop" 
*/
function setStopStart (action) {
	var text = '';
	if (action == undefined) {
		if (this.runShow == 1) action = 'stop';
		else						  action = 'start';
	}
	if (action == 'start') {
		this.runShow    =  1;
		if (this.stopText   != undefined) text    = this.stopText;
//		if (this.stopButton != undefined) graphic = this.stopButton;
	} else {
		this.runShow    = 0;
		if (this.startText   != undefined) text    = this.startText;
//		if (this.startButton != undefined) graphic = this.startButton;
	}
	if (document.getElementById(this.name+".startStop") != undefined &&
	     text != '') {
		document.getElementById(this.name+".startStop").innerHTML = text;
	}
	if (typeof startStopAction != 'undefined') startStopAction(action); //If additional action to be taken
}

function slideShowRefresh (param) {
	//this.virtual is the list of images with any omitted pictures removed, and, if editing pictures, in the specified order. this.indexVirtual points to the current picture in this.virtual. These were generated by generateVirtual().
	//Return for trivial conditions
	if (this.virtual.length == 0) return; //No pictures at all
	if (this.virtual.length == 1 && this.indexVirtual >= 0) return; //No refr. same px
	if (param == undefined) param = 'N'; //Default action: next picture, don't stop show
	var index = this.indexVirtual;
	var absolute = -1; //If reset, will be absolute reference to selected image
	switch (param) {
		case "H":
			//Go to first picture and stop the show
			index = 0;
			clearTimeout(this.timeoutCode);
			this.runShow = 0;
			break;
		case "B":
			//Step back one and stop the show
			if (--index < 0) index = this.virtual.length - 1;
			clearTimeout(this.timeoutCode);
			this.runShow = 0;
			break;
		case "F":
			//Step forward one and stop the show
			if (++index >= this.virtual.length) index = 0;
			clearTimeout(this.timeoutCode);
			this.runShow = 0;
			break;
		case "N":
			//Go to next picture (may be random) and do NOT stop the show
			if (this.random) {
				//Build array pointing to all virtual entries that are not the current entry
				var virtualPointer = new Array();
				for (var i = 0; i < this.virtual.length; i++) {
					if (this.index != this.virtual[i]) virtualPointer.push(i);
				}
				index = virtualPointer[Math.floor(Math.random() * (virtualPointer.length - .01))];
			}
			else if (++index >= this.virtual.length) index = 0;
			break;
		case "L":
			//Go to last picture and stop the show
			index = this.virtual.length - 1;
			clearTimeout(this.timeoutCode);
			this.runShow = 0;
			break;
		default:
			//Assume param is picture number; go to it (even if omitted) and stop the show
			//First make sure parameter is an integer within proper range
			absolute = Math.min(this.images.length, Math.max(0, param));
			//Find the virtual pointer to this absolute value. If not found, default to -1
			this.indexVirtual = -1;
			for (var i = 0; i < this.virtual.length; i++) {
				if (absolute != this.virtual[i]) continue
				//Match
				this.indexVirtual = i;
				break;
			}
			clearTimeout(this.timeoutCode);
			this.runShow = 0;
			break;
	}

	if (absolute >= 0) indexNew = this.index = absolute;
	else indexNew = this.index = this.virtual[this.indexVirtual = index];
	//Pointers are now set:
	//  indexNew = absolute index of new image and caption, etc.
	//  this.oldIndex was set at the last call, or is undefined (initial condition)

	//-----------------------CHANGE IMAGE AND CAPTION----------------------
	//Define object(s) for image(s)
	var image=document.images[this.name];
	var imageBack;
	//Determine if crossFade legal
	if(document.images[this.name+'.back']==undefined && this.fadeMode=='crossFade') {
		this.fadeMode='fadeOutIn'; //crossFade mode not allowed if back image undefined
	}

	//Get fade time from global variable
	var fadeTime;
	if (this.fadeTimes.length == undefined) {fadeTime=this.fadeTimes}
	else {fadeTime=this.fadeTimes[indexNew]}
	//Calculate interval/fadetime parameter
	var numberOfIntervals;
	if (this.fadeMode=='crossFade' || this.fadeMode=='fadeOutIn') {
		numberOfIntervals=Math.round(1000*fadeTime/this.interval);
	}
	//Get width and height from global variables
	var width;
	var height;
	if (this.fileWidths!=undefined) {
		if (this.fileWidths.length==undefined) {width=this.fileWidths}
		else {width=this.fileWidths[indexNew]}
	}
	if (this.fileHeights!=undefined) {
		if (this.fileHeights.length==undefined) {height=this.fileHeights}
		else {height=this.fileHeights[indexNew]}
	}
	//Get the width and height of the context, if any
	var widthContext=0, heightContext=0;
	if (document.getElementById(this.name + ".context") != undefined) {
		widthContext=document.getElementById(this.name + ".context").style.width;
		widthContext=widthContext.replace(/\D/g,'');
		heightContext=document.getElementById(this.name + ".context").style.height;
		heightContext=heightContext.replace(/\D/g,'');
	}
	else {
		if (this.groupWidth != undefined) widthContext=this.groupWidth;
		else widthContext=width;
		if (this.groupHeight != undefined) widthContext=this.groupHeight;
		else heightContext=height;
	}
	if (this.putImageAtTop != undefined && this.putImageAtTop) heightContext=height;

	//Get title and caption
	var title = document.getElementById(this.name+".title");   //HTML object
	var titleText = '';
	if (title != undefined) {
		if (this.imageTitles != undefined) titleText = this.imageTitles[indexNew];
		if (titleText == '' && this.mode == 'edit') titleText='<i>(no title)</i>';
		if (titleText == '') titleText = '&nbsp;';
	}
	var caption = document.getElementById(this.name+".caption");   //HTML object
	var captionText = '';
	if (caption != undefined) {
		if (this.captions != undefined) captionText = this.captions[indexNew];
		if (captionText == '' && this.mode == 'edit') captionText='<i>(no caption)</i>';
		if (captionText == '') captionText = '&nbsp;';
	}
	if (captionText.search(/\|/) >= 0 && title != undefined) {
		//Special case: caption is split between title and caption with "|"
		var temp = captionText.split(/\s*\|\s*/);
		titleText = temp[0];
		if (titleText == '' && this.mode == 'edit') titleText='<i>(no title)</i>';
		if (titleText == '') titleText = '&nbsp;';
		if (temp[1] != undefined) captionText=temp[1];
		if (captionText == '' && this.mode == 'edit') captionText='<i>(no caption)</i>';
		if (captionText == '') captionText = '&nbsp;';
	}

	//NOW READY TO PERFORM THE TRANSITION
	//Handle crossFade mode
	if (this.fadeMode=='crossFade') {
		if (this.frontBackFlag==0) {
			imageBack=document.images[this.name+'.back'];
			this.frontBackFlag=1;
		}
		else {
			imageBack=image;
			image=document.images[this.name+'.back'];
			this.frontBackFlag=0;
		}
		//Make sure image is opaque before displaying it
		changeOpac(0,image.name);
		//Set new image & caption
		image.loaded=0;
		image.src=this.path+this.images[indexNew];
		image.width=width;
		image.height=height;
		image.style.left=(widthContext-width)/2;
		image.style.top=(heightContext-height)/2;
		//do crossFade
		countTimes=0;
		makeCrossFade(image.name, imageBack.name, this.name, numberOfIntervals,
		 titleText, captionText, this.interval, indexNew);

	}
	//Handle Fade Out - Fade In mode
	else if (this.fadeMode=='fadeOutIn') {
		//fade out image
		if (typeof this.changeStartFunction != 'undefined') {
			this.changeStartFunction(indexNew);
		}
		var opacityFactor=100 / numberOfIntervals;
		for (i=0; i<=numberOfIntervals; i++) {
			var opacity=100 - i * opacityFactor;
			setTimeout( "changeOpac("  +    opacity    + ",'" + image.name     + "')",
			 this.interval*i );
		}
		//Throw switching images into the "setTimeout" queue
		var string = "document.images." + this.name + ".src='"+this.path+
		 this.images[indexNew]+"'; document.images." + this.name + ".width=" +
		 width + "; document.images." + this.name + ".height=" + height + ";" + 
		 "document.images." + this.name + ".style.left=" + (widthContext-width)/2 +
		 ";" + "document.images." + this.name + ".style.top=" + (heightContext-height)/2+
		 ";";
		//Add captions, if there are any (captionText may contain single quotes, but
		//double quotes have been escaped)
		if (titleText != '') {
			string += "document.getElementById('" + this.name +
						 ".title').innerHTML=\"" + titleText + "\";";
		}
		if (captionText != '') {
			string += "document.getElementById('" + this.name +
						 ".caption').innerHTML=\"" + captionText + "\";";
		}
		setTimeout(string,this.interval*(numberOfIntervals+1) );
		//fade in image
		for (i=0; i<=numberOfIntervals; i++) {
			var opacity=i * opacityFactor;
			setTimeout( "changeOpac("  +    opacity    + ",'" + image.name     + "')",
			 this.interval*(i + numberOfIntervals + 5) );
		}
		if (typeof changeEndFunction != 'undefined') {
			setTimeout( this.name + ".changeEndFunction('" + indexNew + "')",
			 this.interval * (2 * numberOfIntervals + 6) );
		}
	}
	//Handle Abrupt Change mode
	else {
		//Just switch images
		if (typeof this.ChangeStartFunction != 'undefined') {
			this.changeStartFunction(indexNew);
		}
		changeOpac(0,image.name); //Hide the picture for a moment
		image.src=this.path + this.images[indexNew];
		image.width=width;
		image.height=height;
		image.style.left=(widthContext-width)/2;
		image.style.top=(heightContext-height)/2;
		changeOpac(100,image.name);
		if (titleText != '') {
			title.innerHTML=titleText;
		}
		if (captionText != '') {
			caption.innerHTML=captionText;
		}
		if (typeof changeEndFunction != 'undefined') this.changeEndFunction(indexNew);
	}
	//---------------END of CHANGE IMAGE AND CAPTION----------------------

	//Set sequence number
	var index=this.index+1;
	if (document.getElementById(this.name+".seqNo")!=undefined) {
		document.getElementById(this.name+".seqNo").innerHTML=index.toString();
	}

	//If there is a function to move the show pointer, execute it
	if (typeof changeImagePointer == 'function') {
		changeImagePointer(this,this.oldIndex,indexNew);
	}

	if (this.runShow) {
		var holdTime;
		if (this.holdTimes.length==undefined) {holdTime=this.holdTimes}
		else {holdTime=this.holdTimes[indexNew]}
		this.timeoutCode=setTimeout(this.name + ".refresh()",1000 * holdTime);
	}
	this.oldIndex=indexNew; //Save for next time
}
//-----END OF slideShowRefresh() FUNCTION

function changeOpac(opacity, image, name) {
	 var object;
	 if (opacity > 99) opacity=99;
	 if (name != undefined) object = document.getElementById(name).style;
    else object=document.images[image].style;
    object.opacity = (opacity / 100);
    object.MozOpacity = (opacity / 100);
    object.KhtmlOpacity = (opacity / 100);
    object.filter = "alpha(opacity=" + opacity + ")";
}

function makeCrossFade (imageName, imageBackName, thisName, numberOfIntervals, titleText, captionText, interval, indexNew) {
 	if (document.images[imageName].loaded == 0) {
 		//If not loaded, wait another 100 ms
 		setTimeout('makeCrossFade ("' + imageName + '", "' + imageBackName + '", "' +
 		 thisName + '", ' + numberOfIntervals + ', "' + titleText + '", "' +
 		 captionText + '",' + interval + ',' + indexNew + ')', 100);
		return;
 	}
 	if (typeof changeStartFunction != 'undefined') {
 		eval (thisName + '.changeStartFunction(' + indexNew + ');');
 	}
	var halfWay = Math.round(numberOfIntervals/2);
	var opacityFactor = 100 / numberOfIntervals;
	var titleString = '', captionString = '';
	for (i=1; i<=numberOfIntervals; i++) {
		//Prepare a string that will change the image opacity
		var opacity=i * opacityFactor;
		var opString="changeOpac("  +    opacity    + ",'" + imageName     + "');" +
		             "changeOpac("  + (100-opacity) + ",'" + imageBackName + "');";
		//Change the title and caption if halfway through the fade
		if (i == halfWay) {
			if (titleText != '') {
				//Prepare a string that will change the title
				titleString = "document.getElementById('" + thisName +
				 ".title').innerHTML=\"" + titleText + "\";";
			}
			if (captionText != '') {
				//Prepare a string that will change the caption
				captionString = "document.getElementById('" + thisName +
				 ".caption').innerHTML=\"" + captionText + "\";";
			}
			setTimeout(opString + titleString + captionString, i*interval);
		}
		else {
			setTimeout(opString, i*interval);
		}
	}
	if (typeof changeEndFunction != 'undefined') {
		setTimeout( thisName + '.changeEndFunction("' + indexNew + '")' ,
		 (numberOfIntervals + 1) * interval);
	}
}
