/*
 *	Adds spring load functionality to an image gallery
 *	This is a fire and forget script with no further interaction needed
 *	image size has to be SCALE_FACTOR times the thumbnail size
 */

var springLoadedImages = new kwasiPhotoZoom();

function kwasiPhotoZoom() {
	var IMAGE_ARRAY_ID = "imageArray";
	var DURATION = 500;
	var ACTUAL_DURATION = 500;
	var SCALE_FACTOR = 8;
	var MODAL_OPACITY = 3/4;
	var SLIDE_DURATION = 4000;
	var startTime;
	var start = { x:0, y:0, width:0, height:0, padding:2 };
	var end = { x:0, y:0, width:0, height:0, padding:0 };
	var springLoadModal;
	var springLoadDiv;
	var springLoadBack;
	var springLoadFront;
	var springLoadBezel;
	var busy = false;
	var open = false;
	var target = null;
	var slideshowTimer = null;
	var tempImage = null;
	var tempImageProgress = null;
	var zoomImage = new Object();
	var multiTouchStart = { x:0, y:0, fingers:0 };	//iPhoneOS
	var multiTouchEnd = { x:0, y:0, fingers:0 };	//iPhoneOS
	
	kwasiAddEventListener( window, "load", init, false );
	kwasiAddEventListener( window, "resize", resizeWindow, false );
	kwasiAddEventListener( window, "orientationchange", resizeWindow, false );	//iPhoneOS
	
	/*
	 *	creates all items required and attaches events to the images (private)
	 */
	function init() {
		var images_a = document.getElementById( IMAGE_ARRAY_ID );
		
		if ( images_a ) {
			images_a = images_a.getElementsByTagName( "a" );
			createModalTags();
			kwasiAddEventListener( springLoadModal, "click", closeImage, true );
			kwasiAddEventListener( springLoadDiv, "touchstart", touchStart, false );	//iPhoneOS
			kwasiAddEventListener( springLoadDiv, "touchmove", touchMove, false );		//iPhoneOS
			kwasiAddEventListener( springLoadDiv, "touchend", touchEnd, false );		//iPhoneOS
			kwasiAddEventListener( springLoadDiv, "click", closeImage, true );
			kwasiAddEventListener( document, "keydown", keyPressed, false );
			
			for ( var i = 0; i < images_a.length; i++ ) {
				var zoomIMG = new Image();
				zoomIMG.src = images_a[i].getElementsByTagName( "img" )[0].src;
				zoomImage[images_a[i]] = zoomIMG;
				kwasiAddEventListener( images_a[i], "click", clickImage, true );
			}
		}
	}
	
	/*
	 *	click EVENT handler (private)
	 */
	function clickImage( EVENT ) {
		if ( !busy ) {
			ACTUAL_DURATION = kwasiShiftKey( EVENT ) ? 10 * DURATION : DURATION;
			target = kwasiEventTarget( EVENT );
			
			//what was clicked, the image or the anchor. we need the latter
			if ( target.nodeName.match( new RegExp( "img", "i" ) ) ) {
				target = target.parentNode;
			}
			
			try {
				openImage();
			} catch (e) {
				alert(e);
				//debug(e);
			}
		}
	}
	
	/*
	 *	computes the destination dimensions and replaces the low quality image (private)
	 */
	function imageInit() {
		var selectedImage = target.getElementsByTagName( "img" )[0];
		var targetImageSize = Math.max( selectedImage.width, selectedImage.height ) * SCALE_FACTOR;
		var imageBorderWidth = 4 * SCALE_FACTOR / targetImageSize;
		var viewPort = getViewportDimensions();
		var scale = viewPort.width * 0.9 / (selectedImage.width * SCALE_FACTOR + 2 * imageBorderWidth * targetImageSize);
		var scaleY = viewPort.height * 0.9 / (selectedImage.height * SCALE_FACTOR + 2 * imageBorderWidth * targetImageSize);
		scale = Math.min( scaleY, scale, 1 );
		end.width = Math.round( selectedImage.width * SCALE_FACTOR * scale );
		end.height = Math.round( selectedImage.height * SCALE_FACTOR * scale );
		var maximum = Math.max( end.width, end.height );
		end.x = Math.round( ( viewPort.width - end.width ) / 2 + viewPort.left ) - maximum * imageBorderWidth;
		end.y = Math.round( ( viewPort.height - end.height ) / 2 + viewPort.top ) - maximum * imageBorderWidth;
		end.padding = maximum * imageBorderWidth;
		springLoadBack = replaceImg( springLoadBack, zoomImage[target].src );
	}
	
	/*
	 *	computes the source dimensions of the clicked image (private)
	 */
	function startPos() {
		var selectedImage = target.getElementsByTagName( "img" )[0];
		start.x = selectedImage.offsetLeft;
		start.y = selectedImage.offsetTop;
		start.width = selectedImage.width;
		start.height = selectedImage.height;
	}
	
	/*
	 *	prepares the zoom in animation (private)
	 */
	function openImage() {
		startPos();
		imageInit();
		commonZoom( 0 );
		springLoadModal.style.display = "block";
		springLoadDiv.style.display = "block";
		startTime = new Date();
		busy = true;
		setTimeout( zoomIn, 0 );
	}
	
	/*
	 *	zoom in animation (private)
	 */
	function zoomIn() {
		var timeDelta = new Date() - startTime;
		var step = 1 - Math.pow( ( 1 - timeDelta / ACTUAL_DURATION ), 3.0 );
		
		if ( step < 1 ) {
			commonZoom( step );
			setTimeout( zoomIn, 33 );
		} else {
			switchImage();
			busy = false;
			open = true;
		}
	}
	
	/*
	 *	prepares the zoom out animation (private)
	 */
	function closeImage( EVENT ) {
		if ( !busy ) {
			ACTUAL_DURATION = kwasiShiftKey( EVENT ) ? 10 * DURATION : DURATION;
			springLoadBack = replaceImg( springLoadBack, zoomImage[target].src );
			springLoadBack.style.width = end.width + "px";
			springLoadBack.style.height = end.height + "px";
			startPos();	//target position may have changed
			springLoadFront.style.display = "none";
			clearTimeout( slideshowTimer );
			slideshowTimer = null;
			startTime = new Date();
			busy = true;
			open = false;
			setTimeout( zoomOut, 0 );
		}
	}
	
	/*
	 *	zoom out animation (private)
	 */
	function zoomOut() {
		var timeDelta = new Date() - startTime;
		var step = Math.pow( 1 - timeDelta / ACTUAL_DURATION, 3.0 );
		
		if ( step > 0 ) {
			commonZoom( step );
			setTimeout( zoomOut, 33 );
		} else {
			commonZoom( 0 );
			kwasiStyleOpacity( springLoadModal, 0 );
			springLoadModal.style.display = "none";
			springLoadDiv.style.display = "none";
			busy = false;
		}
	}
	
	/*
	 *	common animation steps for zoom in and out at time positon STEP [0,1] (private)
	 */
	function commonZoom( STEP ) {
		var width = ( end.width - start.width ) * STEP + start.width;
		var height = ( end.height - start.height ) * STEP + start.height;
		var maximum = Math.max( width, height );
		kwasiStyleOpacity( springLoadModal, STEP * MODAL_OPACITY );
		springLoadDiv.style.padding = ( ( end.padding - start.padding ) * STEP + start.padding ) + "px";
		springLoadDiv.style.left = ( ( end.x - start.x ) * STEP + start.x ) + "px";
		springLoadDiv.style.top = ( ( end.y - start.y ) * STEP + start.y ) + "px";
		kwasiStyleOpacity( springLoadDiv, STEP );
		springLoadBack.style.width = width + "px";
		springLoadBack.style.height = height + "px";
	}
	
	function bezelAnim( IMG ) {
		springLoadBezel.src = IMG;
		springLoadBezel.style.width = "128px";
		springLoadBezel.style.height = "128px";
		springLoadBezel.style.margin = "-64px 0 0 -64px";
		kwasiStyleOpacity( springLoadBezel, 1 );
		springLoadBezel.style.display = "block";
		startTime = new Date();
		setTimeout( bezelAnimLoop, 0 );
	}
	
	function bezelAnimLoop() {
		var timeDelta = new Date() - startTime;
		var step = 1 - Math.pow( 1 - timeDelta / ACTUAL_DURATION / 2, 3.0 );
		
		if ( step < 1 ) {
			kwasiStyleOpacity( springLoadBezel, Math.min( 1, 2 * ( 1 - step ) ) );
//			springLoadBezel.style.width = 128 * Math.max( 1, ( 2 * step ) ) + "px";
//			springLoadBezel.style.height = 128 * Math.max( 1, ( 2 * step ) ) + "px";
//			springLoadBezel.style.margin = "-" + 64 * Math.max( 1, ( 2 * step ) ) + "px 0 0 -" + 64 * Math.max( 1, ( 2 * step ) ) + "px";
			setTimeout( bezelAnimLoop, 33 );
		} else {
			kwasiStyleOpacity( springLoadBezel, 0 );
			springLoadBezel.style.display = "none";
		}
	}
	
	/*
	 *	replaces the high quality image (private)
	 */
	function switchImage() {
		commonZoom( 1 );
		springLoadFront = replaceImg( springLoadFront, target.href );
		springLoadFront.style.width = end.width + "px";
		springLoadFront.style.height = end.height + "px";
		springLoadFront.style.margin = -end.height + "px 0 0 0";
		
		//we want to zoom in full resolution when the image is cached
		tempImageProgress = new Image();
		kwasiAddEventListener( tempImageProgress, "load", cachedImage, true );	//wait until full image is loaded before continuing
		tempImageProgress.src = target.href;
	}
	
	function cachedImage() {
		zoomImage[target] = tempImageProgress;
		delete tempImageProgress;
	}
	
	function resizeWindow() {
		if ( open ) {
			springLoadModal.style.height = getCanvasDimensions().height + "px";
			nextImage();
		}
	}
	
	/*
	 *	slideshow animation (private)
	 */
	function slideShow() {
		if ( nextImage( "next" ) ) {
			tempImage = new Image();	//this is a workaround for browsers that don't trigger load on cached images O9 IE8
			kwasiAddEventListener( tempImage, "load", startSlideShowTimer, true );	//wait until full image is loaded before continuing
			tempImage.src = target.href;
		} else {
			clearTimeout( slideshowTimer );
			slideshowTimer = null;
		}
	}
	
	/*
	 *	slideshow timer waits until the high quality image has been completely loaded (private)
	 */
	function startSlideShowTimer() {
		delete tempImage;
		slideshowTimer = setTimeout( slideShow, SLIDE_DURATION );
	}
	
	/*
	 *	image navigation in passed DIRECTION (private)
	 */
	function nextImage( DIRECTION ) {
		var newTarget = target;
		
		do {
			if ( DIRECTION == "next" ) {
				newTarget = newTarget.nextSibling;
			} else if ( DIRECTION == "prev" ) {
				newTarget = newTarget.previousSibling;
			}
			
			if ( !newTarget ) {
				return false;
			}
		} while ( newTarget.nodeName != "A" );
		
		target = newTarget;
		imageInit();
		switchImage();
		return true;
	}
	
	/*
	 *	captured key-events EVENT (private)
	 */
	function keyPressed( EVENT ) {
		var keyCode = kwasiKeyCode( EVENT );
		
		if ( open ) {			
			switch ( keyCode ) {
				case 27:	//esc
					closeImage();
					break;
				case 32:	//space
					if ( slideshowTimer ) {
						bezelAnim( "/img/pause.png" );
						clearTimeout( slideshowTimer );
						slideshowTimer = null;
					} else {
						bezelAnim( "/img/play.png" );
						slideshowTimer = setTimeout( slideShow, SLIDE_DURATION );
					}
					
					break;
				case 37:	//left arrow
				case 178:	//Wiimote left
					nextImage( "prev" );
					
					if ( slideshowTimer ) {
						clearTimeout( slideshowTimer );
						slideshowTimer = setTimeout( slideShow, SLIDE_DURATION );
					}
					
					break;
				case 39:	//right arrow
				case 177:	//Wiimote right
					nextImage( "next" );
					
					if ( slideshowTimer ) {
						clearTimeout( slideshowTimer );
						slideshowTimer = setTimeout( slideShow, SLIDE_DURATION );
					}
					
					break;
				default:
					return;
			}
			
			kwasiEventPreventDefault( EVENT );
		}
	}
	
	/*
	 *	capture touchStart EVENT from iPhoneOS and store where touch has started (private)
	 */
	function touchStart( EVENT ) {
		if ( EVENT.touches.length == 1 ) {
			multiTouchStart = { x:EVENT.touches[0].pageX, y:EVENT.touches[0].pageY };
			multiTouchEnd = { x:EVENT.touches[0].pageX, y:EVENT.touches[0].pageY };
			EVENT.preventDefault();
		} else {
			multiTouchStart = { x:-1, y:-1 };
			multiTouchEnd = { x:-1, y:-1 };
		}
	}
	
	/*
	 *	capture touchMove EVENT from iPhoneOS and store where touch has ended (private)
	 */
	function touchMove( EVENT ) {
		if ( EVENT.touches.length == 1 ) {
			multiTouchEnd = { x:EVENT.touches[0].pageX, y:EVENT.touches[0].pageY };
			EVENT.preventDefault();
		}
	}
	
	/*
	 *	capture touchEnd EVENT from iPhoneOS and switch image according to sweep direction (private)
	 */
	function touchEnd( EVENT ) {
		if ( multiTouchStart.x != -1 ) {
			var e = { x:(multiTouchEnd.x - multiTouchStart.x), y:(multiTouchEnd.y - multiTouchStart.y) };
			
			if ( Math.abs( e.x ) > Math.abs( e.y ) ) {
				if ( e.x > 10 ) {
					nextImage( "prev" );
				} else if ( e.x < -10 ) {
					nextImage( "next" );
				} else {
					closeImage();
				}
			} else {
				if ( e.y > 10 ) {
					nextImage( "prev" );
				} else if ( e.y < -10 ) {
					nextImage( "next" );
				} else {
					closeImage();
				}
			}
			
			EVENT.preventDefault();
		}
	}
	
	/*
	 *	creates a new DOM image element with URL (private)
	 */
	function createImgTag( SRC ) {
		var newImg = document.createElement( "img" );
		
		if ( SRC ) {
			var newImgSrc = document.createAttribute( "src" );
			newImgSrc.nodeValue = SRC;
			newImg.setAttributeNode( newImgSrc );
		}
		
		return newImg;
	}
	
	/*
	 *	replaces a DOM image elemtnt with a new with URL (private)
	 */
	function replaceImg( DOM, URL ) {	//quirk fix for some browsers that do not show images loading in progress.
		var tempDOM = createImgTag( URL );
		tempDOM.style.display = "block";
		DOM.parentNode.replaceChild( tempDOM, DOM );
		return tempDOM;
	}
	
	/*
	 *	creates the required DOM elements (private)
	 */
	function createModalTags() {
		springLoadModal = document.createElement( "div" );
		var newDivId = document.createAttribute( "id" );
		newDivId.nodeValue = "kwasiSpringLoadModal";
		springLoadModal.setAttributeNode( newDivId );
		springLoadModal.style.background = "#000";
		springLoadModal.style.display = "none";
		springLoadModal.style.height = getCanvasDimensions().height + "px";
		springLoadModal.style.left = 0;
		springLoadModal.style.position = "absolute";
		springLoadModal.style.top = 0;
		springLoadModal.style.width = "100%";
		springLoadModal.style.zIndex = "998";
		kwasiStyleOpacity( springLoadModal, 0 );
		document.body.appendChild( springLoadModal );
		
		springLoadDiv = document.createElement( "div" );
		newDivId = document.createAttribute( "id" );
		newDivId.nodeValue = "kwasiSpringLoad";
		springLoadDiv.setAttributeNode( newDivId );
		springLoadDiv.style.background = "#fff";
		springLoadDiv.style.display = "none";
		springLoadDiv.style.position = "absolute";
		springLoadDiv.style.zIndex = "999";
		document.body.appendChild( springLoadDiv );
		
		springLoadBack = createImgTag( null );
		springLoadDiv.appendChild( springLoadBack );
		
		springLoadFront = createImgTag( null );
		springLoadFront.style.display = "none";
		springLoadDiv.appendChild( springLoadFront );
		
		springLoadBezel = createImgTag( "/img/play.png" );
		springLoadBezel.style.position = "absolute";
		springLoadBezel.style.top = "50%";
		springLoadBezel.style.left = "50%";
		springLoadBezel.style.margin = "-64px 0 0 -64px";
		springLoadBezel.style.width = "128px";
		springLoadBezel.style.height = "128px";
		springLoadBezel.style.display = "none";
		springLoadDiv.appendChild( springLoadBezel );
	}
}

