/* 
  album.js
  
  Javascript album viewer built on top of Prototype, Scriptaculous and
  Stuart Rackham's popup.
  
  NOTE: The version of popup.js used has a bug fix related to scroll offsets

  Version: 1.0
  Copyright (c) 2008 Peter Nguyen <peter.q.n@gmail.com>
  
  License: This source code is released under the MIT license.
*/
Event.observe(window, 'load', function() {
	AlbumManager.instance = new AlbumManager();
});

var AlbumManager = Class.create({
	initialize: function() {
		this.albums = {};
		
		$$('.Album').each(function(album) {
			this.albums[album.id] = new Album(album);
		}.bind(this));
		
		this.renderBorder();
		
	},
	
	getAlbum: function(albumId) {
		return this.albums[albumId];
	},
	getPhotos: function() {
		return this.currentAlbum.photos;
	},
	countPhotos: function(width) {
		if (width<0)
			return 0;
		
		var count = 0;
		var x = 0;
		var photos = this.getPhotos();
		for (var i=0; i<photos.length; i++) {
			x += photos[i].thumbnail.width + 4;
			if (x > width) {
				break;
			}
			count++;
		}
		
		return count;
	},
	countLeftPhotos: function() {
		return this.countPhotos(-this.currentStrip.offsetLeft);
	},
	countRightPhotos: function() {
		return this.getPhotos().length - this.countPhotos(this.stripCell.getWidth()-this.currentStrip.offsetLeft);
	},
	clickBackground: function() {
		this.closeAlbum();
	},
	closeAlbum: function() {
		Event.stopObserving(Popup.overlay, 'click', this.clickBackground);
		
		this.navBar.hide();
		
		$('photoAlbum').popup.hide();
	},
	viewAlbum: function(album) {
		this.currentAlbum = album;
		this.currentStrip = album.getStrip();
		
		this.stripCell.innerHTML = '';
		this.stripCell.appendChild(this.currentStrip);
		
		this.showPhoto(album.photos[0]);
		
		$('photoAlbum').popup.show();
		
		this.update();
		
		Event.observe(Popup.overlay, 'click', this.clickBackground = this.clickBackground.bind(this));
		
		new Draggable(this.currentStrip.id, {
			constraint:'horizontal',
			starteffect: null,
			endeffect: null,
			onStart: function(draggable, event) {
				this.dragging = true;
				this.startOffset = this.currentStrip.offsetLeft;
				
				Event.stop(event);
			}.bind(this),
			onEnd: function(draggable, event) {
				Event.stop(event);
				
				var x = this.currentStrip.offsetLeft;
				
				if (Math.abs(this.startOffset - x) < 5) {
					this.dragging = false; // forgive slight accidental dragging
				} else {
					setTimeout(function() {
						this.dragging = false;
					}.bind(this), 100);
				}
				
				this.leftIndicator.setStyle({visibility:'hidden'});
				this.rightIndicator.setStyle({visibility:'hidden'});
				
				this.boundaryCorrection();
				
				this.update();
			}.bind(this),
			onDrag: this.updateIndicator.bind(this)
		});
	},
	scroll: function(right) {
		var photos = this.getPhotos();

		var offset = -this.currentStrip.offsetLeft;
		var fullWidth = this.currentStrip.getWidth();
		var viewWidth = this.stripCell.getWidth();
		
		var width = offset;
		
		if (right) {
			width += viewWidth;
			
			if (width > fullWidth - viewWidth) {
				this.scrollStrip(viewWidth - fullWidth);
				return;
			}
		} else {
			if (width < viewWidth) {
				this.scrollStrip(0);
				return;
			}
		}
		
		var x = 0;
		for (var i=0; i<photos.length; i++) {
			var x2 = x + photos[i].thumbnail.width + 4;
			if (right) {
				if (x2 > width) {
					this.scrollStrip(-x-2);
					return;
				}
			} else {
				if (x2 + 10 > width) {
					this.scrollStrip(-x2-2+viewWidth);
					return;
				}
			}
			x = x2;
		}
	},
	rightScroll: function() {
		this.scroll(true);
	},
	leftScroll: function() {
		this.scroll(false);
	},
	scrollStrip: function(x) {
		new Effect.Move(this.currentStrip, {x : x, mode: 'absolute', afterFinish: this.update.bind(this)});
	},
	boundaryCorrection: function() {
		var x = this.currentStrip.offsetLeft;
		var viewWidth = this.stripCell.getWidth();
		var fullWidth = this.currentStrip.getWidth();
		var x2 = x + fullWidth;
		if (fullWidth > viewWidth && x2 < viewWidth) {
			this.scrollStrip(viewWidth - fullWidth);
		} else if (x > 0) {
			this.scrollStrip(0);
		}
	},
	showIndicator: function(show) {
		if (show) {
			this.updateIndicator();
		} else {
			this.leftIndicator.setStyle({visibility:'hidden'});
			this.rightIndicator.setStyle({visibility:'hidden'});
		}
	},
	update: function() {
		this.updateButtons();
		
		if (this.isIndicatorShowing())
			this.updateIndicator();

	},
	isIndicatorShowing: function() {
		return this.leftIndicator.style.visibility=='visible' || this.rightIndicator.style.visibility=='visible';
	},
	updateIndicator: function() {
		var left = this.countLeftPhotos();
		var right = this.countRightPhotos();
				
		var pos = this.stripCell.cumulativeOffset();
		var y = pos.top - 25;
		if (left==0) {
			this.leftIndicator.setStyle({visibility:'hidden'});
		} else {
			this.leftIndicator.setStyle({visibility:'visible'});
			$('leftCounter').innerHTML = left;
			
			this.leftIndicator.setStyle({left:pos.left, top:y});
		}
		
				
		if (right==0) {
			this.rightIndicator.setStyle({visibility:'hidden'});
		} else {
			this.rightIndicator.setStyle({visibility:'visible'});
			$('rightCounter').innerHTML = right;
			
			var x = pos.left + this.stripCell.getWidth() - this.rightIndicator.getWidth();
			this.rightIndicator.setStyle({left:x, top:y});
		}
	},
	updateButtons: function() {
		this.leftButton.setEnabled(this.countLeftPhotos()>0);
		this.rightButton.setEnabled(this.countRightPhotos()>0);
	},
	showPhoto: function(photo) {
		if (!this.dragging) {
			this.photoCell.innerHTML = '';
			this.photoCell.appendChild(photo.getImage());
			
			if (this.currentPhoto) {
				$(this.currentPhoto.thumbnail).setStyle({border:0});
			}
			
			this.currentPhoto = photo;
			$(this.currentPhoto.thumbnail).setStyle({border:'1px solid #FFFFFF'});
			
			$('navBarPage').innerHTML = (photo.index + 1) + ' / ' + this.getPhotos().length;
		}
	},
	firstPhoto: function() {
		this.showPhoto(this.getPhotos()[0]);
	},
	previousPhoto: function() {
		if (this.currentPhoto) {
			var index = this.currentPhoto.index;
			if (index > 0) {
				this.showPhoto(this.getPhotos()[index - 1]);
			}
		}
	},
	nextPhoto: function() {
		if (this.currentPhoto) {
			var index = this.currentPhoto.index;
			var photos = this.getPhotos();
			if (index < photos.length-1) {
				this.showPhoto(photos[index + 1]);
			}
		}
	},
	lastPhoto: function() {
		var photos = this.getPhotos();
		this.showPhoto(photos[photos.length-1]);
	},
	renderBorder: function() {
		var table = Html.create('table', {id: 'photoAlbum', cellPadding:0, cellSpacing:0}, {position: 'absolute'});
		
		var titleRow = table.insertRow(-1);
		titleRow.style.height = 33;
		titleRow.className = 'popup_draghandle';
		
		var NW = Html.insertCell(titleRow, {width: 16});
		NW.appendChild(Html.createImage(AlbumManager.getImage('NW.gif')));
		
		var title = Html.insertCell(titleRow, {backgroundImage:'url(' + AlbumManager.getImage('N.gif') + ')'});
		
		var NE = Html.insertCell(titleRow, {width:16});
		NE.appendChild(Html.createImage(AlbumManager.getImage('NE.gif')));
		
		var photoRow = table.insertRow(-1);

		Html.insertCell(photoRow, {backgroundImage:'url(' + AlbumManager.getImage('W.gif') + ')'});
		
		this.photoCell = Html.insertCell(photoRow, {width:640, height: 480, background:'#000000', textAlign:'center'})
		this.photoCell.onmouseover = function() {
			if (this.navBarTimer) {
				clearTimeout(this.navBarTimer);
				this.navBarTimer = null;
			}
			
			var offset = this.photoCell.cumulativeOffset();
			var x = offset.left + (this.photoCell.getWidth() - this.navBar.getWidth())/2;
			var y = offset.top + 450;
			
			this.navBar.setStyle({left:x, top: y});
			if (this.navBarEffect)
				this.navBarEffect.cancel();
			
			this.navBar.appear();
		}.bind(this);
		
		this.photoCell.onmouseout = function() {
			this.navBarTimer = setTimeout(function() {
				if (!this.navBarActive) {
					this.navBarEffect = new Effect.Fade(this.navBar, {
						afterFinish:function() {
							this.navBarEffect = null;
						}.bind(this)
					});
				}
			}.bind(this), 500);
		}.bind(this);
		
		this.renderNav();
		
		
		Html.insertCell(photoRow, {backgroundImage:'url(' + AlbumManager.getImage('E.gif') + ')'});
		
		var stripRow = table.insertRow(-1);
		
		var stripW = Html.insertCell(stripRow, {backgroundImage:'url(' + AlbumManager.getImage('W.gif') + ')'});
		
		var stripTbl = Html.create('table', {id: 'photoAlbum', cellPadding:0, cellSpacing:0}, {background: '#000000'});
		
		var stripTblRow = stripTbl.insertRow(-1);

		// left scroller
		this.leftButton = new Button(stripTblRow.insertCell(-1), {
			image: AlbumManager.getImage('left2.gif'),
			hoverImage: AlbumManager.getImage('left.gif'),
			onClick: this.leftScroll.bind(this)
		});

		this.leftButton.addListener('mouseover', function() {
			this.showIndicator(true);
		}.bind(this));
		this.leftButton.addListener('mouseout', function() {
			this.showIndicator(false);
		}.bind(this));
		
		var div = Html.create('div', null, {background:'#000000', width: 600, overflow:'hidden'});
		
		stripTblRow.insertCell(-1).appendChild(div);

		// right scroller		
		this.rightButton = new Button(stripTblRow.insertCell(-1), {
			image: AlbumManager.getImage('right2.gif'),
			hoverImage: AlbumManager.getImage('right.gif'),
			onClick: this.rightScroll.bind(this)
		});

		this.rightButton.addListener('mouseover', function() {
			this.showIndicator(true);
		}.bind(this));
		this.rightButton.addListener('mouseout', function() {
			this.showIndicator(false);
		}.bind(this));
		
		stripRow.insertCell(-1).appendChild(stripTbl);
		
		this.stripCell = div;
		
		Html.insertCell(stripRow, {backgroundImage:'url(' + AlbumManager.getImage('E.gif') + ')'});
		
		var bottomRow = table.insertRow(-1);
		bottomRow.style.height = 23;
		
		var SW = Html.insertCell(bottomRow, {width:16});
		SW.appendChild(Html.createImage(AlbumManager.getImage('SW.gif')));
		
		var S = Html.insertCell(bottomRow, {backgroundImage:'url(' + AlbumManager.getImage('S.gif') + ')', textAlign:'center'});
		
		var handle = new Image();
		handle.src = AlbumManager.getImage('handle.gif');
		handle.title = 'Close';
		handle.style.cursor = 'pointer';
		handle.className = 'popup_closebox';
		/*
		handle.onclick = function() {
			//this.albumViewer.popup.hide();
			new Effect.BlindUp(this.albumViewer);
		}.bind(this);
		*/
		
		S.appendChild(handle);
		/*
		var close = document.createElement('span');
		close.style.display = 'none';
		close.className = 'popup_closebox';
		S.appendChild(close);
		*/
		
		var SE = bottomRow.insertCell(-1);
		SE.style.width = 16;
		SE.appendChild(Html.createImage(AlbumManager.getImage('SE.gif')));
		$(document.body).insert({top: table});
	

		// left counter
		this.leftIndicator = Html.create('table',
			{cellPadding:0, cellSpacing:0},
			{position: 'absolute', visibility: 'hidden', zIndex:9999});
		
		var row = this.leftIndicator.insertRow(-1);
		row.height = 20;
		
		var cell = row.insertCell(-1);
		cell.width = 14;
		cell.appendChild(Html.createImage(AlbumManager.getImage('leftpoint.gif')));
		
		cell = $(row.insertCell(-1));
		cell.id = 'leftCounter';
		cell.setStyle({backgroundImage:'url(' + AlbumManager.getImage('arrowbg.gif') + ')', backgroundRepeat:'repeat-x'});
		
		cell = row.insertCell(-1);
		cell.width = 3;
		cell.appendChild(Html.createImage(AlbumManager.getImage('leftend.gif')));
		
		$(document.body).appendChild(this.leftIndicator);
		
		// right counter
		this.rightIndicator = Html.create('table',
			{cellPadding:0, cellSpacing:0},
			{position: 'absolute', visibility: 'hidden', zIndex:9999});
			
		row = this.rightIndicator.insertRow(-1);
		row.height = 20;
		
		var cell = row.insertCell(-1);
		cell.width = 3;
		cell.appendChild(Html.createImage(AlbumManager.getImage('rightend.gif')));
		
		cell = $(row.insertCell(-1));
		cell.id = 'rightCounter';
		cell.setStyle({backgroundImage:'url(' + AlbumManager.getImage('arrowbg.gif') + ')', backgroundRepeat:'repeat-x'});
		
		cell = row.insertCell(-1);
		cell.width = 14;
		cell.appendChild(Html.createImage(AlbumManager.getImage('rightpoint.gif')));
		
		$(document.body).appendChild(this.rightIndicator);
		
		this.albumViewer = table;
		
		new Popup('photoAlbum', null, {modal:true, show_delay:0, hide_delay:0, effect: 'blind'});
	},
	renderNav: function() {
		var navBar = Html.create('table',
			{id:'navBar', cellPadding:0, cellSpacing:0},
			{position: 'absolute', zIndex:9999, display:'none'});
		
		var style = {backgroundImage:'url(' + AlbumManager.getImage('nav/Mid.gif') + ')', paddingLeft:10, paddingRight:10, cursor:'pointer'};
		
		var row = navBar.insertRow(-1);
		Html.insertCell(row).appendChild(Html.createImage(AlbumManager.getImage('nav/W.gif')));
		
		var cell = Html.insertCell(row, style);
		cell.onclick = this.firstPhoto.bind(this);
		cell.appendChild(Html.createImage(AlbumManager.getImage('nav/First.gif')));
		
		Html.insertCell(row).appendChild(Html.createImage(AlbumManager.getImage('nav/Sep.gif')));
		
		cell = Html.insertCell(row, style);
		cell.onclick = this.previousPhoto.bind(this);
		cell.appendChild(Html.createImage(AlbumManager.getImage('nav/Prev.gif')));
		
		Html.insertCell(row).appendChild(Html.createImage(AlbumManager.getImage('nav/Sep.gif')));
		
		cell = Html.insertCell(row, {backgroundImage:'url(' + AlbumManager.getImage('nav/Mid.gif') + ')', paddingLeft:10, paddingRight:10, color:'#FFFFFF'});
		cell.id = 'navBarPage';
		
		
		Html.insertCell(row).appendChild(Html.createImage(AlbumManager.getImage('nav/Sep.gif')));
		
		cell = Html.insertCell(row, style);
		cell.onclick = this.nextPhoto.bind(this);
		cell.appendChild(Html.createImage(AlbumManager.getImage('nav/Next.gif')));
		
		Html.insertCell(row).appendChild(Html.createImage(AlbumManager.getImage('nav/Sep.gif')));
		
		cell = Html.insertCell(row, style);
		cell.onclick = this.lastPhoto.bind(this);
		cell.appendChild(Html.createImage(AlbumManager.getImage('nav/Last.gif')));
			
		Html.insertCell(row).appendChild(Html.createImage(AlbumManager.getImage('nav/E.gif')));
		
		navBar.onmouseover = function() {
			this.navBarActive = true;
		}.bind(this);
		
		navBar.onmouseout = function() {
			this.navBarActive = false;
		}.bind(this);
		
		this.navBar = navBar;
		
		$(document.body).appendChild(this.navBar);
		
		return navBar;
	}
});

AlbumManager.getImage = function(image) {
	return 'scripts/album/images/' + image;
}

var Album = Class.create({
	initialize: function(container) {
		this.container = $(container);
		this.src = this.container.getAttribute('album');
		
		this.container.setStyle({cursor:'pointer'});
		
		if (eval(this.container.getAttribute('filmstrip'))) {
			var thumbnail = new Image();
			thumbnail.src = this.container.getAttribute('thumbnail');
			
			var strip = this.createStrip(this.getTrigger(thumbnail));
			
			this.container.appendChild(strip);
		}
		
		this.container.onclick = function() {
			if (!this.photos) {
				new Ajax.Request(this.src, {
					method: 'get',
					asynchronous: false,
					onSuccess: this.load.bind(this)
				});
			}

			AlbumManager.instance.viewAlbum(this);
		}.bind(this);
	},
	load: function(transport) {
		this.photos = [];
		
		var json = transport.responseText.evalJSON();
		
		this.title = json.title;
		this.description = json.description;
		
		var base = this.src.substring(0, this.src.lastIndexOf('/') + 1);
		
		for (var i=0; i<json.photos.length; i++) {
			this.photos.push(new Photo(i, base, json.photos[i], json.debug));
		}
	},
	getStrip: function() {
		return this.createStrip(this.getThumbnails());
	},
	createStrip: function(content) {
		var strip = Html.create('table',
			{id: this.src, cellPadding:0, cellSpacing:0},
			{position: 'relative'});
		
		var top = strip.insertRow(-1);
		top.style.height = 15;
		
		Html.insertCell(top, {backgroundImage:'url(' + AlbumManager.getImage('striptop.gif') + ')'});
		
		var mid = strip.insertRow(-1);
		
		var midTD = Html.insertCell(mid, {background:'#000000'});
		midTD.appendChild(content);
		
		var bottom = strip.insertRow(-1);
		bottom.style.height = 15;
		
		Html.insertCell(bottom, {backgroundImage:'url(' + AlbumManager.getImage('stripbottom.gif') + ')'});
		
		return strip;
	},
	getTrigger: function(image) {
		var table = Html.create('table', {cellPadding:0, cellSpacing:0});
		
		var row = table.insertRow(-1);
		
		var td = row.insertCell(-1);
		td.style.padding = "0 2 0 2";
		
		image.style.cursor = 'pointer';
		td.appendChild(image);
		
		return table;
	},
	getThumbnails: function() {
		var table = Html.create('table', {cellPadding:0, cellSpacing:0});
		
		var row = table.insertRow(-1);
		
		for (var i=0; i<this.photos.length; i++) {
			var thumbnail = this.photos[i].thumbnail;
			
			var td = row.insertCell(-1);
			td.style.padding = "0 2 0 2";
			
			thumbnail.style.cursor = 'pointer';
			
			var photo = this.photos[i];
			new function() {
				var myphoto = photo;
				thumbnail.onclick = function() {
					AlbumManager.instance.showPhoto(myphoto);
				}
			}();
			td.appendChild(thumbnail);
		}
		
		return table;
	}
});

var Photo = Class.create({
	initialize: function(index, base, options, debug) {
		this.index = index;
		this.thumbnail = new Image();
		this.thumbnail.src = base + options.thumbnail;
		this.thumbnail.type = 'thumbnail';
		
		if (index==0) {
			this.image = new Image();
			this.image.src = base + options.image;
		} else {
			this.imageURL = base + options.image;
		}
		
		this.description = options.description;
		
		if (debug) {
			this.thumbnail.title = '(' + options.thumbnail + ')' + options.description;
		} else {
			this.thumbnail.title = options.description;
		}
	},
	getImage: function() {
		if (!this.image) {
			this.image = new Image();
			this.image.src = this.imageURL;
		}
		return this.image;
	}
});

var Button = Class.create({
	initialize: function(container, options) {
		this.options = options;
		this.container = $(container);
		
		container.style.textAlign = 'center';
		container.style.cursor = 'pointer';
		container.width = 20;
		
		this.image = Html.createImage(options.image);
		container.appendChild(this.image);
		
		this.hoverImage = Html.createImage(options.hoverImage);
		
		this.enabled = true;
		
		this.addListener('click', this.onMouseClick.bindAsEventListener(this));
		
		if (options.hoverImage) {
			this.addListener('mouseover', this.onMouseOver.bindAsEventListener(this));
			this.addListener('mouseout', this.onMouseOut.bindAsEventListener(this));
			this.addListener('mousedown', this.onMouseDown.bindAsEventListener(this));
			this.addListener('mouseup', this.onMouseUp.bindAsEventListener(this));
		}
	},
	setEnabled: function(enabled) {
		this.enabled = enabled;
		
		if (!enabled) {
			this.changeImage(false);
		}
	},
	onMouseOver: function(event) {
		if (this.enabled && this.options.hoverImage) {
			this.changeImage(true);
		}
	},
	onMouseOut: function(event) {
		if (this.options.hoverImage) {
			this.changeImage(false);
		}
	},
	onMouseDown: function(event) {
		if (this.enabled && this.options.hoverImage) {
			this.changeImage(false);
		}
	},
	onMouseUp: function(event) {
		if (this.enabled && this.options.hoverImage) {
			this.changeImage(true);
		}
	},
	onMouseClick: function(event) {
		if (this.enabled && this.options.onClick) {
			this.options.onClick();
		}
	},
	changeImage: function(active) {
		this.image.src = active ? this.options.hoverImage : this.options.image;
		this.container.style.cursor = active ? 'pointer' : 'auto';
	},
	addListener: function(event, func) {
		Event.observe(this.container, event, func);
	},
	removeListener: function(event, func) {
		Event.stopObserving(this.container, event, func);
	}
});

var Html = {
	create: function(type) {
		var elem = $(document.createElement(type));
		
		if (arguments.length > 1) {
			if (arguments[1])
				Object.extend(elem, arguments[1]);
		
			if (arguments.length > 2)
				elem.setStyle(arguments[2]);
		}
		
		return elem;
	},
	createImage: function(src) {
		var img = new Image();
		img.src = src;
		return img;
	},
	isChild: function(c, p) {
		while (c) {
			if (c==p)
				return true;
			c = c.parentNode;
		}
		return false;
	},
	insertCell: function(row, style) {
		var cell = $(row.insertCell(-1));
		if (style)
			cell.setStyle(style);
		return cell;
	},
	applyAttr: function(elem, attr) {
		Object.extend(elem, attr);
		return elem;
	},
	applyStyle: function(elem, style) {
		elem.setStyle(style);
		return elem;
	},
	applyEvents: function(elem, events) {
		for (var eventId in events)
			Event.observe(elem, eventId, events[eventId]);
		
		return elem;
	}
}
