/* 	Module:
		PhotoGallery

	Author:
		Neil Jenkins - http://www.nmjenkins.com
		
	Version:
		1.01 (2008-04-16)
		
	Version History:
		1.01 (2008-04-16) Allow preference to no resize images
		1.0  (2008-02-01) Initial release

	License:
		GNU GPL 2.0: http://creativecommons.org/licenses/GPL/2.0/
	
	Description:
		Javascript module to build a kickass photo gallery from a Flickr account.
		
	Usage:
		Attach this file to a suitable HTML page.

	Dependencies:
		mootools: http://mootools.net
		Preloader
		HistoryManager
		Flickr
		Stylesheet

	Notes:
		Supports Firefox 1.5+, Safari 2+, Opera 9+ and IE 6+
		Other browsers may work but are untested.
*/



// Insert your Flickr API key here.
FlickrAPI.initialise("866db0aecc1ec5e55f28ca5f66e42c4a");

// Insert your Flickr nsid here:
var nsid = "16142610@N04"; //Should look a bit like: 45492092@N00

// Preference: Stop the browser to resize images. You may wish to set this for Windows machines as most
// Windows browsers don't interpolate images properly so it looks a bit odd.
var noResize = false; //e.g. navigator.userAgent.test(/Windows/);



/* ----------------------------------------------------------------- */

// DON'T TOUCH ANYTHING BELOW HERE UNLESS YOU KNOW WHAT YOU ARE DOING!

/* ----------------------------------------------------------------- */



/* 	Function: elementFromTemplate
		Creates and returns a new element using an existing one as a template.
		Replaces dummy variables beginning with a '$' within the template with the correct values.
		
	Arguments:
		string/HTML Element: template id/element
		object: an associative array of variable: value pairs.
		
	Notes:
		for clarity can optionally be used with the new keyword
*/

function elementFromTemplate(templateID, templateVariables) {
	var newElement = $(templateID).clone(false);
	newElement.removeAttribute('id');
	var template = $(templateID).innerHTML;
	
	// For Opera - it does stupid things to improper image addresses when retrieved via innerHTML
	if (window.opera)
		template = template.replace(/url[^\)\$]*\$/gm, 'url("$$');
	
	for(property in templateVariables) {
		var matchPattern = new RegExp(property.escapeRegExp(), 'g');
		template = template.replace(matchPattern, templateVariables[property]);
	};

	newElement.innerHTML = template;
	return newElement;
};

/* ---------------------------------- */

window.addEvent('domready', function() {

	new Gallery();

});

/* galleryData structure:
	
	{
	albumList: [photoset_id],	
	album{photoset_id}: {
		title: String,
		description: String,
		keyPhotoSrc: String,
		keyPhotoID
		numberOfPhotos: Number,
		photos: imgData
		}
	}
	
	imgData structure:

	{
	photo'photoID': {
		id: String,
		title: String,
		description: String,
		src: String,
		next: String photoID,
		previous: String photoID,
		ratio: Number (width/height),
		variants: [{width: Number, height: Number, name: String}]
	},
	_firstImage: String photoid
	}
*/

/* All Flickr API calls should in theory be from this class */

var Gallery = new Class({

	initialize: function() {
	
		var gallery = this;
		this._galleryData = {};

		this._preloader = new Preloader();
		this.albums = new Albums(this);
		this.grid = new Grid(this);
		this.displayPhoto = new DisplayPhoto(this);
		this.filmstrip = new Filmstrip(this);
		
		// Setup history management
		
		this.history = new HistoryManager();
	
		this.history.addEvent('onHistoryChange', function(hash) {

			var mode = hash.split(':')[0] || 'albums';
			var photoset_id = hash.split(':')[1];
			var image_id = hash.split(':')[2];
			
			gallery.changeMode(mode, photoset_id, image_id);
		});

		['albums', 'grid','filmstrip','single'].each(function(mode){
			$$('.mode' + mode.capitalize()).addEvent('click', function(event) {
				new Event(event).stop();
				this.blur();
				gallery.changeMode(mode);
			});
		});

		this.history.fireEvent('onHistoryChange', [this.history.getCurrentLocation()]);
		
		this.addEvent('modeChange', function(mode, photoset_id, image_id) {
			if (mode == 'albums') return this.history.addState('');
			this.history.addState(mode + ':' + photoset_id + (image_id ? (':' + image_id) : ''));
		}.bind(this));
	},
	
	getAlbumList: function(callbackFunction) {
	
		if (this._galleryData.albumList)
			return callbackFunction(this._galleryData.albumList);

		FlickrAPI.get({method: 'flickr.photosets.getList', user_id: nsid}, function(data) {

			this._galleryData.albumList = [];

			data.photosets.photoset.forEach(function(album) {
				this._galleryData.albumList.push(album.id);
				
				this._galleryData['album' + album.id] = this._galleryData['album' + album.id] || {};
							
				$extend(this._galleryData['album' + album.id], {
					title: album.title._content,
					description: album.description._content,
					keyPhotoSrc: "http://farm" + album.farm + ".static.flickr.com/" + album.server + "/" +
								  album.primary + "_" + album.secret + ".jpg",
					keyPhotoID: album.primary,
					numberOfPhotos: parseInt(album.photos)
				});
			}.bind(this));
			callbackFunction(this._galleryData.albumList);
		}.bind(this));
	},
	
	getAlbumData: function(photoset_id, callbackFunction) {
		if (this._galleryData['album' + photoset_id])
			return callbackFunction(this._galleryData['album' + photoset_id]);
		
		FlickrAPI.get({method: 'flickr.photosets.getInfo', photoset_id: photoset_id}, function(data) {

			var album = data.photoset;
			this._galleryData['album' + album.id] = this._galleryData['album' + album.id] || {};
						
			$extend(this._galleryData['album' + album.id], {
				title: album.title._content,
				description: album.description._content,
				keyPhotoSrc: "http://farm" + album.farm + ".static.flickr.com/" + album.server + "/" +
							  album.primary + "_" + album.secret + ".jpg",
				keyPhotoID: album.primary,
				numberOfPhotos: parseInt(album.photos)
			});
			callbackFunction(this._galleryData['album' + photoset_id]);
		}.bind(this));
	},

	getAlbumPhotos: function(photoset_id, callbackFunction) {
		if (this._galleryData['album' + photoset_id]
		 && this._galleryData['album' + photoset_id].photos
		 && this._galleryData['album' + photoset_id].photos._firstImage)
			return callbackFunction(this._galleryData['album' + photoset_id].photos);
		
		// Optional extra: date_taken
		FlickrAPI.get({method: 'flickr.photosets.getPhotos', photoset_id: photoset_id, extras: 'original_format'}, function(data) {

			this._galleryData['album' + photoset_id] = this._galleryData['album' + photoset_id] || {};

			var imageData = (this._galleryData['album' + photoset_id].photos = this._galleryData['album' + photoset_id].photos || {});
			
			data.photoset.photo.forEach(function(photo, i){
				imageData['photo' + photo.id] = imageData['photo' + photo.id] || {}
				$extend(imageData['photo' + photo.id], {
					id: photo.id,
					title: photo.title || '',
					srcBase: "http://farm" + photo.farm + ".static.flickr.com/" + photo.server + "/" +
							  photo.id + "_" + photo.secret,
					originalSrc: "http://farm" + photo.farm + ".static.flickr.com/" + photo.server + "/" +
							  photo.id + "_" + photo.originalsecret + "_o_d." + photo.originalformat,
					next: (i == data.photoset.photo.length-1) ? null : data.photoset.photo[i + 1].id,
					previous: (i == 0) ? null : data.photoset.photo[i - 1].id
				});
			});

			imageData._firstImage = data.photoset.photo[0].id;

			callbackFunction(this._galleryData['album' + photoset_id].photos);
		}.bind(this));
	},

	getImageData: function(photoset_id, image_id, callbackFunction) {
		if (this._galleryData['album' + photoset_id]
		 && this._galleryData['album' + photoset_id].photos
		 && this._galleryData['album' + photoset_id].photos['photo' + image_id]
		 && $defined(this._galleryData['album' + photoset_id].photos['photo' + image_id].description))
				return callbackFunction(this._galleryData['album' + photoset_id].photos['photo' + image_id]);

		FlickrAPI.get({method: 'flickr.photos.getInfo', photo_id: image_id}, function(data) {
			var photo = data.photo;

			var photoData = this._galleryData['album' + photoset_id] = (this._galleryData['album' + photoset_id] || {});
		 	photoData = (photoData.photos = photoData.photos || {});
		 	photoData = (photoData['photo' + photo.id] = photoData['photo' + photo.id] || {});

		 	$extend(photoData, {
				id: photo.id,
				title: photo.title._content || ' ',
				description: photo.description._content || ' ',
				srcBase: "http://farm" + photo.farm + ".static.flickr.com/" + photo.server + "/" +
						  photo.id + "_" + photo.secret,
				originalSrc: "http://farm" + photo.farm + ".static.flickr.com/" + photo.server + "/" +
						  photo.id + "_" + photo.originalsecret + "_o_d." + photo.originalformat
			});
			return callbackFunction(this._galleryData['album' + photoset_id].photos['photo' + image_id]);
		}.bind(this));
	},

	getSizes: function(photoset_id, image_id, callbackFunction) {
		if (this._galleryData['album' + photoset_id]
		 && this._galleryData['album' + photoset_id].photos
		 && this._galleryData['album' + photoset_id].photos['photo' + image_id]
		 && this._galleryData['album' + photoset_id].photos['photo' + image_id].variants)
		 		return callbackFunction(this._galleryData['album' + photoset_id].photos['photo' + image_id].variants);

		FlickrAPI.get({method: 'flickr.photos.getSizes', photo_id: image_id}, function(data) {
		
			this._galleryData['album' + photoset_id] = this._galleryData['album' + photoset_id] || {};
			this._galleryData['album' + photoset_id].photos = this._galleryData['album' + photoset_id].photos || {};
			this._galleryData['album' + photoset_id].photos['photo' + image_id] = this._galleryData['album' + photoset_id].photos['photo' + image_id] || {};
		
			var variants = (this._galleryData['album' + photoset_id].photos['photo' + image_id].variants = []);
			data.sizes.size.forEach(function(size) {
				variants.push({
					label: size.label,
					width: parseInt(size.width),
					height: parseInt(size.height),
					src: size.source
				});
			});

			variants.ratio = variants[variants.length-1].height / variants[variants.length-1].width;

		 	return callbackFunction(variants);
		 }.bind(this));
	},

	// Presumes image data already been called.
	getContext: function(photoset_id, image_id, callbackFunction) {
		var photo = this._galleryData['album' + photoset_id].photos['photo' + image_id];
		if (photo.next)
			return callbackFunction({next: photo.next, previous: photo.previous});
		FlickrAPI.get({method: 'flickr.photosets.getContext', photo_id: image_id, photoset_id: photoset_id}, function(data) {
			photo.next = data.nextphoto.id || null;
			photo.previous = data.prevphoto.id || null;
			callbackFunction({next: photo.next, previous: photo.previous})
		});
	},

	// Presumes getAlbumPhotos() has already been called.
	getFirst: function(photoset_id) {
		return this._galleryData['album' + photoset_id].photos._firstImage;
	},
	
	changeMode: function(mode, photoset_id, image_id) {
		if (this.mode == mode) {
			if (this.displayPhoto._active)
				this.displayPhoto.show(photoset_id, image_id);
			return;
		}

		document.body.className = mode.capitalize();

		if (mode != 'albums')
			photoset_id = photoset_id || this.displayPhoto._currentAlbum || this.grid._currentAlbum;

		if (mode == 'single' || mode == 'filmstrip')
			image_id = image_id || this.displayPhoto._currentPhoto || this.getFirst(photoset_id);

		this.fireEvent('modeChange', [mode, photoset_id, image_id]);

		$$('.modeSelector').removeClass('selected');
		$$('.mode' + mode.capitalize()).addClass('selected');

		this.mode = mode;
	}
});

Gallery.implement (new Events);

var Albums = new Class({
	
	initialize: function(gallery) {
		this._gallery = gallery;
		this.title = document.title;
		gallery.addEvent('modeChange', function(mode) {
			if (mode != 'albums')
				return;
			if (!this.built)
				this.build();
			document.title = this.title;
		}.bind(this));
	},
	
	build: function() {
		var gallery = this._gallery;

		this._gallery.getAlbumList(function(albumList) {
	
			albumList.forEach(function(photoset_id, i) {
			
				gallery.getAlbumData(photoset_id, function(album) {

					var albumDiv = new elementFromTemplate('template_album', {
						$album_title: album.title,
						$album_description: album.description,
						$album_date: "",
						$album_linkHref: "#grid:" + photoset_id,
						$album_numberOfPhotos: album.numberOfPhotos,
						$album_keyImageSrc: album.keyPhotoSrc
					});
					
					albumDiv.getElements('a').forEach(function(link) {
						if (link.href.split('#')[1] == "grid:" + photoset_id)
							link.addEvent('click', function(event) {
								new Event(event).stop();
								this.blur();
								gallery.changeMode('grid', photoset_id);
							});
					});
					
					if (i % 2) albumDiv.addClass("even");
	
					albumDiv.id = "album" + album.id;
	
					$('albums').appendChild(albumDiv);			
	
					// Add album date - requires a separate API call; this is a bit of a hack.
					
					FlickrAPI.get({method: 'flickr.photos.getInfo', photo_id: album.keyPhotoID}, function(data) {
	
						var monthName = ["January","February","March","April","May","June",
										 "July","August","September","October","November","December"];
						// Formats the date to 'Month yyyy'
						albumDiv.getElement("h4").innerHTML =
							monthName[data.photo.dates.taken.slice(5, 7) - 1] + " " + data.photo.dates.taken.slice(0, 4);
						
					});
				});
			});
		});
			
		this.built = true;
	}
});

var Grid = new Class({

	initialize: function(gallery) {
		this._gallery = gallery;
		this._currentAlbum = '';

		this._preferences = new Hash.Cookie('PhotoGalleryPreferences', {duration: 365, path: '/'});
		this._preferences.set('thumbsSize', this._preferences.get('thumbsSize') || 80);

		gallery.addEvent('modeChange', function(mode, photoset_id) {
			$$('.thumbnails').setStyle('display', 'none');
			if (mode != 'grid') return;
			if (mode == 'grid' && !$('grid' + photoset_id))
				this.build(photoset_id);
			else
				$('grid' + photoset_id).removeAttribute('style');
			this._currentAlbum = photoset_id;
			this._gallery.getAlbumData(photoset_id, function(album) {
				document.title = album.title;
				$('albumTitle').innerHTML = album.title;
			});
			if (!this.sliderBuilt)
				this._createSlider();
		}.bind(this));
	},
	
	build: function(photoset_id) {
		var galleryDiv = new Element('div', {
			'id': 'grid' + photoset_id,
			'class': 'grid thumbnails'
		}).inject('photos');
		
		var gallery = this._gallery;
		
		this._gallery.getAlbumPhotos(photoset_id, function(photos){
		
			var photo = photos['photo' + photos._firstImage];
		
			do {
				var thumbnail = new elementFromTemplate('template_thumbnail', {
					$thumbnail_linkHref: "#single:" + photoset_id + ":" + photo.id,
					$thumbnail_title: photo.title,
					$thumbnail_src: photo.srcBase + "_m.jpg"
				}).inject(galleryDiv);
				
				
				thumbnail.getElements('a').forEach(function(link) {
					var photo_id = photo.id;
					if (link.href.split('#')[1] == "single:" + photoset_id + ":" + photo.id)
						link.addEvent('click', function(event) {
							new Event(event).stop();
							this.blur();
							gallery.changeMode('single', photoset_id, photo_id);
						});
				});

				var img = thumbnail.getElement('img');
				img.addEvent('load', function() {
					if (this.width > this.height)
						this.addClass('landscape');
					else
						this.addClass('portrait');				
				});
			} while (photo = photos['photo' + photo.next]);
		});
	},
	
	resizeThumbnails: function(size) {
	
		var borderX = $E('.thumbnail .imageBorder').getSize().size.x - $E('.thumbnail img').getSize().size.x;
		var borderY = $E('.thumbnail .imageBorder').getSize().size.y - $E('.thumbnail img').getSize().size.y;		
	
		var css = new Stylesheet('main.css');
		css.getRule('.thumbnail').style.width = size + 'px';
		css.getRule('.thumbnail').style.height = size + 'px';
		css.getRule('.landscape').style.width = size - (borderX || 10) + 'px';
		css.getRule('.portrait').style.height = size - (borderY || 10) + 'px';	
	},

	_createSlider: function() {
	
		var grid = this;
			
		new Slider($('resizeSlider'), $('resizeHandle'), {
			steps: 160,	
			onChange: function(pos){
				grid.resizeThumbnails(pos + 80);
			},
			onComplete: function(pos){
				grid._preferences.set('thumbsSize', pos);			
			}		 
		}).set(parseInt(this._preferences.get('thumbsSize')));
		
		this.sliderBuilt = true;
	}
});

var DisplayPhoto = new Class({
	
	initialize: function(gallery) {
		this._gallery = gallery;
		this._preloader = gallery._preloader;
		this._displayPhoto = $E('#displayPhoto .imageBorder');
		this._currentPhoto = '';
		this._currentAlbum = '';
		this._fadeout = new Fx.Style(this._displayPhoto, 'opacity', {duration:400, transition: Fx.Transitions.Sine.easeInOut});
		this._fadein = new Fx.Style(this._displayPhoto, 'opacity', {duration:300, transition: Fx.Transitions.Sine.easeInOut});
		
		var css = new Stylesheet('main.css').getRule('#displayPhoto');
		this._margin = {
			top: parseInt(css.style.marginTop),
			right: parseInt(css.style.marginRight),
			bottom: parseInt(css.style.marginBottom),
			left: parseInt(css.style.marginLeft)
		}
		css.style.margin = '0';
		
		var displayPhoto = this;
		
		$('nextPhoto').addEvent('click', function(event) {
			new Event(event).stop();
			this.blur();
			displayPhoto.next.call(displayPhoto);
		});
		
		$('previousPhoto').addEvent('click', function(event) {
			new Event(event).stop();
			this.blur();
			displayPhoto.previous.call(displayPhoto);
		});
		
		gallery.addEvent('modeChange', function(mode, photoset_id, image_id) {
			if (mode != 'single' && mode != 'filmstrip') this.exit();
			else this.enter(photoset_id, image_id);
		}.bind(this));
		
		//Setup keyboard shortcuts
		
		document.addEvent('keypress', function(event) {
			this.keyPressed.call(this, event);
		}.bind(this));

		this.exit();
	},

	enter: function(photoset_id, image_id) {
	
		this.findDisplayArea();
		
		if (this._active) return this.resizeDisplayImage();
	
		var displayPhoto = this;
		
		function onResize() {
			displayPhoto._area = displayPhoto.findDisplayArea();
			displayPhoto.resizeDisplayImage();
		}

		window.addEvent('resize', onResize);
		
		this.addEvent('exit', function(){
			window.removeEvent('resize', onResize);
		});

		this._active = true;
		
		this._gallery.getAlbumData(photoset_id, function(album) {
			$('albumTitle').innerHTML = album.title;
		});
		
		this.show(photoset_id, image_id);
	},
	
	exit: function() {
		this._preloader.stopAllEvents().flushQueue();
		this._displayPhoto.setStyle('opacity', 0);
		this._active = false;
		this._currentPhoto = '';
		this._currentAlbum = '';
		$('loading').style.display = 'none';
		this.fireEvent('exit');
	},
	
	keyPressed: function(event) {
		if (this._gallery.mode != 'single' && this._gallery.mode != 'filmstrip') return;
		event = new Event(event);

		switch (event.key) {
			case 'up':
			case 'left':
			case 'j':
				return this.previous();
			case 'down':
			case 'right':
			case 'k':
				return this.next();

		}
		// Safari's codes for the arrow keys
		switch (event.code) {
			case 63232:
			case 63234:
				return this.displayPhoto.previous();
			case 63233:
			case 63235:
				return this.displayPhoto.next();
		}
	},
	
	getVariant: function(photoset_id, image_id, callbackFunction) {
		this._gallery.getSizes(photoset_id, image_id, function(variants) {
			var area = this._area;
			var criticalDimension = variants.ratio * area.height > area.width ? 'width' : 'height';		

			var i;
			if (noResize) {
				for (i = variants.length - 1; i > 0; i--)
					if (variants[i][criticalDimension] <= area[criticalDimension])
						break;
			}
			else {
				for (i = 0; i < variants.length - 1; i++)
					if (variants[i][criticalDimension] >= area[criticalDimension])
						break;
			}
			
			// Don't load massive images.		
			if (variants[i].label == 'Original' && (variants[i].width > 1024 || variants[i].height > 1024))
				i--;

			callbackFunction(variants[i].src);
		}.bind(this));
	},

	findDisplayArea: function(){
		var upperBound = this._margin.top;
		var lowerBound = Math.min($('controlBar').getCoordinates().top, $('displayTitle').getCoordinates().top);

		return this._area = {
			width: window.getWidth() - (this._margin.left + this._margin.right) - ($('displayPhoto').getSize().size.x - $E('#displayPhoto img').getSize().size.x),
			height: lowerBound - upperBound - ($('displayPhoto').getSize().size.y - $E('#displayPhoto img').getSize().size.y) - this._margin.bottom,
			offsetTop: upperBound
		}
	},

	resizeDisplayImage: function() {
	
		// Check there's a photo to resize
		if (!this._currentPhoto) return;

		var area = this._area;
		var img = $E('#displayPhoto img');

		this._gallery.getSizes(this._currentAlbum, this._currentPhoto, function(imageData)  {
			img.style.width = Math.min(area.width, area.height / imageData.ratio, img.naturalWidth) + 'px';
			img.style.height = (parseFloat(img.style.width) * imageData.ratio) + 'px';
			$('displayPhoto').style.top = Math.max(area.offsetTop, area.offsetTop + ((area.height - $('displayPhoto').getSize().size.y) / 2)) + 'px';
		});


	},
	
	show: function(photoset_id, image_id) {
		if (this._currentPhoto == image_id) return;
		
		var loading = $('loading');
		
		var displayPhoto = this;
		
		this.getVariant(photoset_id, image_id, function(src) {
			if (!this._preloader.priorityLoadWithCallback(src, switchPhotos))
				loading.style.display = 'block';
		}.bind(this));
		
		function switchPhotos() {
		
			displayPhoto._gallery.getImageData(photoset_id, image_id, function(imageData){
			
				// Preload next/prev image.
				displayPhoto._gallery.getContext(photoset_id, image_id, function(context){
					if (context.next) {
						$('nextPhoto').style.visibility = 'visible';
						displayPhoto.getVariant(photoset_id, context.next, function(src) {
							displayPhoto._preloader.addToQueue(src);
						});
					}
					else
						$('nextPhoto').style.visibility = 'hidden';

					if (context.previous) {
						$('previousPhoto').style.visibility = 'visible';
						displayPhoto.getVariant(photoset_id, context.previous, function(src) {
							displayPhoto._preloader.addToQueue(src);			
						});
					}
					else
						$('previousPhoto').style.visibility = 'hidden';
				});
				
				// In the following code, this == img
				displayPhoto._fadeout.start(0).chain(function(){

					displayPhoto._gallery.getAlbumData(photoset_id, function(album) {
						document.title = album.title + (imageData.title.trim() ? ': ' + imageData.title : '');
					});
					
					// Insert image
					$E('#displayPhoto img').replaceWith(this);
					displayPhoto._currentAlbum = photoset_id;
					displayPhoto._currentPhoto = image_id;

					// Store default width
					if (!this.naturalWidth) {
						this.naturalWidth = this.width;
					}

					// Display title and description
					$('photoTitle').innerHTML = (this.title = imageData.title);
					$('photoDescription').innerHTML = (this.alt = imageData.description);
	
					// Resize and position
					displayPhoto.findDisplayArea();
					displayPhoto.resizeDisplayImage();
	
					// Setup download links
					$$('#download a').each(function(link) {
						switch (link.id) {
							case 'downloadSmall':
								link.href = imageData.srcBase + '_d.jpg';
								return;
							case 'downloadLarge':
								link.href = imageData.srcBase + '_b_d.jpg';
								return;
							case 'downloadOriginal':
								link.href = imageData.originalSrc;
								link.style.display = imageData.originalSrc.test(/undefined/) ? 'none' : '';
								return;
						}
					});
	
					loading.style.display = 'none';
	
					displayPhoto._gallery.fireEvent('photoChange', [photoset_id, image_id]);
					
					displayPhoto._fadein.start(1);
					displayPhoto._gallery.history.addState(displayPhoto._gallery.mode + ':' + photoset_id + ':' + image_id);
	
					
				}.bind(this));
			}.bind(this));
		}
	},
	
	next: function() {
		this.fireEvent('next');
		this._gallery.getContext(this._currentAlbum, this._currentPhoto, function(context) {
			if (!context.next)
				return this._gallery.changeMode('grid', this._currentAlbum);
			this.show(this._currentAlbum, context.next);
		}.bind(this));
	},
	
	previous: function() {
		this.fireEvent('previous');
		this._gallery.getContext(this._currentAlbum, this._currentPhoto, function(context) {
			if (!context.previous)
				this._gallery.changeMode('grid', this._currentAlbum);
			this.show(this._currentAlbum, context.previous);
		}.bind(this));
	}
});

DisplayPhoto.implement(new Events);

var Filmstrip = new Class({

	initialize: function(gallery) {
		this._gallery = gallery;
		this._visible = false;
		this._strip = $('strip');
		a = this._scroll = new Fx.Scroll('strip', {
			duration: 750,
			transition: Fx.Transitions.Quad.easeInOut
		});

		gallery.addEvent('photoChange', function(photoset_id, image_id) {
			this.updateSelected(photoset_id, image_id);
		}.bind(this));
		gallery.addEvent('modeChange', function(mode, photoset_id) {
			if (mode != 'filmstrip') return;
			$$('div[id^=stripContainer]').setStyle('display', 'none');
			if (!$('stripContainer' + photoset_id))
				this.build(photoset_id);
			else
				$('stripContainer' + photoset_id).setStyle('display', 'block');
		}.bind(this));
	},

	build: function(photoset_id) {
		var galleryDiv = new Element('div', {
			id: 'stripContainer' + photoset_id
		}).inject('strip');
		
		var gallery = this._gallery;
		
		this._gallery.getAlbumPhotos(photoset_id, function(photos){
		
			var photo = photos['photo' + photos._firstImage];
		
			do {
				new elementFromTemplate('template_stripThumbnail', {
					$thumbnail_title: photo.title,
					$thumbnail_src: photo.srcBase + "_m.jpg",
					$thumbnail_id: 'stripThumbnail' + photoset_id + photo.id
				}).addEvent('click', function(event) {
					new Event(event).stop();
					this.blur();
					gallery.displayPhoto.show(photoset_id, this.href.split(':')[2]);
				}).inject(galleryDiv).setProperty('href', 'filmstrip:' + photoset_id + ':' + photo.id);
			} while (photo = photos['photo' + photo.next]);
			
			// Resize display image.
			gallery.displayPhoto.findDisplayArea();
			gallery.displayPhoto.resizeDisplayImage();
		});
	},

	updateSelected: function(photoset_id, image_id) {
		if (!$('stripThumbnail' + photoset_id + image_id)) return;
		if (this._currentlySelected) this._currentlySelected.removeClass('selected');
		this._currentlySelected = $('stripThumbnail' + photoset_id + image_id).addClass('selected');
		
		//console.log('currentPos: ' + this._currentlySelected.getPosition().x);
		//console.log('currentSize: ' + this._currentlySelected.getSize().size.x);
		//console.log('border: ' + this._strip.getStyle('border-left-width').toInt());
		//console.log('stripSize: ' + this._strip.getSize().size.x);

		var scrollX = this._currentlySelected.getPosition().x + (this._currentlySelected.getSize().size.x / 2)
					+ this._strip.getStyle('border-left-width').toInt() - (this._strip.getSize().size.x / 2);

		//console.log(scrollX);
		this._scroll.scrollTo(scrollX, false);
	}

});