// photography.danwright.info:slideshow.js
// Copyright (c) 2005-2008 Dan Wright
// photography.danwright.info/contact
// Depends on: javascript.js, prototype.js, effects.js


function setNodeText(id,text)
{
	var elem = $(id);
	while (elem.lastChild != elem.firstChild)
		elem.removeChild(elem.lastChild);
	assert(text != null, "setNodeText: text is null! id = " + id);
	var ibr = text.indexOf('<br/>');
	if (ibr != -1)
		{
		var headstr = document.createTextNode(text.substring(0, ibr));
		var tailstring = text.substring(ibr + 5, text.length);
		var tailstr = document.createTextNode(text.substring(ibr + 5, text.length));
		var brk = document.createElement('br');
		elem.replaceChild(headstr, elem.firstChild);
		elem.appendChild(brk);
		elem.appendChild(tailstr);
		}
	else
		{
		var txt = document.createTextNode(text);
		elem.replaceChild(txt, elem.firstChild);
		}
}

var SlideShow = {
	images : null,		// holds an array of records about our photos
	activeSlide : -1,	// index (0-based) of active slide (photo)
	thumbsPerRow : 1,	// number of thumbs on each row	
	thumbsPerPage : 32000, // assume there's only one page
	initialSlide : 0,	// which slide to show first
	preloadingCount : 0, // how many photos are currently being preloaded
	_thumbnails : null,	// array of thumbnail (img) elements
	
	animation : {
		inProgress: false,
		toSlide: -1,
		changeAgain: false,
		newSlide: 0
		},
	
	interval : null,
	
	// init/initExperimental [public] -- initialize slideshow (while document is loading)
	init : function(initialSlide)
			{
			SlideShow.initialSlide = initialSlide || 0;
			},
	
	initExperimental : function(initialSlide)
			{
			SlideShow.init(initialSlide);
			},

	isIE : function()
			{
			return Prototype.Browser.IE;
			},
			
	ieVersion: function() 
			{
			arr = navigator.appVersion.split("MSIE");
			return parseFloat(arr[1]);
			},

	hasPngSupport: function()
			{
			return !Prototype.Browser.IE || SlideShow.ieVersion() >= 8.0;
			},
	
	hasAlphaImageLoader: function()
			{
			var v = SlideShow.ieVersion();
			return Prototype.Browser.IE && v >= 5.5 && v < 8.0;
			},

	hasThumbZoomingSupport: function()
			{
			return !SlideShow.isIE() || SlideShow.ieVersion() >= 7.0;
			},
			
	hasPlayControls: function()
			{
			return !SlideShow.isIE() || SlideShow.ieVersion() >= 7.0;
			},
		
	getCookie: function(name) 
			{
			var re = new RegExp("^ *"+name+"=");
			var a = $A(document.cookie.split(';')).findAll( function(ck) {return re.test(ck);});
			return a.length ? unescape(a[0].substring(a[0].indexOf("=")+1)) : "";
			},

	isLoggedIn: function()
			{
			return SlideShow.getCookie('uid').length > 0;
			},
	
	hasStarRatingSupport: function()
			{
			return SlideShow.isLoggedIn();
			},
			
	createElement: function(tag, attributes, style, children)
			{
			var element = Element.extend(document.createElement(tag));
			if (attributes) SlideShow.setAttributes(element, attributes);
			if (style) element.setStyle(style);
			if (children) $A(children).each( function(child){element.appendChild(child);} );
			return element;
			},
	
	setAttributes: function(element, attributes)
			{
			for (var attribute in attributes)
				element[attribute] = attributes[attribute];
			},
	
	fixPngTransparency: function(img)
			{
			if (!SlideShow.hasPngSupport() && SlideShow.hasAlphaImageLoader())
				{
				img = $(img);
				var src = img.src;
				var width = parseInt(img.getStyle('width')) || img.width || 32;
				var height = parseInt(img.getStyle('height')) || img.height || width;
				var attr = {};
				if (img.id) attr.id = img.id;
				var div = SlideShow.createElement('div', attr,
							{ /* style */
							filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizing='scale')",
							width: width + 'px',
							height: height + 'px'
							});
				img.replaceNode(div);
				}
			},
								
	thumbnails: function()
			{
			if (!SlideShow._thumbnails)
				{
				SlideShow._thumbnails = $$('#thumbnails img');
				if (SlideShow._thumbnails.length == 0)
					{
					SlideShow._thumbnails = null;
					return [];
					}
				}
			return SlideShow._thumbnails;
			},
			
	getThumb : function(i)
			{
			return SlideShow.thumbnails()[i];
			},
	
	getImageCount : function()
			{
			return SlideShow.thumbnails().length;
			},
			
	getPictureFromIndex : function(index)
			{
			return SlideShow.thumbnails()[index].src.replace(/\/thumbs/,'');
			},
	
	getTitleFromIndex : function(index) 
			{
			return SlideShow.thumbnails()[index].title.replace(/,/, ' <br/>');
			},

	getGalleryFromIndex : function(index)
			{
			return SlideShow.thumbnails()[index].alt.replace(/:.*/, '');
			},
	
	getGalleryOfSelectedPicture : function()
			{
			return SlideShow.getGalleryFromIndex(SlideShow.activeSlide);
			},
			
	selectTab : function(whichTab)
			{
			if (SlideShow.thumbsPerRow == 0)
				{
				SlideShow.countThumbsPerRow();
				SlideShow.countThumbsPerPage();
				}
			assert(SlideShow.thumbsPerPage < SlideShow.getImageCount(), "SlideShow.thumbsPerPage (" + SlideShow.thumbsPerPage + ") < SlideShow.getImageCount() (" + SlideShow.getImageCount() + ")");
			if (SlideShow.thumbsPerPage < SlideShow.getImageCount())
				{
				var thumbtabarray = $$('#thumbtabs li');
				var selectedArray = $$('#thumbtabs li.selected');
				var oldSelected, oldGroup;
				oldSelected = selectedArray[0];
				oldGroup =    'group_' + oldSelected.innerHTML;
				if (oldSelected)
					{
					oldSelected.removeClassName('selected');
					$(oldGroup).removeClassName('selected');
					}
				($('tab_'+whichTab) || oldSelected).addClassName('selected');
				($('group_'+whichTab) || $(oldGroup)).addClassName('selected');
				}
			},
			
	revealThumb : function(whichThumb)
			{
			if (SlideShow.thumbsPerRow == 0)
				{
				SlideShow.countThumbsPerRow();
				SlideShow.countThumbsPerPage();
				}
			if (SlideShow.thumbsPerPage < SlideShow.getImageCount())
				SlideShow.selectTab(Math.floor(whichThumb/SlideShow.thumbsPerPage) + 1);
			},
			
 
	moveThumbHilite : function(i)
			{
			if (SlideShow.activeSlide >= 0)
				SlideShow.getThumb(SlideShow.activeSlide).removeClassName('selected');
			SlideShow.activeSlide = i;
			SlideShow.getThumb(i).addClassName('selected');
			},
				
	gotoSlide : function(i)
			{
			if (SlideShow.activeSlide != i && i >= 0 && i < SlideShow.getImageCount())
				{
				SlideShow.requestPhotoInfo(i);
				if (!SlideShow.images[i].preloaded)
					SlideShow.preloadImages(i, 1);
				if (SlideShow.animation.inProgress)
					{
					SlideShow.animation.changeAgain = true;
					SlideShow.animation.newSlide = i;
					}
				else
					SlideShow.startSpotTransition(i);
				}
			},
	
	gotoSlideByName : function(name)
			{
			var imgs = SlideShow.thumbnails();
			if (name.indexOf('.jpg') < 0)
				name += '.jpg';
			var re = new RegExp(name.replace('/\./', '\\.') + '$');
			for (var i = 0, length = imgs.length; i < length; i++)
				{
				if (imgs[i].src.match(re))
					{
					SlideShow.gotoSlide(i);
					break;
					}
				}
			},
			
	setPhotoAndTitle : function(i)
			{
			if (SlideShow.isIE())
				{ /* specifically this is for IE8b2; removeAttribute is IE-only API */
				$('photo').removeAttribute('width');
				$('photo').removeAttribute('height');
				}
			$('photo').src = SlideShow.getPictureFromIndex(i);
			setNodeText('phototitle', SlideShow.getTitleFromIndex(i));
			SlideShow.revealThumb(i);
			SlideShow.setPhotoInfoHTML(i);
			SlideShow.fetchPhotoRating(i);
			},

	getPhotoIdentifier: function(i)
			{
			var gallery = SlideShow.getGalleryFromIndex(i);
			var picture = SlideShow.getPictureFromIndex(i);
			var image = picture.replace(new RegExp('.*\/'), '');
			return gallery + ":" + image;			
			},
		
	getActivePhotoIdentifier: function()
			{
			if (SlideShow.activeSlide >= 0)
				return SlideShow.getPhotoIdentifier(SlideShow.activeSlide);
			return "";
			},
			
	fetchPhotoRating : function(i)
			{
			if (typeof Rating != 'undefined')
				Rating.prepare(SlideShow.getPhotoIdentifier(i));
			},
			
	fadeIn : function()
			{
			SlideShow.setPhotoAndTitle(SlideShow.animation.toSlide);
			setTimeout("SlideShow.autoPositionPhotoInfo()", 150);
			
			new Effect.Appear('spotlight', {duration: 0.7,
								afterFinish: function() {
										SlideShow.animation.inProgress = false;
										if (SlideShow.animation.changeAgain)
											SlideShow.startSpotTransition(SlideShow.animation.newSlide);
										else
											$('spotlight').setOpacity(1.0);
										}
									});
			},
			
	showNewImageOrProgressIndicator : function()
			{
			var progress = $('progress');
			try {
				if (SlideShow.images[SlideShow.animation.toSlide].loaded 
					|| (SlideShow.images[SlideShow.animation.toSlide].img != null
					    && SlideShow.images[SlideShow.animation.toSlide].img.complete))
					{
					progress.style.display = 'none';
					SlideShow.fadeIn();
					}
				else
					{
					progress.style.position = 'absolute';
					progress.style.left = '55%';
					progress.style.top  = '30%';
					progress.style.display = 'block';
					$('spotlight').setOpacity(1.0);
					}
				}
			catch (e)
				{
				alert("error 1 - " + e);
				progress.style.display = 'none';
				SlideShow.fadeIn();
				}				
			},
			
	startSpotTransition : function(i)
			{
			var isFirstSlide = (SlideShow.activeSlide < 0);
			SlideShow.moveThumbHilite(i);
			SlideShow.animation.inProgress = true;
			SlideShow.animation.changeAgain = false;
			SlideShow.animation.toSlide = i;
			if (isFirstSlide)
				{
				$('spotlight').setOpacity(0);
				SlideShow.showNewImageOrProgressIndicator();
				}
			else 
				new Effect.Fade('spotlight', {duration: 0.3, afterFinish: SlideShow.showNewImageOrProgressIndicator});
			},
			
	nextSlide : function()
			{
			SlideShow.gotoSlide((SlideShow.activeSlide + 1) % SlideShow.getImageCount());
			},
	
	nextRow : function()
			{
			var newSlide = SlideShow.activeSlide + SlideShow.thumbsPerRow;
			if (Math.floor(SlideShow.activeSlide / SlideShow.thumbsPerPage) < Math.floor(newSlide / SlideShow.thumbsPerPage))
				newSlide = newSlide - SlideShow.thumbsPerPage;
			else if (newSlide >= SlideShow.getImageCount())
				newSlide = Math.floor(SlideShow.activeSlide / SlideShow.thumbsPerPage) * SlideShow.thumbsPerPage + (SlideShow.activeSlide % SlideShow.thumbsPerRow);
			SlideShow.gotoSlide(newSlide);
			},
			
	backSlide : function()
			{
			if (SlideShow.activeSlide > 0)
				SlideShow.gotoSlide(SlideShow.activeSlide - 1);
			},
			
	backRow : function()
			{
			var newSlide = SlideShow.activeSlide - SlideShow.thumbsPerRow;
			if (SlideShow.activeSlide % SlideShow.thumbsPerPage < SlideShow.thumbsPerRow)
				{
				newSlide = newSlide + SlideShow.thumbsPerPage;
				if (newSlide > SlideShow.getImageCount())
					{
					var thumbsInLastRow = SlideShow.getImageCount() % SlideShow.thumbsPerRow;
					newSlide = SlideShow.getImageCount() - thumbsInLastRow + SlideShow.activeSlide % SlideShow.thumbsPerRow;
					if (SlideShow.activeSlide % SlideShow.thumbsPerPage >= thumbsInLastRow)
						newSlide -= SlideShow.thumbsPerRow;
					}
				}
			SlideShow.gotoSlide(newSlide);
			},
			
	advanceSlideShow : function()
			{
			var next = SlideShow.activeSlide + 1;
			if (next < SlideShow.getImageCount() && SlideShow.images[next].preloaded && !SlideShow.images[next].loaded && !SlideShow.images[next].img.complete)
				{
				return; // just wait a bit longer
				}
			SlideShow.nextSlide();
			if (SlideShow.activeSlide == SlideShow.getImageCount() - 1)
				SlideShow.stop();
			else
				SlideShow.preloadImages(SlideShow.activeSlide + 1, 2);
			},
	
	setPhotoInfoHTML : function(i)
		{
		if (null == i)
			i = SlideShow.activeSlide;
		var info = $('photoinfo');
		if (SlideShow.images[i].info != null)
			{
			info.innerHTML = "<div id='photoInfoDetails' class='photoInfoDetails'>" + SlideShow.images[i].info + "</div>";
			}
		else
			{
			info.innerHTML = '<div class="progress">Fetching info... <img src="/images/indicator_black.gif" alt="(...)" /></div>';
			}
		},
		
	showSlideOverlays : function()
		{
		$('infoicon','rating','controls').each( function (el) {
							el.style.visibility = 'visible'; } );
		},

	hideSlideOverlays : function()
		{
		$('infoicon','rating','controls').each( function (el) {
							el.style.visibility = 'hidden'; } );
		},
		
	showPhotoInfo : function()
		{
		SlideShow.setPhotoInfoHTML(SlideShow.activeSlide);
		var info = $('photoinfo');
		info.style.display = 'block';
		SlideShow.autoPositionPhotoInfo();
		info.style.visibility = 'visible';
		},
		
	hidePhotoInfo : function()
		{
		$('photoinfo').hide();
		},
		
	autoPositionPhotoInfo : function()
		{
		var photoElem = $('photo');
		var info      = $('photoinfo');
		var infoIcon  = $('infoicon');

		var photoPos = Position.cumulativeOffset(photoElem);
		var imageWidth = photoElem.offsetWidth || 0;
		var photoCenter = getGlobalCenterOfElement(photoElem);
		var photoTopLeft = getGlobalPositionOfElement(photoElem);
		
		if (info && infoIcon)
			{
			setGlobalLeft(infoIcon, photoTopLeft.x + 20);
			setGlobalTop(infoIcon, photoTopLeft.y + 20);
			infoIcon.style.zIndex = 89;

			var infoIconHeight = infoIcon.offsetHeight || 32;
			setGlobalTop(info, photoTopLeft.y + 20 + infoIconHeight + 5);
			setGlobalLeft(info, photoTopLeft.x + 20);
			}
		
		var divRating = $('rating');
		if (divRating)
			{
			divRating.style.position = 'absolute';
			var stars = divRating.getElementsByTagName('a');
			var starsWidth = stars[0].getWidth()*5;
			var ratingWidth = Math.max(divRating.getWidth(), 100, starsWidth + 10);
			if (divRating.getWidth() < starsWidth)
				divRating.style.width = starsWidth + 'px';
			divRating.SMOO_starsWidth = starsWidth;
			divRating.SMOO_ratingWidth = ratingWidth;
			setGlobalLeft(divRating, photoTopLeft.x + imageWidth - ratingWidth - 20);
			setGlobalTop(divRating, photoTopLeft.y + 20);
			}
			
		var divControls = $('controls');
		if (divControls)
			{
			divControls.style.position = 'absolute';
			var buttons = divControls.getElementsByTagName('a');
			if (buttons.length > 2)
				{
				var buttonsWidth = buttons[0].getWidth() + buttons[1].getWidth() + buttons[2].getWidth();
				var controlsWidth = Math.max(divControls.getWidth(), buttonsWidth, 60);
				if (divControls.getWidth() < buttonsWidth)
					divControls.style.width = buttonsWidth + 'px';
				setGlobalLeft(divControls, photoTopLeft.x + infoIcon.offsetWidth + 20);
				setGlobalTop(divControls,  photoTopLeft.y + infoIcon.offsetHeight/2 + 10);
				}
			}
			
		return; /* might still need code below for IE; trying out new css for photolabel */
		var label = $('photolabel');
		if (label)
			{
			label.style.position = 'absolute';
			label.style.minWidth = '300px';
			if (label.offsetWidth < 300)
				label.style.width = '300px';
			setGlobalLeft(label, photoCenter.x - (label.offsetWidth/2));
			label.style.visibility = 'visible';
			}
		},
			
	requestPhotoInfo : function(i)
			{
			if (SlideShow.images[i].info == null)
				{
				var gallery = SlideShow.getGalleryFromIndex(i);
				var picture = SlideShow.getPictureFromIndex(i);
				var image = picture.replace(new RegExp('.*\/'), '');
				var url = "/php/photoinfo.php/" + gallery + "/" + image;
				var myAjax = new Ajax.Request(url, { method: 'get', onComplete: function (request) { SlideShow.receivePhotoInfoResponse(request, i);} } );
				assert(myAjax != null, "could not create Ajax request");
				if (myAjax != null && SlideShow.activeSlide == i)
					$('photoinfo').innerHTML = '<div class="progress">Fetching info... <img src="/images/indicator_black.gif" alt="(...)" /></div>';
				}
			else if (SlideShow.activeSlide == i)
				$('photoinfo').innerHTML = "<div id='photoInfoDetails' class='photoInfoDetails'>" + SlideShow.images[i].info + "</div>";
			},

	wrapEXIF : function(tagLabel, tagValue)
			{
			return (null == tagValue) ? '' : '<div class="tagLabel">' + tagLabel + ':</div><div class="tagValue">' + tagValue + '</div>';
			},
	
			
	receivePhotoInfoResponse : function(request, i)
			{
			try {
			photoInfo = new Array();
			var docXML = request.responseXML;
			assert(docXML != null, "request.responseXML is undefined");
			var photodetails = docXML.getElementsByTagName('photoDetails')[0];
			assert(photodetails != null, "request.responseXML.photoDetails is undefined");
			if (null == photodetails)
				return;
			var children = photodetails.childNodes;
			assert(children != null && children.length > 0, "request.responseXML.photoDetails missing children");

			for (ichild = 0; ichild < children.length; ichild++)
				{
				var node = children[ichild];
				if (node.nodeType == 1 /* Node.ELEMENT_NODE */)
					photoInfo[node.tagName] = node.firstChild.data;
				}

			var newHTML = "";
			
			if (photoInfo['lens'] != null && photoInfo['focal-length'] != null)
				photoInfo['lens'] += ' (' + photoInfo['focal-length'] + ')';
			if (photoInfo['lens'] == null && photoInfo['focal-length'] != null)
				photoInfo['lens'] = photoInfo['focal-length'];
				
			if (photoInfo['tags'] != null)
				photoInfo['tags'] = '<i>' + photoInfo['tags'] + '</i>';
			var tagLabels = ['Title', 'Location', 'Photo No.', 'Date', 'Camera', 'Lens', 'Exposure', 'Tags'];
			['title', 'location', 'FileNumber', 'date', 'camera', 'lens', 'exposure', 'tags'].each(function (tag, index) {
				newHTML += SlideShow.wrapEXIF(tagLabels[index], photoInfo[tag]);
				});
			
			if (newHTML == "")
				newHTML = "(no information available)";

			SlideShow.images[i].info = newHTML;
			if (SlideShow.activeSlide == i)
				$('photoinfo').innerHTML = "<div id='photoInfoDetails' class='photoInfoDetails'>" + newHTML + "</div>";
			}
			catch (e)
				{
				alert("exception occurred handling XML: " + e);
				}
			},
				
	clickOnSlide : function()
			{
			if (SlideShow.interval)
				SlideShow.stop();
			else
				SlideShow.nextSlide();
			return false;
			},
	
	clickThumb : function(element)
			{
			SlideShow.stop();
			SlideShow.gotoSlide(element.thumbindex);
			},
	
	
	// ~=~=~=~ start/stop/toggle the slide show ~=~=~=~
	start : function()
			{
			SlideShow.interval = setInterval("SlideShow.advanceSlideShow()", 4500);
			SlideShow.preloadImages(SlideShow.activeSlide + 1, 2);
			if (SlideShow.hasPlayControls())
				$('playButton').addClassName('playing');
			},
	
	stop : function()
			{
			if (SlideShow.interval)
				clearInterval(SlideShow.interval);
			SlideShow.interval = null;
			if (SlideShow.hasPlayControls())
				$('playButton').removeClassName('playing');
			},
	
	toggle : function()
			{
			if (SlideShow.interval)
				SlideShow.stop();
			else
				SlideShow.start();
			return false;
			},
	
	// ~=~=~=~
	
	preloadImages : function(start, count)
			{
			for (var i = start, imageCount = SlideShow.getImageCount(); i < imageCount && i < start + count; i++)
				{
				if (!SlideShow.images[i].preloaded && !SlideShow.images[i].loaded)
					{
					var img = new Image();
					img.SlideShowIndex = i;
					SlideShow.preloadingCount++;
					Event.observe(img, 'load', SlideShow.photoLoaded);
					img.src = SlideShow.getPictureFromIndex(i);
					SlideShow.images[i].img = img;
					SlideShow.images[i].preloaded = true;
					}
				}
			},
	
	// photoLoaded -- 'onload' event handlers for images. We make a note that the image was loaded, and
	//					preload the next unloaded image, if any.	
	photoLoaded : function(event)
			{
			event = Event.extend(event || window.event);
			var img = this || event.element();
			var index = (img.id == 'photo') ? -1 : img.SlideShowIndex;
			var i = (-1 == index) ? SlideShow.animation.toSlide : index;
			if (index >= 0 && SlideShow.preloadingCount > 0)
				SlideShow.preloadingCount--;
			if (i >= 0 && i < SlideShow.getImageCount())
				{
				if (SlideShow.images[i].preloaded && !SlideShow.images[i].loaded && i == index)
					{
					SlideShow.images[i].loaded = true;
					if (SlideShow.images[i].img.src == $('photo').src || i == SlideShow.animation.toSlide)
						{ // current (active) image has been loaded
						SlideShow.autoPositionPhotoInfo();
						SlideShow.showNewImageOrProgressIndicator();
						}
					if (SlideShow.preloadingCount < 4)
						for (; i < SlideShow.images.length; i++)
							{ // look for next image to preload
							if (!SlideShow.images[i].preloaded && !SlideShow.images[i].loaded)
								{
								SlideShow.preloadImages(i, 1);
								break;
								}
							}
					}
				}
			},
						
	handleKeyPress : function(event)
			{
			if (SlideShow.thumbsPerRow == 0)
				{
				SlideShow.countThumbsPerRow(); // try again now
				SlideShow.countThumbsPerPage();
				}
			var charCode = (event.charCode) ? event.charCode : ((event.which) ? event.which : event.keyCode);
			switch (charCode)
				{
				case Event.KEY_RIGHT:
				case 63235: // Safari 2.0 on Mac OS X 10.4, MacBook Pro
					SlideShow.nextSlide();
					break;
				case Event.KEY_LEFT:
				case 63234:  // Safari 2.0 on Mac OS X 10.4, MacBook Pro
					SlideShow.backSlide();
					break;
				case Event.KEY_ESC:
				case 46: // '.'
				case 3:  // ctrl-C
					SlideShow.stop();
					break;
				case 32:
					SlideShow.toggle();
					break;
				case 112: // p
				case 80:  // P
					SlideShow.start();
					break;	
				case Event.KEY_DOWN:
				case 63233:
					SlideShow.nextRow();
					break;
				case Event.KEY_UP:
				case 63232:
					SlideShow.backRow();
					break;
				
				case 49: // 1..5
				case 50:
				case 51:
				case 52:
				case 53:
					SlideShow.selectTab(charCode - 48);
					break;
					
				default:
					return true;
				}
			return false;
			},

	countThumbsPerPage : function()
			{
			var imgs = SlideShow.thumbnails();
			SlideShow.thumbsPerPage = 32000;
			assert(SlideShow.thumbsPerRow > 1, "countThumbsPerPage: SlideShow.thumbsPerRow == " + SlideShow.thumbsPerRow);
			if (SlideShow.thumbsPerRow > 1)
				{
				var firstTop = Position.cumulativeOffset(imgs[0])[1];
				for (var i = SlideShow.thumbsPerRow, imageCount = imgs.length; i < imageCount; i += SlideShow.thumbsPerRow)
					{
					if (Position.cumulativeOffset(imgs[i])[1] <= firstTop)
						{
						SlideShow.thumbsPerPage = i;
						return;
						}
					}
				}
			},
			
	countThumbsPerRow : function()
			{
			var imgs = SlideShow.thumbnails();
			SlideShow.thumbsPerRow = 1; // safe value
			if (imgs.length > 1)
				{
				var firstImage = imgs[0];
				var firstTop = Position.cumulativeOffset(firstImage);
				var i;
				if (firstTop[1] == 0)
					SlideShow.thumbsPerRow = 0;
				var limit = (imgs.length > 6) ? 6 : imgs.length;
				for (i = 1, imageCount = imgs.length; i < imageCount; i++)
					{
					var thisTop = Position.cumulativeOffset(imgs[i]);
					if (thisTop[1] > firstTop[1])
						{
						SlideShow.thumbsPerRow = i;
						return;
						}
					}
				if (imgs.length > 5) // WinIE 6 has this problem
					SlideShow.thumbsPerRow = 0; // try again later
				}
			},
	
	createSpotlight : function()
			{
		/*	<div id="spotlight" class="loading">
			<div class="photowrapper"><img src="" alt="" id="photo" /></div><br />
			<div id="photolabel">
			<p id="phototitle">&nbsp;</p>
			</div>
			</div> */
			/*var phototitle = SlideShow.createElement('p', {id:'phototitle'}, null, [document.createTextNode(' ')]);
			var photolabel = SlideShow.createElement('div', {id:'photolabel'}, null, [phototitle]);
			var imgPhoto   = SlideShow.createElement('img', {src:'', alt:'', id:'photo'});
			var photowrapper = SlideShow.createElement('div', {className:'photowrapper'}, null, [imgPhoto]);
			
			var spotlight = SlideShow.createElement('div', {id:'spotlight', className:'loading'}, null,
								[photowrapper, SlideShow.createElement('br'), photolabel]);
			$('thumbnails').parentNode.appendChild(spotlight); */
			
			// ====
			var spotlight = SlideShow.createElement('div', {id:'spotlight', className:'loading'});
			spotlight.innerHTML = '<div class="photowrapper"><img src="" alt="" id="photo" /></div><br />' +
								  '<div id="photolabel"><p id="phototitle">&nbsp;</p></div>';
			$('thumbnails').parentNode.appendChild(spotlight);
			},
			
	createPhotoInfoDiv : function()
			{
			var img = SlideShow.createElement('img', {src:'/images/indicator_black.gif', alt:'(...)'});
			var div = SlideShow.createElement('div', {id:'photoinfo'}, null, [img]);
			$('spotlight').appendChild(div);
			},
			
	createInfoControl : function()
			{
			var img = SlideShow.createElement('img', {id:'infoicon', src:'/images/infoicon-embossed.png', height: 16, width: 16, alt:'Info'});
			$('spotlight').appendChild(img);
			img.observe('mouseover', SlideShow.showPhotoInfo);
			img.observe('mouseout', SlideShow.hidePhotoInfo);
			SlideShow.fixPngTransparency(img);
			},
			
	createLoadingIndicator : function()
			{
		//<div id="progress" style="display: none; color: #ee3333;">Loading photograph... <img src="/images/indicator_black.gif" alt="(...)" /></div>
			var img = SlideShow.createElement('img', {src:'/images/indicator_black.gif',alt:'(...)'});
			var text = document.createTextNode('Loading photograph... ');
			var div = SlideShow.createElement('div', {id:'progress'}, {display:'none'}, [text, img]);
			$('spotlight').appendChild(div);
			SlideShow.fixPngTransparency(img);
			},
			
	createRatingControl : function()
			{
			var ratingDiv = SlideShow.createElement('div', {id:'rating'}, {visibility:'hidden'}, 
							['Fair','Good','Very Good','Excellent','Superb'].map(function (label,i) {
								return SlideShow.createElement('a', {id:'_'+(i+1), title:label}); }));
			if (!SlideShow.hasStarRatingSupport())
				ratingDiv.style.display = 'none';
			$('spotlight').appendChild(ratingDiv);
			},
			
	createPlayerControls : function()
			{
			var controls = SlideShow.hasPlayControls() ? [{id:'backButton',title:'Previous Photo'},
									 {id:'playButton',title:'Play/Pause'},
									 {id:'nextButton',title:'Next Photo'}].map(function (attr) {
									 return SlideShow.createElement('a', attr); })
									 : null;
			var controlsDiv = SlideShow.createElement('div', {id:'controls'}, null, controls);
			$('spotlight').appendChild(controlsDiv);
			if (SlideShow.hasPlayControls())
				{
				$('playButton').observe('click', SlideShow.toggle);
				$('nextButton').observe('click', SlideShow.nextSlide);
				$('backButton').observe('click', SlideShow.backSlide);
				}
			},
	
	// 'onload' handler for the page. Installs event handlers for thumbnails and the main photo.
	domLoaded : function()
			{
			var thumbs = SlideShow.thumbnails();
			SlideShow.images = new Array(thumbs.length);
			var emptyRecord = {loaded: false, preloaded: false, img: null, info: null};
			thumbs.each(function(thumb, i) { 
							SlideShow.images[i] = emptyRecord;
							thumb.thumbindex = i;
							thumb.observe('click', function() { SlideShow.clickThumb(this); return false; });
							thumb.observe('dblclick', function() { SlideShow.clickThumb(this); SlideShow.toggle(); return false; });
							if (SlideShow.hasThumbZoomingSupport())
								{
								var matches = thumb.alt.match(/(\d+)x(\d+)/);
								if (!matches)
									alert("thumb dimensions not found - alt=" + thumb.alt);
								thumb.observe('mouseover', function() { Thumbs.magnify(this, matches[1], matches[2]); });
								thumb.observe('mouseout', function() { Thumbs.reduce(this); });
								}
							});
			
			// Safari & Firefox pass arrow keys to 'onkeypress' handler, but WinIE 6 does not!
			// Safari & Firefox pass keys to window.onkeypress handler, but WinIE 6 does not!
			// However, WinIE 6 does pass all keys to document.body.onkeydown, so there we go.
			// March 2008: Safari 3.1 now behaves the same as Win IE!
			if (isAppleWebKit() || SlideShow.isIE())
				Event.observe(document.body, 'keydown', SlideShow.handleKeyPress);
			else	
				Event.observe(window, 'keydown', SlideShow.handleKeyPress);
				
			SlideShow.countThumbsPerRow();

			SlideShow.createSpotlight();
			SlideShow.createPhotoInfoDiv();
			SlideShow.createInfoControl();
			SlideShow.createLoadingIndicator();
			SlideShow.createRatingControl();
			SlideShow.createPlayerControls();
			
			var elemPhoto = $('photo');
			if (elemPhoto != null)
				{
				elemPhoto.observe('click', SlideShow.clickOnSlide);
				elemPhoto.observe('dblclick', SlideShow.toggle);
				elemPhoto.observe('load', SlideShow.photoLoaded);
				}
				
			$('group_1').addClassName('selected');
			if ($('tab_1') != null)
				{
				$('tab_1').addClassName('selected');
				var thumbtabarray = $$('#thumbtabs li');
				var i = 0;
				thumbtabarray.each( function (elem) { 
									var num = parseInt(elem.innerHTML); /* was innerText, didn't work on FireFox */
									assert(num >= 1 && num <= 5, "num (" + elem.innerText + ") is missing or out of range");
									if (num < 1)
										num = ++i;
									elem.observe('click', function() { SlideShow.selectTab(num); return true; });
									} );
				}
			
			$('spotlight').observe('mouseover', SlideShow.showSlideOverlays);
			$('spotlight').observe('mouseout',  SlideShow.hideSlideOverlays);
			SlideShow.hideSlideOverlays();
			
			$('spotlight').setOpacity(0.0);
			$('spotlight').removeClassName('loading');
			SlideShow.preloadImages(0, 3);
			
			if (SlideShow.getImageCount() > SlideShow.initialSlide)
				SlideShow.gotoSlide(SlideShow.initialSlide);
			}
	};

document.observe("dom:loaded", SlideShow.domLoaded);


