/******************************************************************************

EXAMPLES

For usage examples, check out the slideshows in the CustomStyle NPI:

	http://www.geappliances.com/products/introductions/customstyle/overview/slideshow.htm
	http://www.geappliances.com/products/introductions/customstyle/whats_new/slideshow.htm
	http://www.geappliances.com/products/introductions/customstyle/feature_gallery/slideshow.htm
	http://www.geappliances.com/products/introductions/customstyle/whats_inside/slideshow.htm
	http://www.geappliances.com/products/introductions/customstyle/design_options/slideshow.htm

QUICK START

Stick the following placeholders somewhere in the body:

	<div id="slideshowPhoto"></div>

		This will be populated with a single <img> element for the
		currently selected full-sized photo.

	<div id="slideshowThumbnails"></div>

		This will be populated with a sequence of <img> elements for
		all photo thumbnails.

	<div id="slideshowText"></div> <!-- OPTIONAL -->

		This will be populated with a heading for the currently
		selected photo's title and some text for the description of
		the currently selected photo.  subtitle, comment, and
		footnotes (more on those later) will be populated here as
		well.

If you want previous/next navigation links, use the following as
starting points:

	<a href="#" onclick="switchToPreviousPhoto(); return false;">Previous</a>

	<a href="#" onclick="switchToNextPhoto(); return false;">Next</a>

In/near the beginning of your body, create an invisible div containing
bits of HTML (titles, descriptions, etc.) for the slideshow that you
can simply copy from the landing page:

	<!-- start HTML bits for the slideshow -->
	<div style="display: none;">

		<!-- Example 1. -->

		<h3 id="title0">Photo Title</h3>
		<div id="description0">
			<p>A description of the photo.</p>
		</div>

		<!-- Example 2.  Subtitles, comments, and footnotes are optional. -->

		<h3 id="title1">Photo Title</h3>
		<div id="subtitle0">(Available November 2005)</div>
		<div id="description1">
			<p>A description of the photo.</p>
			<p>A description of the photo.</p>
		</div>
		<p id="comment1">Only GE has it!</p>
		<div id="footnotes1">Only available on select models.</div>

		...

	</div>
	<!-- end HTML bits for the slideshow -->

	(NOTES: You *can* use your own nomenclature for the ids in the
	invisible div.  Also, this code doesn't care what element you're
	applying those id attributes to; you can leave h3/p/etc. elements
	as they are and stick id attributes in them, or you can change
	them to div elements if you feel you must.)

To specify the list of photos and references to their bits of text:
run the following code before calling setupSlideshow.  We use scripts
included directly in the <head> instead of external files, but this
isn't strictly necessary.

	var slideshowPhotoURLFormat = "images/*.jpg";
	var slideshowThumbnailURLFormat = "images/*_sm.jpg";
	// the * gets replaced with photo IDs.

	var slideshowPhotoList = new Array(

		// Example 1.
		{	// These three are the only required properties
			// for each element in slideshowPhotoList.

			photoID: "044229",
			titleElementId: "title0",
			descriptionElementId: "description0"
		},

		// Example 2.
		{	// For special cases where the photo and thumbnail
			// URLs don't follow the URL format templates you
			// specified above, you can specify photoURL and
			// thumbnailURL *instead* of photoID.

			photoURL: "images/044203.jpg",
			thumbnailURL: "images/044203_sm.jpg",

			// also, there are some optional properties in addition
			// to the title and description:

			titleElementId: "title1",
			subtitleElementId: "subtitle1", // optional
			descriptionElementId: "description1",
			footnotesElementId: "footnotes1", // optional
			commentElementId: "comment1" // optional
		},

		...
	);		

	REMEMBER: if you used your own nomenclature for the ids in the
	invisible div for the HTML bits above, you have to use those
	ids here as well.

Run this code when your document is finished loading to initialize the
photo gallery.  We do this in a <script> element at the end of the
<body>.

	setupSlideshow({
		thumbnailWidth: 60,
		thumbnailHeight: 63,
		photoWidth: 480,
		photoHeight: 500,
		onmouseover: function () { ... } // optional
	});

THINGS TO KNOW WHEN BUILDING YOUR STYLESHEETS

-	class="selected" will be applied to the currently selected image
	thumbnail.
-	class="slideshowPhotoTitle" is used for currently displayed
	photo titles.
-	class="slideshowPhotoSubtitle" is used for currently displayed
	photo subtitles.
-	class="slideshowPhotoComment" is used for currently displayed
	comments like "New!" or "Only GE has it!".
-	class="slideshowPhotoDescription" is used for currently displayed
	photo descriptions.
-	class="slideshowPhotoFootnotes" is used for currently displayed
	footnotes.

DETAILS

slideshowPhotoURLFormat and slideshowThumbnailURLFormat are strings
that specify the location of thumbnail and full-size images where only
a photo ID is specified.  Place an asterisk in the string, and the
first asterisk will get replaced with the photo ID to create the image
URL.  URLs are relative to the page, not any of the JavaScript files.

slideshowPhotoList is an array of objects containing properties that
provide information about each image.

The following properties are required:
-	titleElementId
-	descriptionElementId

The following properties are optional:
-	subtitleElementId    (e.g., "(Available December 2005)")
-	commentElementId     (e.g., "Only GE has it!")
-	footnotesElementId   (e.g., "Not available in all models.")

To specify the location of the image, you can do one of two things:
-	specify a photoID property.
	"images/PHOTOID.jpg" (e.g.) will be used for the photo URL.
	"images/PHOTOID_sm.jpg" (e.g.) will be used for the thumbnail URL.
	See the description of the slideshow*URLFormat variables
	above for more details.
-	specify photoURL and thumbnailURL properties.  They are simply URLs.
	URLs are relative to the page, not the JavaScript file.
-	In case of conflict between an explicitly specified URL and a photoID,
	the explicitly specified URL overrides, and any URLs not specified will
	be based on the photoID.

Added feature(s):
-	On the description and footnotes,
	newline characters (\n) get replaced with an actual HTML line break.

THE OLD WAY OF POPULATING SLIDESHOW DATA

-	Instead of specifying titleElementId, descriptionElementId, etc.,
	we used to simply have title, description, etc. properties which
	contained strings of HTML.

-	For backward compatibility, this library of code continues to work
	with slideshows containing data populated the old way.

-	If a title property *and* a titleElementId property are
	specified, the title property overrides.  The same applies for
	all the other text properties: subtitle, description,
	footnotes, and comment.

-	Good backward compatibility test:
	http://www.geappliances.com/products/introductions/cooktops/gas/feature_gallery/slideshow.htm

******************************************************************************/

/* Typically set in a separate JavaScript file. */
var slideshowPhotoList;
var slideshowPhotoURLFormat;
var slideshowThumbnailURLFormat;

/* Typically specified when calling setupSlideshow. */
var slideshowPhotoWidth;
var slideshowPhotoHeight;
var slideshowThumbnailWidth;
var slideshowThumbnailHeight;

/* <div id="slideshowPhoto">, etc. */
var slideshowPhotoElement;
var slideshowTextElement;
var slideshowThumbnailsElement;

/* These are containers created once within these elements, then dynamically
   populated as photo switch functions are called. */
var currentPhotoContainer;
var currentTextContainer;
var currentPhotoIndex;

/* Photo switch functions are disabled until setupSlideshow says they're
   ready to go. */
var slideshowIsReady = 0;

/* Utility function used by unescapeEntities.  Concatentates all text nodes,
   ignores everything else, and returns the resulting string. */
function concatTextData (node) {
	if (node.nodeType == 3 /* TEXT_NODE */) {
		return node.data;
	}
	else if (node.hasChildNodes()) {
		var result = "";
		for (var i = 0; i < node.childNodes.length; ++i) {
			result = result + concatTextData(node.childNodes[i]);
		}
		return result;
	}
	else {
		return "";
	}
}

/* Utility function that converts "&trade;" to "\u2122", for example.
   Since we're sticking HTML entities in the title and description strings,
   we need to use the title strings for alt attributes, we're using DOM
   methods to insert images into the slideshows, and those DOM methods
   need unescaped text in order to do the right thing, something like
   this is necessary. */
function unescapeEntities (string) {
	if (!document || !document.createElement) {
		/* fallback */
		return string;
	}
	var span = document.createElement("span");
	span.innerHTML = string;
	return concatTextData(span);
}

/* Utility function to remove all children from a node. */
function removeAllChildren (node) {
	while (node.hasChildNodes())
		node.removeChild(node.childNodes[0]);
}

/* Called by onclick event.  Handler is set up by setupSlideshow. */
function switchThisPhoto () {
	if (!slideshowIsReady) return;
	switchPhoto(slideshowPhotoList[this.photoIndex]);
	return false; /* browser ignores */
}

/* Called by "previous" navigation button. */
function switchToPreviousPhoto () {
	if (!slideshowIsReady) return;
	var newIndex = currentPhotoIndex - 1;
	if (newIndex < 0) newIndex = slideshowPhotoList.length - 1;
	switchPhoto(slideshowPhotoList[newIndex]);
}

/* Called by "next" navigation button. */
function switchToNextPhoto () {
	if (!slideshowIsReady) return;
	var newIndex = (currentPhotoIndex + 1) % slideshowPhotoList.length;
	switchPhoto(slideshowPhotoList[newIndex]);
}

function switchPhoto (photo) {
	if (!slideshowIsReady) return;
	currentPhotoIndex = photo.photoIndex;
	
	/* Switch the selected thumbnail. */
	
	for (var i = 0; i < slideshowPhotoList.length; ++i) {
		if (i == currentPhotoIndex) {
			slideshowPhotoList[i].thumbnailElement.className = "selected";
		} else {
			slideshowPhotoList[i].thumbnailElement.className = "";
		}
	}
	
	/* Make sure the current...Container elements are set up. */
	
	if (!currentPhotoContainer) {
		currentPhotoContainer = document.createElement("div");
		slideshowPhotoElement.appendChild(currentPhotoContainer);
	}
	if (slideshowTextElement) {
		if (!currentTextContainer) {
			currentTextContainer = document.createElement("div");
			slideshowTextElement.appendChild(currentTextContainer);
		}
	}

	/* Populate the current...Container elements. */
	
	if (slideshowTextElement) {
		removeAllChildren(currentTextContainer);

		if (photo.title != null) {
			var h2 = document.createElement("h2");
			h2.className = "slideshowPhotoTitle";
			h2.innerHTML = photo.escapedTitle;
			currentTextContainer.appendChild(h2);
		}
		if (photo.subtitle != null) {
			var div = document.createElement("div");
			div.className = "slideshowPhotoSubtitle";
			div.innerHTML = photo.subtitle;
			currentTextContainer.appendChild(div);
		}
		if (photo.comment != null) {
			var div = document.createElement("div");
			div.className = "slideshowPhotoComment";
			div.innerHTML = photo.comment;
			currentTextContainer.appendChild(div);
		}
		if (photo.description != null) {
			var div = document.createElement("div");
			div.className = "slideshowPhotoDescription";
			div.innerHTML = photo.description.replace(/\n/g, "<br />\n");
			currentTextContainer.appendChild(div);
		}
		if (photo.footnotes != null) {
			var div = document.createElement("div");
			div.className = "slideshowPhotoFootnotes";
			div.innerHTML = photo.footnotes.replace(/\n/g, "<br />\n");
			currentTextContainer.appendChild(div);
		}
	}

	var image = document.createElement("img");
	image.width = slideshowPhotoWidth;
	image.height = slideshowPhotoHeight;
	image.src = photo.photoURL;
	image.alt = photo.unescapedTitle;
	image.title = photo.unescapedTitle;
	removeAllChildren(currentPhotoContainer);
	currentPhotoContainer.appendChild(image);
	
	/* We may have to scroll the thumbnail container so that the selected
	   thumbnail is visible. */
	
	var thumbnail = photo.thumbnailElement;
	if (thumbnail.offsetTop < slideshowThumbnailsElement.scrollTop) {
		slideshowThumbnailsElement.scrollTop = thumbnail.offsetTop;
	}
	else if ((thumbnail.offsetTop + thumbnail.offsetHeight) >
	         (slideshowThumbnailsElement.scrollTop +
	          slideshowThumbnailsElement.offsetHeight)) {
		slideshowThumbnailsElement.scrollTop = (
			thumbnail.offsetTop + thumbnail.offsetHeight
			- slideshowThumbnailsElement.offsetHeight
		);
	}
}

function setupSlideshow (options) {
	slideshowIsReady = 0;
	
	/* required */
	slideshowPhotoElement      = document.getElementById("slideshowPhoto");
	slideshowThumbnailsElement = document.getElementById("slideshowThumbnails");
	
	/* optional */
	slideshowTextElement       = document.getElementById("slideshowText");

	if (options.photoWidth != null)	
		slideshowPhotoWidth = options.photoWidth;
	if (options.photoHeight != null)	
		slideshowPhotoHeight = options.photoHeight;
	if (options.thumbnailWidth != null)	
		slideshowThumbnailWidth = options.thumbnailWidth;
	if (options.thumbnailHeight != null)	
		slideshowThumbnailHeight = options.thumbnailHeight;
	
	for (var i = 0; i < slideshowPhotoList.length; ++i) {
		var photo = slideshowPhotoList[i];
		if (photo.photoURL == null) {
			photo.photoURL = slideshowPhotoURLFormat.replace(/\*/, photo.photoID);
		}
		if (photo.thumbnailURL == null) {
			photo.thumbnailURL = slideshowThumbnailURLFormat.replace(/\*/, photo.photoID);
		}

		/**********************************************************************
			extract innerHTML from elements specified using
			titleElementId, descriptionElementId, etc.
		**********************************************************************/
		var convertElementIdProperty = function (newpropname, oldpropname) {
			/* For backward compatibility, existing title, description,
			   etc. will NOT be overridden. */
			if (photo[oldpropname] != null) return;

			var id = photo[newpropname];
			if (id == null) return;
			if (document.getElementById) {
				var el = document.getElementById(id);
				if (el) {
					photo[oldpropname] = el.innerHTML.replace(/\n/g, " ");
					/* recall that \n is replaced with <br />
					   in description and footnotes for some reason.
					   We'd like to avoid this for stuff we're pulling
					   from HTML elements. */
				}
			}
		};
		/*                       new                     old                 */
		/*                       ---                     ---                 */
		convertElementIdProperty("titleElementId",       "title");
		convertElementIdProperty("descriptionElementId", "description");
		convertElementIdProperty("subtitleElementId",    "subtitle");
		convertElementIdProperty("commentElementId",     "comment");
		convertElementIdProperty("footnotesElementId",   "footnotes");
		/*********************************************************************/
		
		photo.escapedTitle = photo.title;
		photo.unescapedTitle = unescapeEntities(photo.title);

		var anchor = document.createElement("a");
		var image = document.createElement("img");
		image.src = photo.thumbnailURL;
		image.width = slideshowThumbnailWidth;
		image.height = slideshowThumbnailHeight;
		image.alt = photo.unescapedTitle;
		image.title = photo.unescapedTitle;
		anchor.appendChild(image);
		slideshowThumbnailsElement.appendChild(anchor);
		photo.thumbnailElement = image;
		
		/* Since these are accessible from functions called due to
		   events, give them access to the appropriate index in
		   slideshowPhotoList. */
		photo.photoIndex = i; /* switchPhoto() is called with this. */
		anchor.photoIndex = i; /* onclick is called on this object. */
		
		anchor.onclick = switchThisPhoto;
		if (options.onmouseover) {
			anchor.onmouseover = options.onmouseover;
		}
		anchor.href = "#"; /* required in MSIE for a:hover to work. */
	}
	slideshowIsReady = 1; /* enable the photo switch functions. */

	/* Which image gets shown upon page load?  If the query string's startat
	   parameter is an integer between 0 and (number of photos) - 1, it
	   determines which photo is shown first.  Otherwise, the first photo
	   is shown first. */
	
	var initialIndex = 0;
	var match = document.location.search.match(/[\?\&]startat=(\d+)/);
	if (match) {
		var i = match[1];
		if (i >= 0 && i < slideshowPhotoList.length) {
			initialIndex = i;
		}
	}
	switchPhoto(slideshowPhotoList[initialIndex]);
}

