Tutorial Categories:

HTML/CSS JavaScript/AJAX Server-Side Marketing General Comp-Sci

The Creation Of A Slideshow Using Only JavaScript

By Justin Poirier

This paper details the creation of a slideshow to be inserted in a web page. Each image displayed is downloaded from the server only when it is needed, the user having come to it for the first time as she scrolled through the images using the back and forward buttons. The behaviour of presenting images to the user in a predefined order is implemented solely on the client side using AJAX. All that is required on the server side is that the images are .jpg files placed in a subdirectory of the directory in which the page exists and named after sequential numbers starting at one, with the file extension in lower case letters. ie. the files will be named "1.jpg", "2.jpg", etc. The directory they reside in must be named "/images/". Larger versions of the images may be placed in a separate subdirectory of the page's main directory, called "/large_images/". Each image displayed will be a hyperlink to the larger version of itself. The larger version of an image must be given the same filename as the main version. Descriptions of the images may also be created in a separate subdirectory. The description of an image will appear underneath it on screen. The file for an image's description must be named after the same number as the image file, with a file extension of ".txt" in lowercase letters. The subdirectory for descriptions must be called "/image_descriptions/".

The slideshow will be contained in a single parent div with id "slideshow". This div will contain as a child an anchor with id "slideshowLink", which will create the link whereby clicking an image leads to a larger version of it. Images will have class "slideshowImage". The image being viewed and one or more invisible images will be the only children of the anchor. In the initial HTML of the page the anchor and the first image will be the only contents of the "slideshow" div, with their href and src attributes, respectively, pointing to the first image of the slideshow. The slideshow will only be fully created if the user has JavaScript turned on, and if they don't, these anchor and img tages will remain as the only contents of the "slideshow" div, creating a clickable image as a graceful substitute for the slideshow.

The style definitions of the "slideshow" id and "slideshowImage" class follow. The "slideshowLink" does not have an explicit style definition.

div#slideshow
{
position:relative;
margin:89px auto 0px -108px;
background-color:#FFFFBD;
overflow: visible;
display:block;
width:245px;
height:185px;
}
img.slideshowImage {
width:241px;
height:159px;
border:3px solid rgb(128,0,0);
position:absolute;
top:0px;
left:0px
}

There will be JavaScript code executed when the page is loaded, which will finish setting up the slideshow; and also code executed when the user clicks the slideshow's forward and back buttons, which will be responsible for ensuring the appropriate image is displayed. In our implementation, the forward button will be an arrow pointing right henceforth referred to as the "right button", and the back button will be an arrow pointing left henceforth referred to as the "left button". These could obviously be converted to up and down buttons easily.

The greatest challenge in creating this slideshow using only client-side scripting lies in the fact that we want the user to be able to scroll in either direction, with the sequence of images wrapping when she scrolls past one end, to the image at the opposite end. This requirement means we can't simply display the image with the next higher- or lower-numbered filename when she presses the right or left buttons, respectively. If she scrolled left past image #1, we would have to display the highest-numbered image next but we'd have no way of knowing what number that is. So another technique is needed.

The technique chosen involves building an array containing the numbers of images in a meaningful order. This is the order to which the user's left and right scrolling will be relative, as opposed to the numeric order of the image's filenames. When the user scrolls to an image that she has already seen, the number of the filename of the image to display will already exist in the array and can simply be looked up. When she scrolls to an image she has not seen, we will download the next image we have not yet downloaded (in order of filename), and add it to the array. At all times an index in the array, called "numberKnownFromBottomUp", will be maintained. All array positions to the left of this index will represent images that were downloaded on account of the user having gone past the highest-numbered image yet downloaded, while scrolling right. Likewise all positions to the right of this index represent images downloaded after left-scrolling. When the image currently displayed is one array position left of numberKnownFromBottomUp and the user scrolls right, the array's size is increased by one, its entire contents from numberKnownFromBottomUp onward are shifted right, and the next undownloaded image number is placed in the opening created. Then numberKnownFromBottomUp is incremented by one. When the current image is right at numberKnownFromBottomUp and the user scrolls left, the array's contents from numberKnownFromBottomUp onward are shifted in the same way and the next image number placed in the opening, but numberKnownFromBottomUp need not be incremented.

To summarize, the array represents the order of all known images, with images not yet seen set to be added between (numberKnownFromBottomUp - 1) and numberKnownFromBottomUp (or be placed at the end of the array if numberKnownFromBottomUp is 0). It should be noted that the order of the images is created "on the fly", and is defined by the sequence of right and left button presses by the user. For instance, if the user starts out by scrolling to the right, the images, which will be downloaded in order of their filenames, will be added to the array from left to right, expanding the array by one each time as the algorithm sees that the user has scrolled right when the position in the array is already one less than numberKnownFromBottomUp. If, however, she then scrolls all the way to the left past the 0 index in the array, then the images downloaded, even as their filenames continue to be in order, will start being inserted from the right end of the array inward as the algorithm sees a left scroll when the position in the array is already at numberKnownFromBottomUp.

We've said that an array will be used to keep track of which images have so far been downloaded, and their order, but we haven't said how these images will actually be stored so that they needn't be re-downloaded every time they are needed. AJAX HTTP requests, with a MIME type of "HEAD", will be used when a new image must be downloaded to determine if the image exists. The actual downloading of the image will be done implicitly by creating an img element in the page's DOM for the image. All img's created for images will be positioned in the exact same location within the slideshow, with the "visibility" CSS property set to "hidden" for all but the image being viewed.

The image descriptions will be stored in an array. That array will contain the descriptions in the order of the filenames that they represent. They will be inserted in it as they are downloaded. When a description must be displayed it will be pulled from the array using the number of the corresponding image's filename as an index.

The aforementioned code that is executed when the user presses the right and left buttons is where the techniques we've described for downloading and displaying images and descriptions will be implemented. The "clickLeftButton()" and "clickRightButton()" functions are registered for callback when the user presses the left and right buttons, respectively. These functions will determine whether the next image has been downloaded, and if it has they will display it. If it hasn't, they will make an AJAX request, the response for which will be handled by "receiveImage()". This function will insert the image's number in the array, create an img element for the image, and display it. Another AJAX request will then be made for the image's description, the response for which will be handled by "receiveImageDescription()". In addition to numberKnownFromBottomUp, these functions use the following global variables:

images[] the main array we've discussed, containing the numbers of images.
imageIndex the position in images[] of the number of the image currently being displayed.
imageDescriptions[] the array containing the image descriptions, as strings.
numberKnownFromTopDown a counter maintained to always equal the length of images[] minus numberKnownFromBottomUp, used so that this value need not be calculated when it is needed.
highestFound a boolean representing whether the highest-numbered image has yet been downloaded.
highest the number of the highest image, if it has been found.
currentlyInBottomGroup a boolean representing whether the current position in images[] is in the portion before numberKnownFromBottomUp, ie. at an image that was downloaded on account of forward-scrolling; or equal to or after numberKnownFromBottomUp, ie. at an image downloaded on account of backward-scrolling.
awaitingImage a boolean representing whether or not there are outstanding AJAX requests for an image and its description, used to disable the left and right buttons' functionalities until the response arrives and is dealt with.

Also note that the code for these functions refers to an element with id "imageDescription". This is the div appearing underneath the slideshow's image, containing the image's description.

The code for these functions follows. Note that they contain code to implement AJAX. This code is explained sufficiently by the comments surrounding it.

function clickLeftButton() {
	if (!awaitingImage) {
		if (highestFound) {
			imageIndex = imageIndex - 1;
			if (imageIndex < 0)
				imageIndex = highest - 1;
			if (imageIndex == (highest - 1))
				document.getElementById("slideshowLink").childNodes[images[0] - 1].style.visibility = "hidden";
			else
				document.getElementById("slideshowLink").childNodes[images[imageIndex + 1] - 1].style.visibility = "hidden";
			document.getElementById("slideshowLink").childNodes[images[imageIndex] - 1].style.visibility = "visible";
			document.getElementById("slideshowLink").href = 'large_images/' + images[imageIndex] + '.jpg';
			document.getElementById("imageDescription").innerHTML = imageDescriptions[images[imageIndex] - 1];
		} else {
			imageIndex = imageIndex - 1;
			if (imageIndex < 0) {
				imageIndex = images.length - 1;
				currentlyInBottomGroup = false;
			}
			if (!currentlyInBottomGroup && ((images.length - imageIndex) > numberKnownFromTopDown)) {
				awaitingImage = true;
				// Now we must make a request for the next image that we haven't pulled in yet:
				// First we get an HTTP object:
				var XMLHttp = getRequestObject();
				if (XMLHttp==null) {
					alert('Next image not available at this time.');
					awaitingImage = false;
					imageIndex = imageIndex + 1;
					if (imageIndex == images.length) {
						imageIndex = 0;
						currentlyInBottomGroup = true;
					}
					return;
				}
				// Now we register the handler function:
				XMLHttp.onreadystatechange = function() { receiveImage(XMLHttp, false); }
				// Now we open the request:
				XMLHttp.open('HEAD', 'images/' + (highest + 1) + '.jpg', true);
				// Now we send it:
				XMLHttp.send(null);
			} else {
				if (imageIndex == (images.length - 1))
					document.getElementById("slideshowLink").childNodes[images[0] - 1].style.visibility = "hidden";
				else
					document.getElementById("slideshowLink").childNodes[images[imageIndex + 1] - 1].style.visibility = "hidden";
				document.getElementById("slideshowLink").childNodes[images[imageIndex] - 1].style.visibility = "visible";
				document.getElementById("slideshowLink").href = 'large_images/' + images[imageIndex] + '.jpg';
				document.getElementById("imageDescription").innerHTML = imageDescriptions[images[imageIndex] - 1];
			}
		}
	}
	return false;
}
function clickRightButton() {
	if (!awaitingImage) {
		if (highestFound) {
			imageIndex = imageIndex + 1;
			if (imageIndex >= highest)
				imageIndex = 0;
			if (imageIndex == 0)
				document.getElementById("slideshowLink").childNodes[images[highest - 1] - 1].style.visibility = "hidden";
			else
				document.getElementById("slideshowLink").childNodes[images[imageIndex - 1] - 1].style.visibility = "hidden";
			document.getElementById("slideshowLink").childNodes[images[imageIndex] - 1].style.visibility = "visible";
			document.getElementById("slideshowLink").href = 'large_images/' + images[imageIndex] + '.jpg';
			document.getElementById("imageDescription").innerHTML = imageDescriptions[images[imageIndex] - 1];
		} else {
			imageIndex = imageIndex + 1;
			if (imageIndex >= images.length) {
				imageIndex = 0;
				currentlyInBottomGroup = true;
			}
			if (currentlyInBottomGroup && (imageIndex >= numberKnownFromBottomUp)) {
				awaitingImage = true;
				// Now we must make a request for the next image that we haven't pulled in yet:
				// First we get an HTTP object:
				var XMLHttp = getRequestObject();
				if (XMLHttp==null) {
					alert('Next image not available at this time.');
					awaitingImage = false;
					imageIndex = imageIndex - 1;
					if (imageIndex <= 0) {
						imageIndex = images.length - 1;
						currentlyInBottomGroup = false;
					}
					return;
				}
				// Now we register the handler function:
				XMLHttp.onreadystatechange = function() { receiveImage(XMLHttp, true); }
				// Now we open the request:
				XMLHttp.open('HEAD', 'images/' + (highest + 1) + '.jpg', true);
				// Now we send it:
				XMLHttp.send(null);
			} else {
				if (imageIndex == 0)
					document.getElementById("slideshowLink").childNodes[images[images.length - 1] - 1].style.visibility = "hidden";
				else
					document.getElementById("slideshowLink").childNodes[images[imageIndex - 1] - 1].style.visibility = "hidden";
				document.getElementById("slideshowLink").childNodes[images[imageIndex] - 1].style.visibility = "visible";
				document.getElementById("slideshowLink").href = 'large_images/' + images[imageIndex] + '.jpg';
				document.getElementById("imageDescription").innerHTML = imageDescriptions[images[imageIndex] - 1];
			}
		}
	}
	return false;
}
function receiveImage(requestParam, calledFromRB) {
	try {
		if (requestParam.readyState == 4) {
			if (requestParam.status == 200) {
				// The image with the imageIndex exists.
				// We want to shift the contents of images right by one from numberKnownFromBottomUp onward.
				for (index = images.length - 1; index >= numberKnownFromBottomUp; index = index - 1)
					images[index + 1] = images[index];
				// Now we put the newly-retrieved image into the space we made:
				images[numberKnownFromBottomUp] = highest + 1;
				// If we were going down from the top, we must move the imageIndex back up one:
				if (!calledFromRB)
					imageIndex = imageIndex + 1;
				var imageToAdd = document.createElement("IMG");
				imageToAdd.setAttribute("src", "images/" + images[numberKnownFromBottomUp] + ".jpg");
				imageToAdd.setAttribute("name", "images/" + images[numberKnownFromBottomUp] + ".jpg");
				imageToAdd.className = "slideshowImage";
				document.getElementById("slideshowLink").appendChild(imageToAdd);
				if (calledFromRB)
					document.getElementById("slideshowLink").childNodes[images[numberKnownFromBottomUp - 1] - 1].style.visibility = "hidden";
				else
					document.getElementById("slideshowLink").childNodes[images[numberKnownFromBottomUp + 1] - 1].style.visibility = "hidden";
				document.getElementById("slideshowLink").href = 'large_images/' + images[numberKnownFromBottomUp] + '.jpg';
				highest = highest + 1;
				if (calledFromRB)
					numberKnownFromBottomUp = numberKnownFromBottomUp + 1;
				else
					numberKnownFromTopDown = numberKnownFromTopDown + 1;
				// Now we must make a request for the next image description that we haven't pulled in yet:
				var XMLHttp = getRequestObject();
				if (XMLHttp==null) {
					imageDescriptions[images[imageIndex] - 1] = 'Click to enlarge.';
					document.getElementById("imageDescription").innerHTML = 'Click to enlarge';
					awaitingImage = false;
					return;
				}
				XMLHttp.onreadystatechange = function() { receiveImageDescription(XMLHttp); }
				XMLHttp.open('GET', 'image_descriptions/' + highest + '.txt', true);
				XMLHttp.send(null);
			} else {
				// The image does not exist. This means we've retrieved all images.
				highestFound = true;
				if (calledFromRB) {
					document.getElementById("slideshowLink").childNodes[images[numberKnownFromBottomUp - 1] - 1].style.visibility = "hidden";
					document.getElementById("slideshowLink").childNodes[images[numberKnownFromBottomUp] - 1].style.visibility = "visible";
					//document.slideshowImage.src = 'images/' + images[numberKnownFromBottomUp] + '.jpg';
					document.getElementById("slideshowLink").href = 'large_images/' + images[numberKnownFromBottomUp] + '.jpg';
					document.getElementById("imageDescription").innerHTML = imageDescriptions[images[numberKnownFromBottomUp] - 1];
				} else {
					document.getElementById("slideshowLink").childNodes[images[numberKnownFromBottomUp] - 1].style.visibility = "hidden";
					document.getElementById("slideshowLink").childNodes[images[numberKnownFromBottomUp - 1] - 1].style.visibility = "visible";
					//document.slideshowImage.src = images[numberKnownFromBottomUp - 1] + '.jpg';
					document.getElementById("slideshowLink").href = 'large_images/' + images[numberKnownFromBottomUp - 1] + '.jpg';
					document.getElementById("imageDescription").innerHTML = imageDescriptions[images[numberKnownFromBottomUp - 1] - 1];
				}
				awaitingImage = false;
			}
		}
	} catch (e) {
		alert('An error has occured in this page.');
		document.getElementById("slideshow").style.display = "none";
	}
}
function receiveImageDescription(requestParam) {
	try {
		if (requestParam.readyState == 4) {
			if (requestParam.status == 200) {
				// First we add the response to the imageDescriptions array:
				imageDescriptions[images[imageIndex] - 1] = requestParam.responseText + ' Click to enlarge.';
				// Then we set the contents of the imageDescription div to the text of the response:
				document.getElementById("imageDescription").innerHTML = requestParam.responseText + ' Click to enlarge.';

			} else {
				// The image description does not exist. Only the words "Click to enlarge" will appear under the image. We add this string to imageDescriptions and the imageDescription div:
				imageDescriptions[images[imageIndex] - 1] = 'Click to enlarge.';
				document.getElementById("imageDescription").innerHTML = 'Click to enlarge';
			}
			awaitingImage = false;
		}
	} catch (e) {
		alert('An error has occured in this page.');
		document.getElementById("slideshow").style.display = "none";
	}
}

The slideshow script also has a function called "initialize()" that is to be executed when the page is loaded, and the line window.onload = initialize; to ensure this happens.

It is assumed there will be some kind of element on the page stating that JavaScript is turned off, so that there will be an explanation to the user which will remain if JavaScript is in fact turned off, but which we can remove if JavaScript is turned on. Our code assumes this element has id "JavaScriptLabel". The first line of initialize() removes this element by setting its "display" CSS property to "none".

initialize() then assigns the initial values for the global variables discussed earlier. After that, it creates an img element for the slideshow image "2.jpg". Recall that an img for "1.jpg" is already present from the initial HMTL of the page. These two images are the minimum required for the slideshow to work. The newly-created img's visibility property is set to "hidden", since "1.jpg" will remain the image currently in view. The number 2 is also added to images[].

The remaining elements of the slideshow are then created; recall that initially it only contained the first image and its enclosing anchor in case the user did not have JavaScript turned on. The "imageDescription" div is created now, as well as the left and right buttons, which have id's "leftButton" and "rightButton", respectively. The style definitions for these id's follow.

#imageDescription {
font-size:10pt;
color:rgb(128,0,0);
border:1px solid rgb(128,0,0);
background-color:#FFFFD7;
line-height:100%;
position:absolute;
top:166px;
left:0px;
height:56px;
width:239px;
overflow:none;
padding:3px
margin:0px;
}
a#leftButton {
position:absolute;
font-size:1px;
line-height:1px;
left:0px;
top:231px;
width:39px;
height:11px;
background: url(left_button.bmp)
}
a#leftButton:link {
background: url(left_button.bmp)
}
a#leftButton:hover, a#leftButton:focus {
background: url(left_button_highlighted.bmp)
}
a#rightButton {
position:absolute;
font-size:1px;
line-height:1px;
left:206px;
top:231px;
width:39px;
height:11px;
background: url(right_button.bmp)
}
a#rightButton:link {
background: url(right_button.bmp)
}
a#rightButton:hover, a#rightButton:focus {
background: url(right_button_highlighted.bmp)
}

initialize() then makes the AJAX requests for the first two images' descriptions. The responses will be handled by the functions "receiveImage1Description()" and "receiveImage2Description()". These functions make use of a global boolean variable called "awaitingImage1And2Descriptions" so that each has a way of knowing whether the other has executed yet, in spite of the fact that there is no telling which HTTP response will arrive first. When both have executed, the awaitingImage boolean described earlier is set to false, since the left and right buttons may then by used.

The code for the initialize(), receiveImage1Description() and receiveImage2Description() functions follows.

function initialize() {
document.getElementById("JavaScriptLabel").style.display = "none";

imageIndex = 0;
highest = 2;
highestFound = false;
numberKnownFromBottomUp = 1;
numberKnownFromTopDown = 1;
currentlyInBottomGroup = true;
images[0] = "1";
images[1] = "2";

var imageToAdd = document.createElement("IMG");
imageToAdd.setAttribute("src", "images/2.jpg");
imageToAdd.setAttribute("name", "images/2.jpg");
imageToAdd.className = "slideshowImage";
document.getElementById("slideshowLink").appendChild(imageToAdd);
document.getElementById("slideshowLink").childNodes[1].style.visibility = "hidden";
document.getElementById("slideshow").style.height = 220;

var imageDescriptionDiv = document.createElement("DIV");
imageDescriptionDiv.id = "imageDescription";
imageDescriptionDiv.innerHTML = "Click to enlarge.";
document.getElementById("slideshow").appendChild(imageDescriptionDiv);
var leftButton = document.createElement("A");
leftButton.id = "leftButton";
leftButton.href = ""; /* Internet Explorer requires that we set a value for href, for the button's :hover subclass to work. */
leftButton.onclick = clickLeftButton;
document.getElementById("slideshow").appendChild(leftButton);
var rightButton = document.createElement("A");
rightButton.id = "rightButton";
rightButton.href = ""; /* Internet Explorer requires that we set a value for href, for the button's :hover subclass to work. */
rightButton.onclick = clickRightButton;
document.getElementById("slideshow").appendChild(rightButton);

// Now we retrieve the descriptions of the first two images:
awaitingImage1And2Descriptions = true;
awaitingImage = true;
var XMLHttp = getRequestObject();
if (XMLHttp==null) {
	imageDescriptions[0] = 'Click to enlarge.';
	document.getElementById("imageDescription").innerHTML = 'Click to enlarge';
	return;
}
// Now we register the handler function:
XMLHttp.onreadystatechange = function() { receiveImage1Description(XMLHttp); }
// Now we open the request:
XMLHttp.open('GET', 'image_descriptions/1.txt', true);
// Now we send it:
XMLHttp.send(null);

var XMLHttp2 = getRequestObject();
if (XMLHttp2==null) {
	imageDescriptions[0] = 'Click to enlarge.';
	return;
}
XMLHttp2.onreadystatechange = function() { receiveImage2Description(XMLHttp2); }
XMLHttp2.open('GET', 'image_descriptions/2.txt', true);
XMLHttp2.send(null);
return;
}

function receiveImage1Description(requestParam) {
	try {
		if (requestParam.readyState == 4) {
			if (requestParam.status == 200) {
				// First we add the response to the imageDescriptions array:
				imageDescriptions[0] = requestParam.responseText + ' Click to enlarge.';
				// Then we set the contents of the imageDescription div to the text of the response:
				document.getElementById("imageDescription").innerHTML = requestParam.responseText + ' Click to enlarge.';
			} else {
				// The image description does not exist. Only the words "Click to enlarge" will appear under the image. We add this string to imageDescriptions and the imageDescription div:
				imageDescriptions[0] = 'Click to enlarge.';
				document.getElementById("imageDescription").innerHTML = 'Click to enlarge.';
			}
		}
	} catch (e) {
		imageDescriptions[0] = 'Click to enlarge.';
		document.getElementById("imageDescription").innerHTML = 'Click to enlarge.';
	}
	if (awaitingImage1And2Descriptions)
		awaitingImage1And2Descriptions = false;
	else
		awaitingImage = false;
}

function receiveImage2Description(requestParam) {
	try {
		if (requestParam.readyState == 4) {
			if (requestParam.status == 200) {
				// We must add the response to the imageDescriptions array:
				imageDescriptions[1] = requestParam.responseText + ' Click to enlarge.';
			} else {
				// The image description does not exist. Only the words "Click to enlarge" will appear under the image. We add this string to imageDescriptions:
				imageDescriptions[1] = 'Click to enlarge.';
			}
		}
	} catch (e) {
		imageDescriptions[1] = 'Click to enlarge.';
	}
	if (awaitingImage1And2Descriptions)
		awaitingImage1And2Descriptions = false;
	else
		awaitingImage = false;
}

Here and at other parts of the slideshow script, there is AJAX which makes use of a function called "getRequestObject()". This function creates the XMLHTTPRequest object. The code for it follows.


function getRequestObject () {
	// First we get an HTTP object:
	var XMLHttp=null;
	try {
		XMLHttp=new ActiveXObject("Msxml2.XMLHTTP");
	}
	catch(e) {
		try {
			XMLHttp=new ActiveXObject("Microsoft.XMLHTTP");
		} catch (e2) {}
	}

	if (XMLHttp==null) {
		XMLHttp=new XMLHttpRequest();
	}
	return XMLHttp;
}

The complete code for the slideshow follows. Also observe an example page containing the slideshow.


var imageIndex;
var images = new Array();
var imageDescriptions = new Array();
var awaitingImage;
var awaitingImage1And2Descriptions;
var highest;
var highestFound;
var numberKnownFromBottomUp;
var numberKnownFromTopDown;
var currentlyInBottomGroup;

function initialize() {
document.getElementById("JavaScriptLabel").style.display = "none";

imageIndex = 0;
highest = 2;
highestFound = false;
numberKnownFromBottomUp = 1;
numberKnownFromTopDown = 1;
currentlyInBottomGroup = true;
images[0] = "1";
images[1] = "2";

var imageToAdd = document.createElement("IMG");
imageToAdd.setAttribute("src", "images/2.jpg");
imageToAdd.setAttribute("name", "images/2.jpg");
imageToAdd.className = "slideshowImage";
document.getElementById("slideshowLink").appendChild(imageToAdd);
document.getElementById("slideshowLink").childNodes[1].style.visibility = "hidden";
document.getElementById("slideshow").style.height = 220;

var imageDescriptionDiv = document.createElement("DIV");
imageDescriptionDiv.id = "imageDescription";
imageDescriptionDiv.innerHTML = "Click to enlarge.";
document.getElementById("slideshow").appendChild(imageDescriptionDiv);
var leftButton = document.createElement("A");
leftButton.id = "leftButton";
leftButton.href = ""; /* Internet Explorer requires that we set a value for href, for the button's :hover subclass to work. */
leftButton.onclick = clickLeftButton;
document.getElementById("slideshow").appendChild(leftButton);
var rightButton = document.createElement("A");
rightButton.id = "rightButton";
rightButton.href = ""; /* Internet Explorer requires that we set a value for href, for the button's :hover subclass to work. */
rightButton.onclick = clickRightButton;
document.getElementById("slideshow").appendChild(rightButton);

// Now we retrieve the descriptions of the first two images:
awaitingImage1And2Descriptions = true;
awaitingImage = true;
var XMLHttp = getRequestObject();
if (XMLHttp==null) {
	imageDescriptions[0] = 'Click to enlarge.';
	document.getElementById("imageDescription").innerHTML = 'Click to enlarge';
	return;
}
// Now we register the handler function:
XMLHttp.onreadystatechange = function() { receiveImage1Description(XMLHttp); }
// Now we open the request:
XMLHttp.open('GET', 'image_descriptions/1.txt', true);
// Now we send it:
XMLHttp.send(null);

var XMLHttp2 = getRequestObject();
if (XMLHttp2==null) {
	imageDescriptions[0] = 'Click to enlarge.';
	return;
}
XMLHttp2.onreadystatechange = function() { receiveImage2Description(XMLHttp2); }
XMLHttp2.open('GET', 'image_descriptions/2.txt', true);
XMLHttp2.send(null);
return;
}

function receiveImage1Description(requestParam) {
	try {
		if (requestParam.readyState == 4) {
			if (requestParam.status == 200) {
				// First we add the response to the imageDescriptions array:
				imageDescriptions[0] = requestParam.responseText + ' Click to enlarge.';
				// Then we set the contents of the imageDescription div to the text of the response:
				document.getElementById("imageDescription").innerHTML = requestParam.responseText + ' Click to enlarge.';
			} else {
				// The image description does not exist. Only the words "Click to enlarge" will appear under the image. We add this string to imageDescriptions and the imageDescription div:
				imageDescriptions[0] = 'Click to enlarge.';
				document.getElementById("imageDescription").innerHTML = 'Click to enlarge.';
			}
		}
	} catch (e) {
		imageDescriptions[0] = 'Click to enlarge.';
		document.getElementById("imageDescription").innerHTML = 'Click to enlarge.';
	}
	if (awaitingImage1And2Descriptions)
		awaitingImage1And2Descriptions = false;
	else
		awaitingImage = false;
}

function receiveImage2Description(requestParam) {
	try {
		if (requestParam.readyState == 4) {
			if (requestParam.status == 200) {
				// We must add the response to the imageDescriptions array:
				imageDescriptions[1] = requestParam.responseText + ' Click to enlarge.';
			} else {
				// The image description does not exist. Only the words "Click to enlarge" will appear under the image. We add this string to imageDescriptions:
				imageDescriptions[1] = 'Click to enlarge.';
			}
		}
	} catch (e) {
		imageDescriptions[1] = 'Click to enlarge.';
	}
	if (awaitingImage1And2Descriptions)
		awaitingImage1And2Descriptions = false;
	else
		awaitingImage = false;
}

function clickLeftButton() {
	if (!awaitingImage) {
		if (highestFound) {
			imageIndex = imageIndex - 1;
			if (imageIndex < 0)
				imageIndex = highest - 1;
			if (imageIndex == (highest - 1))
				document.getElementById("slideshowLink").childNodes[images[0] - 1].style.visibility = "hidden";
			else
				document.getElementById("slideshowLink").childNodes[images[imageIndex + 1] - 1].style.visibility = "hidden";
			document.getElementById("slideshowLink").childNodes[images[imageIndex] - 1].style.visibility = "visible";
			document.getElementById("slideshowLink").href = 'large_images/' + images[imageIndex] + '.jpg';
			document.getElementById("imageDescription").innerHTML = imageDescriptions[images[imageIndex] - 1];
		} else {
			imageIndex = imageIndex - 1;
			if (imageIndex < 0) {
				imageIndex = images.length - 1;
				currentlyInBottomGroup = false;
			}
			if (!currentlyInBottomGroup && ((images.length - imageIndex) > numberKnownFromTopDown)) {
				awaitingImage = true;
				// Now we must make a request for the next image that we haven't pulled in yet:
				// First we get an HTTP object:
				var XMLHttp = getRequestObject();
				if (XMLHttp==null) {
					alert('Next image not available at this time.');
					awaitingImage = false;
					imageIndex = imageIndex + 1;
					if (imageIndex == images.length) {
						imageIndex = 0;
						currentlyInBottomGroup = true;
					}
					return;
				}
				// Now we register the handler function:
				XMLHttp.onreadystatechange = function() { receiveImage(XMLHttp, false); }
				// Now we open the request:
				XMLHttp.open('HEAD', 'images/' + (highest + 1) + '.jpg', true);
				// Now we send it:
				XMLHttp.send(null);
			} else {
				if (imageIndex == (images.length - 1))
					document.getElementById("slideshowLink").childNodes[images[0] - 1].style.visibility = "hidden";
				else
					document.getElementById("slideshowLink").childNodes[images[imageIndex + 1] - 1].style.visibility = "hidden";
				document.getElementById("slideshowLink").childNodes[images[imageIndex] - 1].style.visibility = "visible";
				document.getElementById("slideshowLink").href = 'large_images/' + images[imageIndex] + '.jpg';
				document.getElementById("imageDescription").innerHTML = imageDescriptions[images[imageIndex] - 1];
			}
		}
	}
	return false;
}
function clickRightButton() {
	if (!awaitingImage) {
		if (highestFound) {
			imageIndex = imageIndex + 1;
			if (imageIndex >= highest)
				imageIndex = 0;
			if (imageIndex == 0)
				document.getElementById("slideshowLink").childNodes[images[highest - 1] - 1].style.visibility = "hidden";
			else
				document.getElementById("slideshowLink").childNodes[images[imageIndex - 1] - 1].style.visibility = "hidden";
			document.getElementById("slideshowLink").childNodes[images[imageIndex] - 1].style.visibility = "visible";
			document.getElementById("slideshowLink").href = 'large_images/' + images[imageIndex] + '.jpg';
			document.getElementById("imageDescription").innerHTML = imageDescriptions[images[imageIndex] - 1];
		} else {
			imageIndex = imageIndex + 1;
			if (imageIndex >= images.length) {
				imageIndex = 0;
				currentlyInBottomGroup = true;
			}
			if (currentlyInBottomGroup && (imageIndex >= numberKnownFromBottomUp)) {
				awaitingImage = true;
				// Now we must make a request for the next image that we haven't pulled in yet:
				// First we get an HTTP object:
				var XMLHttp = getRequestObject();
				if (XMLHttp==null) {
					alert('Next image not available at this time.');
					awaitingImage = false;
					imageIndex = imageIndex - 1;
					if (imageIndex <= 0) {
						imageIndex = images.length - 1;
						currentlyInBottomGroup = false;
					}
					return;
				}
				// Now we register the handler function:
				XMLHttp.onreadystatechange = function() { receiveImage(XMLHttp, true); }
				// Now we open the request:
				XMLHttp.open('HEAD', 'images/' + (highest + 1) + '.jpg', true);
				// Now we send it:
				XMLHttp.send(null);
			} else {
				if (imageIndex == 0)
					document.getElementById("slideshowLink").childNodes[images[images.length - 1] - 1].style.visibility = "hidden";
				else
					document.getElementById("slideshowLink").childNodes[images[imageIndex - 1] - 1].style.visibility = "hidden";
				document.getElementById("slideshowLink").childNodes[images[imageIndex] - 1].style.visibility = "visible";
				document.getElementById("slideshowLink").href = 'large_images/' + images[imageIndex] + '.jpg';
				document.getElementById("imageDescription").innerHTML = imageDescriptions[images[imageIndex] - 1];
			}
		}
	}
	return false;
}
function receiveImage(requestParam, calledFromRB) {
	try {
		if (requestParam.readyState == 4) {
			if (requestParam.status == 200) {
				// The image with the imageIndex exists.
				// We want to shift the contents of images right by one from numberKnownFromBottomUp onward.
				for (index = images.length - 1; index >= numberKnownFromBottomUp; index = index - 1)
					images[index + 1] = images[index];
				// Now we put the newly-retrieved image into the space we made:
				images[numberKnownFromBottomUp] = highest + 1;
				// If we were going down from the top, we must move the imageIndex back up one:
				if (!calledFromRB)
					imageIndex = imageIndex + 1;
				//document.getElementById("slideshowLink").write("");
				var imageToAdd = document.createElement("IMG");
				imageToAdd.setAttribute("src", "images/" + images[numberKnownFromBottomUp] + ".jpg");
				imageToAdd.setAttribute("name", "images/" + images[numberKnownFromBottomUp] + ".jpg");
				imageToAdd.className = "slideshowImage";
				document.getElementById("slideshowLink").appendChild(imageToAdd);
				if (calledFromRB)
					document.getElementById("slideshowLink").childNodes[images[numberKnownFromBottomUp - 1] - 1].style.visibility = "hidden";
				else
					document.getElementById("slideshowLink").childNodes[images[numberKnownFromBottomUp + 1] - 1].style.visibility = "hidden";
				document.getElementById("slideshowLink").href = 'large_images/' + images[numberKnownFromBottomUp] + '.jpg';
				highest = highest + 1;
				if (calledFromRB)
					numberKnownFromBottomUp = numberKnownFromBottomUp + 1;
				else
					numberKnownFromTopDown = numberKnownFromTopDown + 1;
				// Now we must make a request for the next image description that we haven't pulled in yet:
				var XMLHttp = getRequestObject();
				if (XMLHttp==null) {
					imageDescriptions[images[imageIndex] - 1] = 'Click to enlarge.';
					document.getElementById("imageDescription").innerHTML = 'Click to enlarge';
					awaitingImage = false;
					return;
				}
				XMLHttp.onreadystatechange = function() { receiveImageDescription(XMLHttp); }
				XMLHttp.open('GET', 'image_descriptions/' + highest + '.txt', true);
				XMLHttp.send(null);
			} else {
				// The image does not exist. This means we've retrieved all images.
				highestFound = true;
				if (calledFromRB) {
					document.getElementById("slideshowLink").childNodes[images[numberKnownFromBottomUp - 1] - 1].style.visibility = "hidden";
					document.getElementById("slideshowLink").childNodes[images[numberKnownFromBottomUp] - 1].style.visibility = "visible";
					//document.slideshowImage.src = 'images/' + images[numberKnownFromBottomUp] + '.jpg';
					document.getElementById("slideshowLink").href = 'large_images/' + images[numberKnownFromBottomUp] + '.jpg';
					document.getElementById("imageDescription").innerHTML = imageDescriptions[images[numberKnownFromBottomUp] - 1];
				} else {
					document.getElementById("slideshowLink").childNodes[images[numberKnownFromBottomUp] - 1].style.visibility = "hidden";
					document.getElementById("slideshowLink").childNodes[images[numberKnownFromBottomUp - 1] - 1].style.visibility = "visible";
					//document.slideshowImage.src = images[numberKnownFromBottomUp - 1] + '.jpg';
					document.getElementById("slideshowLink").href = 'large_images/' + images[numberKnownFromBottomUp - 1] + '.jpg';
					document.getElementById("imageDescription").innerHTML = imageDescriptions[images[numberKnownFromBottomUp - 1] - 1];
				}
				awaitingImage = false;
			}
//			awaitingImage = false;
		}
	} catch (e) {
		alert('An error has occured in this page.');
		document.getElementById("slideshow").style.display = "none";
	}
}
function receiveImageDescription(requestParam) {
	try {
		if (requestParam.readyState == 4) {
			if (requestParam.status == 200) {
				// First we add the response to the imageDescriptions array:
				imageDescriptions[images[imageIndex] - 1] = requestParam.responseText + ' Click to enlarge.';
				// Then we set the contents of the imageDescription div to the text of the response:
				document.getElementById("imageDescription").innerHTML = requestParam.responseText + ' Click to enlarge.';

			} else {
				// The image description does not exist. Only the words "Click to enlarge" will appear under the image. We add this string to imageDescriptions and the imageDescription div:
				imageDescriptions[images[imageIndex] - 1] = 'Click to enlarge.';
				document.getElementById("imageDescription").innerHTML = 'Click to enlarge';
			}
			awaitingImage = false;
		}
	} catch (e) {
		alert('An error has occured in this page.');
		document.getElementById("slideshow").style.display = "none";
	}
}
function getRequestObject () {
	// First we get an HTTP object:
	var XMLHttp=null;
	try {
		XMLHttp=new ActiveXObject("Msxml2.XMLHTTP");
	}
	catch(e) {
		try {
			XMLHttp=new ActiveXObject("Microsoft.XMLHTTP");
		} catch (e2) {}
	}

	if (XMLHttp==null) {
		XMLHttp=new XMLHttpRequest();
	}
	return XMLHttp;
}
window.onload = initialize;