/*
             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'.
  19 11/27/09 Major revision; fade titles
  20  1/04/10 Change function "changeImagePointer" and "startStopAction" calls to
              object function calls
  21  1/13/10 Add textAlt to allow splitting of title and caption to different divs
  22  1/19/10 Allow text to snap at 50% opacity in face of IE bug
  23  5/27/10 Set text/caption display to "none" when opacity zero (for links)
  24  7/13/10 Change comments only, after determining that object.preloadImages() should
				  be called by object.slideShowInit(), not in slideShowAlbum.js

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

*/
var FIXIEBUG = false; //IE bug, set to true if browser is IE, used by "setOpacity()"
//The bug is distortion of large text if the alpha filter is used for opacity.
//The fix is to snap, not fade, the "text" (not "textAlt") div block
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.loaded = false;
	this.initialized = false;
}
slideShow.prototype.interval = 50; //Number of ms. each image shows in a fade

/* object.preloadImages()
	Preloads all images in "this" image group. Called by "object.slideShowInit()"
*/
slideShow.prototype.preloadImages = function () {
	if ( ! this.images ) return;
	if (this.images.length == 0) return;
	if (this.img0) return; //Return if already executed
	for (i = 0; i <= this.images.length; i++) {
		//prototype: this.img0=new Image(); this.img0.src=this.path+"image0.jpg";
		eval ('this.img' + i + '= new Image(); this.img' + i + '.src=this.path+"' + this.images[i] + '";');
	}
}

/* object.slideShowInit (param)
	Called after page is loaded ("onLoad=")
	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
*/
slideShow.prototype.slideShowInit = function (param) {
	if (this.images==undefined || this.images.length==0) return;
	if (! this.initialized) {
		//Check HTML setup if requested (function is in separate file that may or may not
		//be called as link in HTML header
		if (typeof (this.checkHTMLSetup) != 'undefined' && this.checkHTMLSetup()) return;
		//Initialize parameters based on HTML setup
		this.context = document.getElementById(this.name + '.context');
		this.imgA = document.images[this.name + '.A'];
		this.imgB = document.images[this.name + '.B'];
		this.doText = document.getElementById(this.name + '.text');
		this.doTextAlt = document.getElementById(this.name + '.textAlt');
		this.doTitle = document.getElementById(this.name + '.title.A') != undefined;
		this.doCaption = document.getElementById(this.name + '.caption.A') != undefined;
		if (this.doText) {
			this.text = document.getElementById(this.name + '.text');
			this.textA = document.getElementById(this.name + '.text.A');
			this.textB = document.getElementById(this.name + '.text.B');
			if (this.doTitle) {
				this.titleA = document.getElementById(this.name + '.title.A');
				this.titleB = document.getElementById(this.name + '.title.B');
			}
			if (this.doCaption) {
				this.captionA = document.getElementById(this.name + '.caption.A');
				this.captionB = document.getElementById(this.name + '.caption.B');
			}
		}
		if (this.doTextAlt) {
			this.textAlt = document.getElementById(this.name + '.textAlt');
			this.textAltA = document.getElementById(this.name + '.textAlt.A');
			this.textAltB = document.getElementById(this.name + '.textAlt.B');
		}
		this.widthContext = document.getElementById(this.name + ".context").style.width.replace(/\D/g,'');
		this.heightContext = document.getElementById(this.name + ".context").style.height.replace(/\D/g,'');
		this.imgA.loaded = false;
		this.imgB.loaded = false;
		this.nowShowing = 'B'; //Initialize which "frame" is currently showing
		this.preloadImages();
		this.initialized = true;
	}
	this.generateVirtual(); //Generate virtual vector and index if anything is omitted
	//Perform the requested initial state of the slideshow
	if (param == undefined) {
		//Put up first picture without starting show
		this.setStopStart('stop');
		this.refresh(0, true);
		return;
	}
	else if (param == -1) {
		//Stop the show
		this.setStopStart('stop');
		this.haltPending();
	}
	else if (param == -2) {
		//Stop the show and refresh the current index
		this.setStopStart('stop');
		this.haltPending()
		this.refresh(this.index);
	}
	else {
		//Start the show with the given initial delay
		this.setStopStart('start');
		if (param > 0) {
			this.delayStartID = setTimeout(this.name + ".refresh()",param);
		}
		else {this.refresh()}
	}
}
/* object.stopStartSlideShow();
		If show is running, stop it; if not, start at current picture with zero delay
*/
slideShow.prototype.stopStartSlideShow = function () {
	if (this.runShow == 1) this.slideShowInit(-1); //Stop the show
	else						  this.slideShowInit(0);	//start the show with zero delay
}

/* 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 object.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" 
*/
slideShow.prototype.setStopStart = function (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 this.startStopAction != 'undefined') this.startStopAction(action); //If additional action to be taken
}

slideShow.prototype.refresh = function (param, abrupt) {
	//Parameter "abrupt" overrides this.fadeMode and does an abrupt change
	//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
	this.abrupt = abrupt;
	this.haltPending();
	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;
			this.runShow = 0;
			break;
		case "B":
			//Step back one and stop the show
			if (--index < 0) index = this.virtual.length - 1;
			this.runShow = 0;
			break;
		case "F":
			//Step forward one and stop the show
			if (++index >= this.virtual.length) index = 0;
			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;
			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;
			}
			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 = this.index = absolute index of new image and caption, etc.
	//  this.oldIndex was set at the last call, or is undefined (initial condition)

	//-------------------READY TO INITIATE CHANGE OF IMAGE----------------------
	//Set up objects for old and new image, title and caption HTML structuress
	var nowHidden = this.nowShowing == 'A' ? 'B' : 'A';
	this.imgOld = document.images[this.name + '.' + this.nowShowing];
	this.imgNew = document.images[this.name + '.' + nowHidden];
	if(this.doText) {
		this.textOld = document.getElementById(this.name + '.text.' + this.nowShowing);
		this.textNew = document.getElementById(this.name + '.text.' + nowHidden);
	}
	if(this.doTextAlt) {
		this.textAltOld
		 = document.getElementById(this.name + '.textAlt.' + this.nowShowing);
		this.textAltNew = document.getElementById(this.name + '.textAlt.' + nowHidden);
	}
	if (this.doTitle) {
		this.titleOld = document.getElementById(this.name + '.title.' + this.nowShowing);
		this.titleNew = document.getElementById(this.name + '.title.' + nowHidden);
	}
	if (this.doCaption) {
		this.captionOld = document.getElementById(this.name + '.caption.' + this.nowShowing);
		this.captionNew = document.getElementById(this.name + '.caption.' + nowHidden);
	}
	//Get parameters for the new image, title and captions
	this.width = (this.fileWidths.length == undefined) ? this.fileWidths :
	 this.fileWidths[indexNew];
	this.height = (this.fileHeights.length == undefined) ? this.fileHeights :
	 this.fileHeights[indexNew];
	this.holdTime = (this.holdTimes.length == undefined) ? this.holdTimes :
	 this.holdTimes[indexNew];
	this.fadeTime = (this.fadeTimes.length == undefined) ? this.fadeTimes :
	 this.fadeTimes[indexNew];
	 //The following allows the specified fade time to apply to the full fade out and then to the full fade in.
	if (this.fadeMode == 'fadeOutIn'  && ! abrupt) this.fadeTime *= 2;
	this.numberOfIntervals = Math.round(1000*this.fadeTime / this.interval)
	if (this.doTitle) {
		this.titleText = (this.imageTitles == undefined) ?
		 ((this.mode == 'edit') ? '<i>(no title)</i>' : '&nbsp;') :
		 this.imageTitles[this.index];
	}
	if (this.doCaption) {
		this.captionText = (this.captions == undefined) ?
		 ((this.mode == 'edit') ? '<i>(no title)</i>' : '&nbsp;') :
		 this.captions[this.index];
	}

	//Set up new image, title and caption in HTML elements not currently showing
	this.setOpacity(this.imgNew, 0);
	this.imgNew.loaded = false;
	this.imgNew.src = this.path + this.images[this.index];
	this.imgNew.width = this.width;
	this.imgNew.height = this.height;
	this.imgNew.style.left = (this.widthContext - this.width) / 2;
	this.imgNew.style.top = (this.heightContext - this.height) / 2;
	if (this.doText) this.setOpacity(this.textNew, 0, 1); //IE bug
	if (this.doTextAlt) this.setOpacity(this.textAltNew, 0);
	if (this.doTitle) this.titleNew.innerHTML = this.titleText;
	if (this.doCaption) this.captionNew.innerHTML = this.captionText;

	//Perform beginning change function, if any
	if (typeof this.ChangeStartFunction != 'undefined')
	 this.changeStartFunction(this.index);

	//Continue with transition after new image is loaded
	this.refreshContinue();
	return;
}

slideShow.prototype.refreshContinue = function () {
	this.haltPending();
	var p = this; //'current slideShow object must survive setInterval call
	if (! this.imgNew.loaded) {
		//If new image not loaded, wait another 100 milliseconds and try again
		this.waitingID = setInterval(function () {p.refreshContinue()}, 100);
		return;
	}
	//---------------------------PERFORM TRANSITION------------------------
	if (this.fadeMode == 'none' || this.abrupt) {
		this.setOpacity(this.imgOld, 0);
		this.setOpacity(this.imgNew, 100);
		if (this.doText) {
			this.setOpacity(this.textOld, 0, 1); //IE bug
			this.setOpacity(this.textNew, 100, 1); //IE bug
		}
		if (this.doTextAlt) {
			this.setOpacity(this.textAltOld, 0);
			this.setOpacity(this.textAltNew, 100);
		}
		this.refreshCloseout();
	}
	else {
		//this.fadeMode == 'fadeOutIn' or 'crossFade'
		this.step = 0;
		this.inProcessID = setInterval(function () {p.refreshStep();}, this.interval);
	}
	return;
}

slideShow.prototype.refreshStep = function () {
	++this.step;
	var opacity = Math.round(100 * this.step / this.numberOfIntervals);
	var inverseOpacity = 100 - opacity;
	if (this.fadeMode == 'fadeOutIn') {
		//The numbers here cause opacity to swing from -100 to 100 and inverseOpacity to swing from 100 to -100. setOpacity() floors at 0.
		opacity = 2 * opacity - 100;
		inverseOpacity = - opacity;
	}
	if (this.step >= this.numberOfIntervals) opacity = 100;
	this.setOpacity(this.imgOld, inverseOpacity);
	this.setOpacity(this.imgNew, opacity);
	if (this.doText) {
		this.setOpacity(this.textOld, inverseOpacity, 1); //IE bug
		this.setOpacity(this.textNew, opacity, 1); //IE bug
	}
	if (this.doTextAlt) {
		this.setOpacity(this.textAltOld, inverseOpacity);
		this.setOpacity(this.textAltNew, opacity);
	}
	if (this.step >= this.numberOfIntervals) {
		this.haltPending();
		this.refreshCloseout();
	}
}

slideShow.prototype.refreshCloseout = function () {
	if (typeof changeEndFunction != 'undefined') this.changeEndFunction(this.index);
	this.nowShowing = (this.nowShowing == 'A') ? 'B' : 'A';
	//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 this.changeImagePointer == 'function') {
		this.changeImagePointer(this,this.oldIndex,this.index);
	}
	if (this.runShow) {
		this.nextPixID = setTimeout(this.name + ".refresh()", 1000 * this.holdTime);
	}
	this.oldIndex=this.index; //Save for next time
}

//Generate this.virtual and this.indexVirtual, properly reflecting re-sort and omitted images if appropriate
slideShow.prototype.generateVirtual = function () {
	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) this.removeOmittedFromVirtual();
	}
	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
		this.removeOmittedFromVirtual();
	}
	//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;
		}
	}
}

slideShow.prototype.removeOmittedFromVirtual = function () {
	var newVirtual = new Array();
	for (var i = 0; i < this.virtual.length; i++) {
		if (this.omit[i] == 0) newVirtual.push(this.virtual[i]);
	}
	this.virtual = newVirtual;
}

slideShow.prototype.setOpacity = function (object, opacity, titleFlag) {
	opacity = Math.max(opacity, 0); //opacity could be negative if 'fadeOutIn'
	object.style.opacity = opacity / 100;
	//The following is needed to allow links to function in the "A" window
	object.style.display = (opacity == 0) ? 'none' : 'inline';
	if (FIXIEBUG && titleFlag) {
		//Make title snap at 50% to overcome IE bug
		object.style.visibility = opacity < 50 ? 'hidden' : 'visible';
	}
	else object.style.filter = 'alpha(opacity=' + opacity + ')';
}

slideShow.prototype.haltPending = function () {
	clearTimeout(this.delayStartID);
	clearInterval(this.waitingID);
	clearTimeout(this.nextPixID);
	clearInterval(this.inProcessID);
}

function jsDebug () {
	var string, msg = '';
	while (string = prompt('Enter object name', msg)) {
		try {var result = eval(string)}
		catch (exception) {
			alert(exception);
			msg = string;
			continue;
		}
		var type = typeof result;
		if (type == 'object') alert('(object)');
		else alert(string + ' (' + type + '):\n' + result);
		msg = string;
	}
}
