/*--------------------------------------------------------------------------
 *                      Copyright (c) 2007-2009 Mikael Relbe
 *
 *                This code may not be used for any other purposes
 *                than viewing photos on the site www.relbe.se
 *
 *--------------------------------------------------------------------------



/*--------------------------------------------------------------------------
 * Declarations of gallery information, all set by function calls created
 * by the BreezeBrowser Photo Gallery template (written in conjunction
 * with this code).
 *
 * A gallery contains all information, which includes all photos. Each photo
 * contains camera, exposure, image processing and file information.
 *--------------------------------------------------------------------------
 */

var gallery; // The gallery object used by BB Template


function Gallery(title, description, maxMainWidth, maxMainHeight,
				 maxThumbWidth, maxThumbHeight)
{
	this.title = str2html(title);
	this.maxMainWidth = maxMainWidth;
	this.maxMainHeight = maxMainHeight;
	this.maxMainBorderWidth = 12; // See .image in style.css
	this.maxThumbWidth = maxThumbWidth;
	this.maxThumbHeight = maxThumbHeight;
	this.thumbFramePadding = 6; // nbr of pixels between thumbnail frames, see .thumbTable td in CSS
	this.thumbFrameThickness = 0;
	this.description = 	str2html(description);
	this.photographer = '';
	this.photo = new Array();

	this.currentPhoto = 0;      // The index of the currently viewed photo
	this.currentThumbPage = 1;  // and thumbnail page (-1 means "show all")

	this.presentationState = 0; // 0 = nothing
								// 1 = thumbpage
								// 2 = photo

	this.isPlayingSlideshow = false;
	this.slideshowTimerId = 0;
	this.slideshowDelay = 5000; // ms

	this.navbarHeight = 24;

	this.thumbnailFrame =
		new ThumbnailFrame(thumbColor(), maxThumbWidth, maxThumbHeight,
						   this.thumbFrameThickness);

	// Number of thumbnail rows and columns, based on available client size
	this.nThumbRows = Math.max(2, Math.floor((document.documentElement.clientHeight-100) / (this.thumbnailFrame.frameWidth + 2*this.thumbFramePadding)));
	this.nThumbCols = Math.max(3, Math.floor((document.documentElement.clientWidth-(144+24)) / (this.thumbnailFrame.frameHeight + 2*this.thumbFramePadding)));

	function thumbColor() {
		// Thumbnail color randomized by the current second-part of time
/*
		var thumbColorArr = new Array ('blue', 'silver', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default');
		var today = new Date();
		var seconds = today.getSeconds();
		return thumbColorArr[seconds % thumbColorArr.length];
*/
		return 'default';
	}

	this.addPhoto = function(photo) {
		// Called by BreezeBrowser Template for each photo
		this.photo[this.photo.length] = photo;
	};


	this.show = function() {
		// Called by BreezeBrowser Template, presents the gallery when all
		// photos have been added.
		this.writeStaticContent();

		// Check the referenced URL whether a specific thumbnail page or photo
		// shall be presented, or the first thumbnail page (which is the
		// default)
		var url = new String(window.location);

		// If no ?-sign, present default page
		var i = url.indexOf("?");
		if (i == 0) {
			this.showThumbPage(1);
			return;
		}

		// Check thumbnail page number query
		i = url.indexOf("?page=");
		if (i > 0) {
			// The url contains a thumbpage number, get it
			var numPages = this.nThumbPages;
			var wantedPage = url.substring(i + 6, url.length);
			if (wantedPage < 1) wantedPage = 1;
			if (wantedPage > numPages) wantedPage = numPages;
			this.showThumbPage(wantedPage);
			return;
		}

		// Check for photo query
		i = url.indexOf("?photo=");
		if (i > 0) {
			var photoUrl = encodeURI(url.substring(i + 7, url.length));

			// Check if the query contains the URL or filename of a photo
			for (var n = 0; n < this.photo.length; n++) {
				if (photoUrl == this.photo[n].mainUrl ||
					photoUrl == this.photo[n].thumbUrl ||
				    photoUrl == this.photo[n].filenameAsUrl) {
					// Found it, present the photo
					this.showPhotoPage(n);
					return;
				}
			}
		}

		// If we end up here, no valid info was stated after the ?-sign
		this.showThumbPage(1);
	};


	this.writeStaticContent = function() {
		// Adds static sections to the web page in which the thumbs
		// and photos are dynamically presented
		var myHtml = '';

		// Main content (either photo or thumbs)
		myHtml += '<div id="content">';
		myHtml += '</div>';

		// Info
		myHtml += '<div id="info">';
		myHtml += '</div>';

		document.write (myHtml);
	};


	this.showThumbPage = function(pageNum)
	{
		var showAll = (pageNum == -1);

		if (pageNum < 1) pageNum = 1;

		var numImages = this.photo.length + 1;
		var imagesPerPage = this.nThumbRows * this.nThumbCols;
		var numPages = this.nThumbPages();

		if (pageNum > numPages) pageNum = numPages;

		// Work out start and end images for this page
		var startImage = (pageNum - 1) * imagesPerPage;
		var endImage = startImage + imagesPerPage;
		if (startImage < 0  || showAll) startImage = 0;
		if (endImage > this.photo.length || showAll) endImage = this.photo.length;

		this.currentPhoto = startImage;

		function create (gallery, contentElem)
		{
			// Creates the static content of a thumbnail page.
			// contentElem = the page content element object

			var thumbDivHeight = gallery.nThumbRows *
								(gallery.thumbnailFrame.frameHeight +
								 2*gallery.thumbFramePadding);
			var thumbDivWidth = gallery.nThumbCols *
								(gallery.thumbnailFrame.frameWidth +
								2*gallery.thumbFramePadding);

			var titleDivHeight = (document.documentElement.clientHeight -
								 thumbDivHeight) / 2 - gallery.navbarHeight;

			var titleDivStyle = '';

			if (titleDivHeight > 0)
				titleDivStyle = 'height: ' + titleDivHeight + 'px;';

			var myHtml = new String();

			//
			// Title
			//
			myHtml += '<div class="gallerytitle" style="' + titleDivStyle + '">' + gallery.title + '</div>';

			//
			// Main Content (navbar + thumbs)
			//

			myHtml += '<div style="text-align:center">';

			//
			// Navigation bar
			//
			myHtml += '<div style="width:' + (thumbDivWidth-24) + 'px;height:' + gallery.navbarHeight + 'px">';

			// 1 Left part, slideshow
			myHtml += '<p class="navbar" style="float:left;width:15%;text-align:left">';
			myHtml += '<a title="Starta bildspel" href="javascript:gallery.btnSlideshow();">Start</a>';
			myHtml += '</p>';

			// 2 Right part, empty
			myHtml += '<p class="navbar" style="float:right;width:15%;text-align:right"></p>';

			// 3 Middle part nav buttons
			myHtml += '<p id="navbar" class="navbar" style="text-align:center"></p>';

			myHtml += '</div>';

			//
			// Thumbnails
			//
			if (showAll)
				myHtml += '<div id="thumbnails" style="width:' + thumbDivWidth + 'px"></div>';
			else
				myHtml += '<div id="thumbnails" style="width:' + thumbDivWidth + 'px;height:' + thumbDivHeight + 'px"></div>';

			//
			// Gallery description
			//
			if (gallery.description.length > 100)
				myHtml += '<div class="longDescription"><p>' + gallery.description + '</p></div>';
			else
				myHtml += '<div class="shortDescription"><p>' + gallery.description + '</p></div>';

			myHtml += '</div>';

			//
			// Write the update into the container element
			//
			contentElem.innerHTML = myHtml;
		}


		function update(gallery) {
			// Update the content of a thumbnail page.

			function updateNavbar() {
				// Update middle part buttons
				var navbarElem = document.getElementById("navbar");
				var myHtml = new String();

				if (numPages == 1)
					myHtml += '&nbsp;'; // empty bar, only one page
				else {
					// Previous page
					if (!showAll && pageNum > 1)
						myHtml += '<a class="wide" title="Visa föregående sida"  href="javascript:gallery.btnThumbPageShowPage(' + (pageNum - 1) + ');">&laquo;</a>';
					else
						myHtml += '<span class="inactiveLink wide">&laquo;</span>';

//					myHtml += '<span class="spacer"></span>'; // spacer

					// Pages
					for (var i = 1; i <= numPages; i++) {
						if (!showAll && i == pageNum)
							myHtml += '<span class="selected">' + i + '</span>';
						else
							myHtml += '<a href="javascript:gallery.btnThumbPageShowPage(' + i + ');">' + i + '</a>';
					}

//					myHtml += '<span class="spacer"></span>'; // spacer

					// Show all
					if (showAll)
						myHtml += '<span class="wide selected">&bull;</span>';
					else
						myHtml += '<a class="wide" title="Visa alla på en sida" href="javascript:gallery.btnThumbPageShowPage(-1);">&bull;</a>';

//					myHtml += '<span class="spacer"></span>'; // spacer

					// Next page
					if (!showAll && pageNum < numPages)
						myHtml += '<a class="wide" title="Visa nästa sida" href="javascript:gallery.btnThumbPageShowPage(' + (pageNum + 1) + ');">&raquo;</a>';
					else
						myHtml += '<span class="wide inactiveLink">&raquo;</span>';
				}

				navbarElem.innerHTML = myHtml;
			}

			function updateThumbnails() {
				var thumbnailsElem = document.getElementById("thumbnails");
				var myHtml = new String();

				// The thumbs are places in a table on its own
				myHtml += '<table class="thumbTable"><tr>';

				for (var i = startImage; i < endImage; i++) {
					if (i % gallery.nThumbCols == 0) myHtml += '</tr><tr>';
					myHtml += '<td id="thumb' + i + '">' +
							  gallery.thumbnailFrame.frameHtml (gallery.photo[i], i) +
							  '</td>';
				}
				myHtml += '</tr></table>';

				thumbnailsElem.innerHTML = myHtml;
			}

			updateNavbar();
			updateThumbnails();
		}


		/* -------------------------------------------------------------------
		 *	this.showThumbPage
		 */
		var contentElem = document.getElementById("content");
		var infoElem = document.getElementById("info");
		var thumbnailsElem = document.getElementById("thumbnails");

		// Apply filter to either whole page or part of it
		if (this.presentationState != 1) {
			// Thumbnails are currently not presented
			create(this, contentElem);
			// Hide parts on the page that are only shown on photo pages
			infoElem.style.display = 'none';
		}
		else {
			// Thumbnails are currently presented, just freeze the filter
		}

		// Update with a new set of thumbnails
		if (showAll) {
			// Show all thumbs
			this.currentThumbPage = -1;
			create(this, contentElem);
		}
		else {
			this.currentThumbPage = this.normalizeThumbPageNumber(pageNum);

			// Preload thumbs for current page
			this.preloadThumbs(this.nThumbRows, this.nThumbCols,
							   this.currentThumbPage);

			// and next page
			this.preloadThumbs(this.nThumbRows, this.nThumbCols,
							   this.currentThumbPage + 1);
		}

		update(this);

		// Play filter to either whole page or part of it
		if (this.presentationState != 1) {
			// Thumbnails was currently not presented
			this.presentationState = 1;
		}
		else {
			// Thumbnails are currently presented, just play the filter
		}

//		// Pre-load the first photo
//		var imagesPerPage = this.nThumbRows * this.nThumbCols;
//		var startImage = (pageNum - 1) * imagesPerPage;
//		this.photo[startImage].preloadMain();
	};


	this.showPhotoPage = function(photoIndex) {

		function create (gallery, contentElem) {
			// Creates the static content of a photo page.
			// contentElem = the page content element object

			var myHtml = new String();
			var width = gallery.maxMainWidth + 2*gallery.maxMainBorderWidth + 48;

			myHtml += '<div style="text-align:center">';

			//
			// Navigation bar
			//
			myHtml += '<div id="navbar" style="width:' + width + 'px"></div>';

			//
			// Main Content (photo + descriptions)
			//
			myHtml += '<div id="photo" style="width:' + width + 'px"></div>';

			myHtml += '</div>';

			// Write the update into the container element
			contentElem.innerHTML = myHtml;
		}


		function update(gallery)
		{
			// Update the content of a photo page.

			function updateNavbar()
			{
				// The navbar consists of three parts;
				// left part with slideshow buttons, middle part with nav buttons and
				// a right part with info tools. The width of each three parts is
				// based on max width of a photo
				var navbarElem = document.getElementById("navbar");
				var myHtml = new String();

				//
				// 1 Left part, slideshow
				//
				myHtml += '<p class="navbar" style="float:left;width:180px;text-align:left;padding-left:36px">';

				if (gallery.isPlayingSlideshow) {
					myHtml += '<a title="Stoppa bildspel" href="javascript:gallery.btnSlideshow();">Stopp</a>';
					myHtml += '<span class="spacer"></span>'; // spacer
					myHtml += '<a title"Minska 1s" href="javascript:gallery.btnSlideshowSpeed(-1000);">&mdash;</a>';
					myHtml += '<span class="inactiveLink">' + (gallery.slideshowDelay / 1000) + 's</span>';
					myHtml += '<a title"Öka 1s" href="javascript:gallery.btnSlideshowSpeed(1000);">+</a>';
				}
				else
					myHtml += '<a title="Starta bildspel" href="javascript:gallery.btnSlideshow();">Start</a>';
				myHtml += '</p>';

				//
				// 2 Right part, info
				//
				myHtml += '<p class="navbar" style="float:right;width:180px;text-align:right;padding-right:36px">';
				myHtml += '<a title="Visa bilddata" href="javascript:gallery.showInfo();">Info</a>';
				myHtml += '</p>';

				//
				// 3 Middle part nav buttons
				//
				myHtml += '<p class="navbar" style="text-align:center">';

				// 3.1 Previous photo
				if (photoIndex > 0)
					myHtml += '<a class="wide" title="Visa föregående bild" href="javascript:gallery.btnPhotoPageShowPhoto(' + 	(photoIndex - 1) + ');">&laquo;</a>';
				else
					myHtml += '<span class="inactiveLink wide ">&laquo;</span>';

//				myHtml += '<span class="navbar spacer"></span>'; // spacer

				// 3.2 Back to thumb page
				myHtml += '<a class="wide" title="Tillbaka" href="javascript:gallery.btnPhotoPageShowThumbPage(' + photoIndex + ');">&times;</a>';

//				myHtml += '<span class="navbar spacer"></span>'; // spacer

				// 3.3 Next photo
				if (photoIndex < gallery.photo.length-1)
					myHtml += '<a class="wide" title="Visa nästa bild" href="javascript:gallery.btnPhotoPageShowPhoto(' + (photoIndex + 1) + ');">&raquo;</a>';
				else
					myHtml += '<span class="inactiveLink wide">&raquo;</span>';

				myHtml += '</p>';

				// Write the update into the container element
				navbarElem.innerHTML = myHtml;
			}

			function updateInfo()
			{
				// Updates the photo info page.
				// photoIndex = index of which photo to present
				var infoElem = document.getElementById("info");
				var myHtml = new String();
				var p = gallery.photo[photoIndex];
				// Table
				myHtml += '<table border="0" cellpadding="0"' +
						  ' cellspacing="2">';
				// Date
				myHtml += '<tr><td><b>Datum: &nbsp;</b></td><td>' +
						  p.year + '-' +
						  p.month + '-' +
						  p.day + '&nbsp;&nbsp;' +
						  p.hour + ':' +
						  p.minute + '</td></tr>';
				// Camera
				myHtml += '<tr><td><b>Kamera: &nbsp;</b></td><td>' +
						  p.camera.model + '</td></tr>';
				// Focal length
				myHtml += '<tr><td><b>Brännvidd: &nbsp;</b></td><td>' +
						  p.camera.focalLength + ' mm</td></tr>';
				// ISO
				myHtml += '<tr><td><b>Känslighet: &nbsp;</b></td><td>' +
						  'ISO ' + p.exposure.iso + '</td></tr>';
				// Exposure
				myHtml += '<tr><td><b>Exponering: &nbsp;</b></td><td>' +
						  p.exposure.shutter + ' sek&nbsp;&nbsp;f/' +
						  p.exposure.aperture + '</td></tr>';
				// Flash
				myHtml += '<tr><td><b>Blixt: &nbsp;</b></td><td>' +
						  p.exposure.flash + '</td></tr>';

				// -spacer-
				myHtml += '<tr><td>&nbsp;</td></tr>';

				// Filename
				myHtml += '<tr><td><b>Filnamn: &nbsp;</b></td><td>' +
						  decodeURI(p.filenameAsUrl) + '</td></tr>';
				// URL (in a textarea for copying reference to photo)
				myHtml += '<tr><td><b>URL: &nbsp;</b></td><td>' +
						  '<input type="text" readonly size="30" value="' +
						  p.URL() + '" onfocus="this.select();" ' +
						  'alt="Kopiera för att direkt länka till denna bild">' +
						  '</td></tr>';
				myHtml += '</table>';
				// Close
				myHtml += '<p class="navbar" style="text-align:center"><a class="navbar" href="javascript:gallery.closeInfo();">Stäng</a></p>';

				// Write the into the container element
				infoElem.innerHTML = myHtml;
			}

			function updatePhoto()
			{
				var myHtml = new String();
				var imgOnClick = new String();
				var vertSpacerStyle = '';
				var photoElem = document.getElementById("photo");

				var photoHeight = gallery.photo[photoIndex].mainHeight + 2*gallery.maxMainBorderWidth;
				var photoWidth = gallery.photo[photoIndex].mainWidth + 2*gallery.maxMainBorderWidth;
				 // Add two times the border width, see style.css

				var vertSpacerHeight = (document.documentElement.clientHeight -
									   photoHeight) / 2 - gallery.navbarHeight;

				if (vertSpacerHeight > 0)
					vertSpacerStyle = 'height:' + vertSpacerHeight + 'px;';

				// Vertical spacer for vertical alignment of the photo
				myHtml += '<div style="font-size:0;' + vertSpacerStyle + '"></div>';

				//
				// The photo
				//
				myHtml += '<div style="width:' + photoWidth + 'px">';
				// On click, goto next pic or to index
				if (photoIndex < gallery.photo.length-1)
					imgOnClick = 'onclick="javascript:gallery.btnPhotoPageShowPhoto(' +
								 (photoIndex + 1) + ');" style="cursor:pointer;" alt="Klicka för nästa bild"';
				else
					imgOnClick = 'onclick="gallery.btnPhotoPageShowThumbPage(' +
								 photoIndex + ');" style="cursor:pointer;" alt="Klicka för översikt"';

				myHtml += '<img class="image" src="' + gallery.photo[photoIndex].mainUrl + '" width="' + gallery.photo[photoIndex].mainWidth + '" height="' + gallery.photo[photoIndex].mainHeight + '" ' + imgOnClick + ' />';
				myHtml += '<br />';

				// Copyright
				//
				var copyrightHolder = gallery.photographer;
				if (copyrightHolder.length == 0)
					copyrightHolder = gallery.photo[photoIndex].camera.owner;
				myHtml += '<p class="copyright"><a href="/info/copyright.php">Copyright &copy; ' + gallery.photo[photoIndex].year + ' ' + copyrightHolder + '</a></p>';

				myHtml += '</div>';

				//
				// Caption
				//
				if (gallery.photo[photoIndex].caption.length > 100)
					myHtml += '<div class="longDescription"><p>' + gallery.photo[photoIndex].caption + '</p></div>';
				else
					myHtml += '<div class="shortDescription"><p>' + gallery.photo[photoIndex].caption + '</p></div>';

				// Write the update into the container element
				photoElem.innerHTML = myHtml;
			}

			updateNavbar();
			updateInfo();
			updatePhoto();
		}

		/* -------------------------------------------------------------------
		 *	this.showPhotoPage
		 */

		// Preload current photo
		this.photo[photoIndex].preloadMain();
		this.currentPhoto = photoIndex;

		// Temporarily stop slideshow
		if (this.isPlayingSlideshow)
			window.clearTimeout(this.slideshowTimerId);

		var contentElem = document.getElementById("content");
		var infoElem = document.getElementById("info");
		var photoElem = document.getElementById("photo");

		// Apply filter to either whole page or part of it
		if (this.presentationState != 2) {
			// Photo is currently not presented
			create(this, contentElem);
		}
		else {
			// Photo is currently presented, just freeze the filter
		}

		// Update
		update(this);

		// Play filter to either whole page or part of it
		if (this.presentationState != 2) {
			// Photo was currently not presented
			this.presentationState = 2;
		}
		else {
			// Photo is currently presented, just play the photo filter
		}

		// Re-start slideshow
		if (this.isPlayingSlideshow)
			this.slideshowTimerId = window.setTimeout('gallery.slideshowNext()', this.slideshowDelay);

		// Preload next photo
		if (photoIndex+1 < this.photo.length)
			this.photo[photoIndex+1].preloadMain();

		// ... next-next photo
		if (photoIndex+2 < this.photo.length)
			this.photo[photoIndex+2].preloadMain();

		// ... previous photo
		if (photoIndex-1 > 0)
			this.photo[photoIndex-1].preloadMain();

//		// ... previous-previous photo
//		if (photoIndex-2 > 0)
//			this.photo[photoIndex-2].preloadMain();
	}


	this.showInfo = function() {
		var infoElem = document.getElementById("info");
		if (infoElem.style.display == 'block') {
			// Hide info page
			infoElem.style.display = 'none';
		}
		else {
			// Show info page
			infoElem.style.display = 'block';
		}
	}


	this.closeInfo = function() {
		var infoElem = document.getElementById("info");
		infoElem.style.display = 'none'; // show info page
	}


	this.preloadThumbs = function (nRows, nCols, pageNum) {
		if (pageNum < 1) return;

		var numPages = this.nThumbPages();

		if (pageNum > numPages) return;

		var numImages = this.photo.length + 1;
		var imagesPerPage = nRows * nCols;

		// Work out start and end images for this page
		var startImage = (pageNum - 1) * imagesPerPage;
		var endImage = startImage + imagesPerPage;
		if (endImage > this.photo.length) endImage = this.photo.length;

		for (var n = startImage; n < endImage; n++)
			this.photo[n].preloadThumb();
	};


	this.btnThumbPageShowPage = function(pageNum) {
		this.showThumbPage(pageNum);
	};


	this.btnThumbPageShowPhoto = function(photoIndex) {
		this.showPhotoPage(photoIndex);
	};


	this.btnPhotoPageShowThumbPage = function(photoIndex) {
		// Stop slideshow
		if (this.isPlayingSlideshow) {
			this.isPlayingSlideshow = false;
			window.clearTimeout(this.slideshowTimerId);
		}

		// Showing all thumbs?
		if (this.currentThumbPage == -1) {
			// Show all thumbs and scroll to the thumb showing photoIndex
			this.showThumbPage(-1); // show all thumbs page
			var td = document.getElementById('thumb' + photoIndex);
			window. window.scrollTo(0, td.offsetTop);
		}
		else {
			// Just showing one thumb page
			this.showThumbPage(this.thumbPageNumOf(photoIndex)); // show thumb page
		}
	};


	this.btnPhotoPageShowPhoto = function(photoIndex) {
		this.showPhotoPage(photoIndex);
	};

	this.btnSlideshow = function() {
		if (this.isPlayingSlideshow) {
			this.isPlayingSlideshow = false;
			window.clearTimeout(this.slideshowTimerId);
			this.showPhotoPage(this.currentPhoto);
		}
		else {
			this.isPlayingSlideshow = true;
			this.currentPhoto--;
			this.slideshowNext();
		}
	};

	this.btnSlideshowSpeed = function(delta) {
		this.slideshowDelay += delta;
		if (this.slideshowDelay < 1000)
			this.slideshowDelay = 1000;
		if (this.slideshowDelay > 30000)
			this.slideshowDelay = 30000;

		if (this.isPlayingSlideshow) {
			window.clearTimeout(this.slideshowTimerId);
			this.showPhotoPage(this.currentPhoto);
		}
	};

	this.slideshowNext = function() {
		this.currentPhoto++;
		if (this.currentPhoto >= this.photo.length)
			this.currentPhoto = 0;
		this.showPhotoPage(this.currentPhoto);
	};




	this.nThumbPages = function() {
		// Count the number of pages
		var numImages = this.photo.length;
		var imagesPerPage = this.nThumbRows * this.nThumbCols;
		var remainder = numImages;
		var numPages = 0;
		while (remainder > 0) {
			numPages++;
			remainder -= imagesPerPage;
		}
		return numPages;
	};


	this.thumbPageNumOf = function(photoIndex) {
		// photoIndex range: 0..this.photo.length
		// Returned page num range: 1..
		var pageNum = 1;
		while (pageNum * this.nThumbRows * this.nThumbCols -1 < photoIndex)
			pageNum++;
		return pageNum;
	};

	this.normalizeThumbPageNumber = function(n) {
		// Returns a normalized tumbs page number depending on number of
		// photos in the gallery
		var p = n;
		if (p < 1) return 1;
		var numPages = this.nThumbPages();
		if (p > numPages) return numPages;
		return p;
	};
}


function Camera(owner, model, serial, firmware, orientation, focusMode,
				focusPoint, driveMode, selfTimer, lens, focalLength,
				subjectDistance)
{
	// Definition of camera information, used by the BreezeBrowser Template
	// and part of a Photo
	this.owner = owner;
	this.model = model;
	this.serial = serial;
	this.firmware = firmware;
	this.orientation = orientation;
	this.focusMode = focusMode;
	this.focusPoint = focusPoint;
	this.driveMode = driveMode;
	this.selfTimer = selfTimer;
	this.lens = lens;
	this.focalLength = focalLength;
	this.subjectDistance = subjectDistance;
}


function Exposure(whiteBalance, iso, shutter, aperture, meteringMode,
				  exposureMode, exposureCompensation,
				  totalExposureCompensation, flash, flashCompensation,
				  EV1, aebExpCompensation, aebShotNumber)
{
	// Definition of exposure information, used by the BreezeBrowser Template
	// and part of a Photo
	this.whiteBalance = whiteBalance;
	this.iso = iso;
	this.shutter = shutter;
	this.aperture = aperture;
	this.meteringMode = meteringMode;
	this.exposureMode = exposureMode;
	this.exposureCompensation = exposureCompensation;
	this.totalExposureCompensation = totalExposureCompensation;
	this.flash = flash;
	this.flashCompensation = flashCompensation;
	this.EV1 = EV1;
	this.aebExpCompensation = aebExpCompensation;
	this.aebShotNumber = aebShotNumber;
}

function ImageProcessing(colorProfile, contrast, quality, saturation,
						 sharpness)
{
	// Definition of in-camera image processing information, used by the
	// BreezeBrowser Template and part of a Photo
	this.colorProfile = colorProfile;
	this.contrast = contrast;
	this.quality = quality;
	this.saturation = saturation;
	this.sharpness = sharpness;
}

function Photo(camera, exposure, imageProcessing,
			   mainUrl, mainWidth, mainHeight,
			   thumbUrl, thumbWidth, thumbHeight,
			   caption, captionStripped,
			   year, month, day, hour, minute, second, subsecond,
			   originalWidth, originalHeight, originalSize,
			   dirEscaped, filenameAsUrl, imageCounter)
{
	// Definition of a photo, used by the BreezeBrowser Template.
	// Each photos is added to a gallery, one at a time.
	this.camera = camera;
	this.exposure = exposure;
	this.imageProcessing = imageProcessing;
	this.mainUrl = mainUrl;
	this.mainWidth = mainWidth;
	this.mainHeight = mainHeight;
	this.thumbUrl = thumbUrl;
	this.thumbWidth = thumbWidth;
	this.thumbHeight = thumbHeight;
	this.caption = str2html(caption);
	this.captionStripped = captionStripped;
	this.year = year;
	this.month = month;
	this.day = day;
	this.hour = hour;
	this.minute = minute;
	this.second = second;
	this.subsecond = subsecond;
	this.originalWidth = originalWidth;
	this.originalHeight = originalHeight;
	this.originalSize = originalSize;
	this.dirEscaped = dirEscaped;
	this.filenameAsUrl = filenameAsUrl;
	this.imageCounter = imageCounter;
	this.isMainPreloaded = false;
	this.isThumbPreloaded = false;

	this.dateTime = function() {
		return this.year.toString() + '-' + this.month.toString() + '-' +
			   this.day.toString() + ' ' + this.hour.toString() + ':' +
			   this.minute.toString();
	};

	this.preloadMain = function() {
		if (!this.isMainPreloaded && document.images) {
			this.imgMain = new Image(this.mainWidth, this.mainHeight);
			this.imgMain.src = this.mainUrl; // pre-load
			this.isMainPreloaded = true;
		}
	};

	this.preloadThumb = function() {
		if (!this.isThumbPreloaded && document.images) {
			this.imgThumb = new Image(this.thumbWidth, this.thumbHeight);
			this.imgThumb.src = this.thumbUrl; // pre-load
			this.isThumbPreloaded = true;
		}
	};

	this.URL = function() {
		var path = document.URL;

		// Remove any existing query-keyword in the URL
		var i = path.indexOf("?");
		if (i > 0)
			path = path.substring(0, i);

		// Remove trailing / in the URL
		if (path.substring(path.length-1) == '/')
			path = path.substring(0, path.length-1);

		return path + '?photo=' + this.filenameAsUrl;
	};
}


//--------------------------------------------------------------------------//
// End
//--------------------------------------------------------------------------//




//--------------------------------------------------------------------------//
// Thumbnail
//--------------------------------------------------------------------------//
function ThumbnailFrameImgPart(color, width, height, basename) {
	var path = '/rsrc/photo/gallery/thumb/' + color + '/';
	this.width = width;
	this.height = height;
	this.src = path + basename + '.png';
	if (document.images) {
		this.img = new Image(width, height);
		this.img.src = this.src; // pre-load
	}

	this.html = function (width, height) {
		return '<img src="' + this.src + '" width="' + width +
			   '" height="' + height + '" />';
	};
}



function ThumbnailFrame(color, maxThumbWidth, maxThumbHeight, thickness) {
	this.thickness = thickness;
	this.maxThumbWidth = maxThumbWidth;
	this.maxThumbHeight = maxThumbHeight;

	this.fill        = new ThumbnailFrameImgPart(color,  1,  1, "fill");
	this.row1_col1   = new ThumbnailFrameImgPart(color, 10, 10, "row1_col1");
	this.row1_col2_6 = new ThumbnailFrameImgPart(color,  1, 10, "row1_col2-6");
	this.row1_col7   = new ThumbnailFrameImgPart(color, 10, 10, "row1_col7");
	this.row2_6_col1 = new ThumbnailFrameImgPart(color, 10,  1, "row2-6_col1");
	this.row2_6_col7 = new ThumbnailFrameImgPart(color, 10,  1, "row2-6_col7");
	this.row3_col3   = new ThumbnailFrameImgPart(color,  3,  3, "row3_col3");
	this.row3_col4   = new ThumbnailFrameImgPart(color,  1,  3, "row3_col4");
	this.row3_col5   = new ThumbnailFrameImgPart(color,  3,  3, "row3_col5");
	this.row4_col3   = new ThumbnailFrameImgPart(color,  3,  1, "row4_col3");
	this.row4_col4   = new ThumbnailFrameImgPart(color,  1,  1, "row4_col4");
	this.row4_col5   = new ThumbnailFrameImgPart(color,  3,  1, "row4_col5");
	this.row5_col3   = new ThumbnailFrameImgPart(color,  3,  3, "row5_col3");
	this.row5_col4   = new ThumbnailFrameImgPart(color,  1,  3, "row5_col4");
	this.row5_col5   = new ThumbnailFrameImgPart(color,  3,  3, "row5_col5");
	this.row7_col1   = new ThumbnailFrameImgPart(color, 10, 10, "row7_col1");
	this.row7_col2_6 = new ThumbnailFrameImgPart(color,  1, 10, "row7_col2-6");
	this.row7_col7   = new ThumbnailFrameImgPart(color, 10, 10, "row7_col7");
	this.innerLeftBorder = 3;
	this.innerRightBorder = 3;
	this.innerTopBorder = 3;
	this.innerBottomBorder = 3;

	this.col1Width = this.row1_col1.width;
	this.col3Width = this.row3_col3.width;;
	this.col5Width = this.row3_col5.width;
	this.col7Width = this.row1_col7.width;
	this.row1Height = this.row1_col1.height;
	this.row3Height = this.row3_col3.height;
	this.row5Height = this.row3_col5.height;
	this.row7Height = this.row1_col7.height;

	this.frameWidth = this.maxThumbWidth + thickness + thickness +
					  this.col1Width + this.innerLeftBorder +
					  this.innerRightBorder + this.col7Width;

	this.frameHeight = this.maxThumbHeight + thickness + thickness +
					   this.row1Height + this.innerTopBorder +
					   this.innerBottomBorder + this.row7Height;


	this.frameHtml = function (photo, photoIndex) {
		// Return the HTML code for a frame
		var myHtml = new String();



		// Calculated sizes
		var col4Width = photo.thumbWidth;
		var col2Width = Math.floor((this.frameWidth - this.col1Width - this.col3Width -
						col4Width - this.col5Width - this.col7Width) / 2);
		var col6Width = this.frameWidth - this.col1Width - col2Width -
						this.col3Width - col4Width - this.col5Width -
						this.col7Width;
		var col2_6Width = col2Width + this.col3Width +
						  col4Width + this.col5Width + col6Width;
		var row4Height = photo.thumbHeight;
		var row2Height = Math.floor((this.frameHeight - this.row1Height - this.row3Height -
						 row4Height - this.row5Height - this.row7Height) / 2);
		var row6Height = this.frameHeight - this.row1Height - row2Height -
						 this.row3Height - row4Height - this.row5Height - this.row7Height;

		var imagePosTop = this.row1Height + row2Height + this.innerTopBorder;
		var imagePosLeft = this.col1Width + col2Width + this.innerLeftBorder;

//		myHtml += '<div>';
//		myHtml += 'thickness: ' + this.thickness + '<br />';
//		myHtml += 'frameWidth: ' + this.frameWidth + '<br />';
//		myHtml += 'frameHeight: ' + this.frameHeight + '<br />';
//		myHtml += 'row2Height: ' + row2Height + '<br />';
//		myHtml += 'row6Height: ' + row6Height + '<br />';
//		myHtml += '</div>';

		myHtml += '<div onclick="gallery.btnThumbPageShowPhoto(' +
				  photoIndex + ');" style="display:block; width:' +
				  this.frameWidth + 'px; height:' + this.frameHeight +
				  'px; padding:0; margin:0px; cursor:pointer;' +
				  'font-size:0"; title="' + photo.captionStripped + '">';

		myHtml += this.row1_col1.html(this.col1Width, this.row1Height);
		myHtml += this.row1_col2_6.html(col2_6Width, this.row1Height);
		myHtml += this.row1_col7.html(this.col7Width, this.row1Height);
		myHtml += '<br />';

		if (row2Height > 0) {
			myHtml += this.row2_6_col1.html(this.col1Width, row2Height);
			myHtml += this.fill.html(col2_6Width, row2Height);
			myHtml += this.row2_6_col7.html(this.col7Width, row2Height);
			myHtml += '<br />';
		}

		myHtml += this.row2_6_col1.html(this.col1Width, this.row3Height);
		if (col2Width > 0) myHtml += this.fill.html(col2Width, this.row3Height);
		myHtml += this.row3_col3.html(this.col3Width, this.row3Height);
		myHtml += this.row3_col4.html(col4Width, this.row3Height);
		myHtml += this.row3_col5.html(this.col5Width, this.row3Height);
		if (col6Width > 0) myHtml += this.fill.html(col6Width, this.row3Height);
		myHtml += this.row2_6_col7.html(this.col7Width, this.row3Height);
		myHtml += '<br />';

		myHtml += this.row2_6_col1.html(this.col1Width, row4Height);
		if (col2Width > 0) myHtml += this.fill.html(col2Width, row4Height);
		myHtml += this.row4_col3.html(this.col3Width, row4Height);
		myHtml += '<img border="0" src="' +
				  photo.thumbUrl + '" width="' + col4Width +
				  '" height="' + row4Height + '" />';
		myHtml += this.row4_col5.html(this.col5Width, row4Height);
		if (col6Width > 0) myHtml += this.fill.html(col6Width, row4Height);
		myHtml += this.row2_6_col7.html(this.col7Width, row4Height);
		myHtml += '<br />';

		myHtml += this.row2_6_col1.html(this.col1Width, this.row5Height);
		if (col2Width > 0) myHtml += this.fill.html(col2Width, this.row5Height);
		myHtml += this.row5_col3.html(this.col3Width, this.row5Height);
		myHtml += this.row5_col4.html(col4Width, this.row5Height);
		myHtml += this.row5_col5.html(this.col5Width, this.row5Height);
		if (col6Width > 0) myHtml += this.fill.html(col6Width, this.row5Height);
		myHtml += this.row2_6_col7.html(this.col7Width, this.row5Height);
		myHtml += '<br />';

		if (row6Height > 0) {
			myHtml += this.row2_6_col1.html(this.col1Width, row6Height);
			myHtml += this.fill.html(col2_6Width, row6Height);
			myHtml += this.row2_6_col7.html(this.col7Width, row6Height);
			myHtml += '<br />';
		}

		myHtml += this.row7_col1.html(this.col1Width, this.row7Height);
		myHtml += this.row7_col2_6.html(col2_6Width, this.row7Height);
		myHtml += this.row7_col7.html(this.col7Width, this.row7Height);
		myHtml += '<br />';

		myHtml += '</div>';

		return myHtml;
	};
}


//--------------------------------------------------------------------------//
// End
//--------------------------------------------------------------------------//



//--------------------------------------------------------------------------//
// String functions
//--------------------------------------------------------------------------//
function str2html(s) {
	var r = new String(s);

	r = r.replace(/&lt;center&gt;/ig, '<center>');
	r = r.replace(/&lt;\/center&gt;/ig, '</center>');
	r = r.replace(/&lt;b&gt;/ig, '<b>');
	r = r.replace(/&lt;\/b&gt;/ig, '</b>');
	r = r.replace(/&lt;i&gt;/ig, '<i>');
	r = r.replace(/&lt;\/i&gt;/ig, '</i>');
	r = r.replace(/&lt;p&gt;/ig, '<p>');
	r = r.replace(/&lt;\/p&gt;/ig, '</p>');
	r = r.replace(/&lt;a href=&quot;/ig, '<a href="');
	r = r.replace(/&lt;\/a&gt;/ig, '</a>');
	r = r.replace(/&quot;&gt;/ig, '">');
	r = r.replace(/&lt;br \/&gt;/ig, '<br />');
	r = r.replace(/&lt;br&gt;/ig, '<br />');
	r = r.replace(/\n/ig, '<br />');
	return r;
}

function objectPropsTable(obj, obj_name) {
	// Put the result in a table
	var result = '<table>';
	for (var i in obj) {
		result += '<tr valign="top"><td>' + i + '</td><td>';
		if (typeof(obj[i]) == 'object')
			result += objectPropsTable(obj[i], i);
		else
			result += obj[i].toString();
		result += '</td></tr>';
	}
	result += '</table>';
	return result;
}
//--------------------------------------------------------------------------//
// End
//--------------------------------------------------------------------------//




//--------------------------------------------------------------------------//
// Browser identification
//--------------------------------------------------------------------------//
function getInternetExplorerVersion() {
	// Returns the version of Internet Explorer or a -1
	// (indicating the use of another browser).
	//
	// Source code copied from
	// http://msdn2.microsoft.com/en-us/library/ms537509.aspx
	var rv = -1; // Return value assumes failure.
	if (navigator.appName == 'Microsoft Internet Explorer')
	{
		var ua = navigator.userAgent;
		var re  = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
		if (re.exec(ua) != null)
			rv = parseFloat( RegExp.$1 );
	}
	return rv;
}

var ieVer = getInternetExplorerVersion();
//--------------------------------------------------------------------------//
// End
//--------------------------------------------------------------------------//




function printTextualContent() {
	var myHtml = '';
	myHtml += '<table><tr valign="top">';
	myHtml += '<td>#</td>';
	myHtml += '<td>Photo Info</td>';
	myHtml += '</tr>';
	for (var i = 0; i < gallery.photo.length; i++) {
		var p = gallery.photo[i];
		if (i<2) {
				myHtml += '<tr valign="top">';
				myHtml += '<td>' + i + '</td>';
				// myHtml += '<td>' + objectPropsTable(p) + '</td>';
				myHtml += '</tr>';
		}
	}
	myHtml += '</table>';
	document.getElementById("thumbpage.thumbs").innerHTML = myHtml;
}

