/**
 * "Serene" full screen gallery plugin
 * 
 * Copyright 2011 ThemeCatcher.net
 * All rights reserved
 */
;(function ($, window) {
	// Serene default settings
	var defaults = {
		speedIn: 3000,		    // Speed of the "fade in" transition between images, in milliseconds 1000 = 1 second
		speedOut: 3000,         // Speed of the "fade out" transition between images
		sync: true,             // If true, both fade animations occur simultaneously, otherwise "fade in" waits for "fade out" to complete
		preload: true,          // Whether or not to preload images
		slideshow: true,        // Whether or not to use the slideshow functionality
		slideshowAuto: true,    // Whether or not to start the slideshow automatically
		slideshowSpeed: 7000,   // How long the slideshow stays on one image, in milliseconds 1000 = 1 second
		keyboard: true,         // Whether or not to use the keyboard controls, left arrow, right arrow and esc key
		onOpen: false,          // Callback when the gallery opens
		onLoad: false,          // Callback when the current image starts loading
		onComplete: false,      // Callback when the current image has completely loaded
		onCleanup: false,       // Callback when cleaning up, just before closing
		onClose: false          // Callback when the gallery closes
	},
	
	// Wrappers & overlay
	$outer,
	$overlay,
	$stage,
	
	// Controls
	$controlsWrap,
	$controls,
	$prev,
	$play,
	$next,
	$loadingWrap,
	$loading,
	$closeWrap,
	$close,
	
	// Images & window
	$image,
	$images,
	$window = $(window),
	
	// Events
	eventOpen = 'sereneOpen',
	eventLoad = 'sereneLoad',
	eventComplete = 'sereneComplete',
	eventCleanup = 'sereneCleanup',
	eventClose = 'sereneClose',

	// Misc
	imageCache = [],
	imageRatio,
	bodyOverflow,
	index = 0,
	total,
	active = false,
	open = false,
	settings,
	serene;
	
	// Cache the images with given indices
	function cache()
	{
		$.each(arguments, function (i, cacheIndex) {
			if (typeof imageCache[cacheIndex] === 'undefined') {
				imageCache[cacheIndex] = document.createElement('img');
				imageCache[cacheIndex].src = $images.eq(cacheIndex).attr('href');
			}
		});
	}
	
	function trigger(event, callback) {
    	if (callback && typeof callback === 'function') {
    		callback.call();
    	}
    	
    	$.event.trigger(event);
    }
	
	// Initialisation
	function init() {
		// Create the div structure
		$outer = $('<div class="serene-outer"></div>').append(
			$overlay = $('<div class="serene-overlay"></div>'),
			$stage = $('<div class="serene-stage"></div>'),
			$controlsWrap = $('<div class="serene-controls-outer"></div>').append(
				$controls = $('<div class="serene-controls"></div>').append(
					$prev = $('<div class="serene-prev"></div>'),
					$play = $('<div class="serene-play"></div>'),
					$next = $('<div class="serene-next"></div>')
				),
				$loadingWrap = $('<div class="serene-loading-wrap"></div>').append(
					$loading = $('<div class="serene-loading"></div>')
				),
				$closeWrap = $('<div class="serene-close-wrap"></div>').append(
					$close = $('<div class="serene-close"></div>')
				)
			)
		);
		
		// Put the controls on the page
		$('body').append($outer);
		
		// Bind the next button to load the next image
		$next.click(function () {
			if (!active) {
				serene.next();
			} else {
				return false;
			}
		});
		
		// Bind the next button to load the next image
		$prev.click(function () {
			if (!active) {
				serene.prev();
			} else {
				return false;
			}
		});
		
		// Bind the close button to close it
		$closeWrap.click(serene.close);
		
		$('body').append($outer);
		bodyOverflow = $('body').css('overflow');
		
		$('.serene-element').live('click', function (e) {
			e.preventDefault();
			trigger(eventOpen, settings.onOpen);
			$overlay.css('opacity', settings.opacity).add($stage).show();
			$('body').css('overflow', 'hidden');
			open = true;
			prepare($(this));
		});
		
		$window.resize(windowResize);
	};
	
	// Prepare the first image to be shown
	function prepare($element)
	{
		$images = $element;
		var rel = $element.attr('rel');
		
		if (rel) {
			$images = $('.serene-element').filter(function () {
				return (this.rel === rel);
			});
			index = $images.index($element);
		}
		
		total = $images.length;
		
		if (total > 1) {
			$controls.show();
			$stage.click(function () { $next.click(); }).css('cursor', 'pointer');
			
			if (settings.keyboard) {
				// Key bindings right arrow, left arrow and esc key
				$(document).bind('keydown.serene', function (e) {
					if (open && e.keyCode === 27) {
						e.preventDefault();
						serene.close();
					}
					if (open && !active && total > 1) {
						if (e.keyCode === 37) {
							e.preventDefault();
							$prev.click();
						} else if (e.keyCode === 39) {
							e.preventDefault();
							$next.click();
						}
					}
				});
			}

			// Slideshow functionality
			if (settings.slideshow) {				
				var timeout,
				start,
				stop;
				
				start = function () {
					$play
						.bind(eventComplete, function () {
							timeout = setTimeout(serene.next, settings.slideshowSpeed);
						})
						.bind(eventLoad, function () {			 
							clearTimeout(timeout);
						})
						.removeClass('serene-play')
						.addClass('serene-pause')
						.unbind('click')
						.one('click ' + eventCleanup, stop);
					
					timeout = setTimeout(serene.next, settings.slideshowSpeed);
				};
				
				stop = function () {
					clearTimeout(timeout);
					$play
						.unbind([eventComplete, eventLoad, eventCleanup, 'click'].join(' '))
						.removeClass('serene-pause')
						.addClass('serene-play')
						.one('click', start);
				};
				
				if (settings.slideshowAuto) {
					start();
				} else {
					stop();
				}
				
				$play.show();
			}
		}		

		// Fade in the controls when the image begins loading
		$controlsWrap.bind(eventLoad, function () {
			$controlsWrap.fadeIn(0).unbind(eventLoad);
		});
				
		// Fade in the first image, then cache one next image and one previous image
		load(function () {			
			if (settings.preload) {
				cache((index == (total - 1)) ? 0 : index + 1, (index == 0) ? total - 1 : index - 1);
			}
		});
	}
	
	// Load the current image
	function load(callback) {
		var image = document.createElement('img'),
		loadingTimeout;
		$image = $(image).css('position', 'fixed');
		$image.load(function () {
			$image.unbind('load');
			setTimeout(function () { // Chrome will sometimes report a 0 by 0 size if there isn't pause in execution
				imageRatio = image.height / image.width;
				var $current = $stage.find('img');
				$stage.append($image);
				windowResize(function () {
					clearTimeout(loadingTimeout);
					$loadingWrap.hide();
					var fn = function () {
						$image.animate({ opacity: 'show' }, {
							duration: settings.speedIn,
							complete: function () {
								active = false;
								
								trigger(eventComplete, settings.onComplete);
								
								if (typeof callback === 'function') {
									callback.call();
								}
							}
						});
					};
					
					if ($current.length) {
						$current.animate({ opacity: 'hide' }, {
							duration: settings.speedOut,
							complete: function () {
								if (!settings.sync) {
									fn();
								}
								$current.remove();
							}
						});
						
						if (settings.sync) {
							fn();
						}
					} else {
						fn();
					}
				});
			}, 1);
		});
		
		loadingTimeout = setTimeout(function () { $loadingWrap.fadeIn(); }, 200);
		trigger(eventLoad, settings.onLoad);
		active = true;
		setTimeout(function () { // Opera 10.6+ will sometimes load the src before the onload function is set, so wait 1ms
			$image.attr('src', $images.eq(index).attr('href'));
		}, 1);
	};
	
	// Resize the current image to set dimensions on window resize
	function windowResize(callback)
	{
		if ($image) {
			var windowWidth = $window.width(),
			windowHeight = $window.height();
						
			if ((windowHeight / windowWidth) > imageRatio) {
				$image.height(windowHeight).width(windowHeight / imageRatio);
			} else {
				$image.width(windowWidth).height(windowWidth * imageRatio);
			}
			
			$image.css({
				left: ((windowWidth - $image.width()) / 2) + 'px',
				top: ((windowHeight - $image.height()) / 2) + 'px'
			});
			
			if (typeof callback === 'function') {
				callback.call();
			}
		}
	}
	
	
	serene = $.fn.serene = function (options) {
		var $this = this;
		
		if (!$this[0] && $this.selector) {
			return $this;
		}
		
		settings = $.extend({}, defaults, options || {});

		return $this.each(function () {
			$(this).addClass('serene-element');
		});
	};
	
	serene.close = function () {
		trigger(eventCleanup, settings.onCleanup);
		
		$('body').css('overflow', bodyOverflow);
		$overlay.add($stage).add($controlsWrap).hide();
		
		var $current = $stage.find('img');
		if ($current.length > 0) {
			$current.remove();
		}
		
		open = false;
		
		if (settings.keyboard) {
			$(document).unbind('keydown.serene');
		}
		
		trigger(eventClose, settings.onClose);
	};
	
	serene.next = function () {
		index = (index == (total - 1)) ? 0 : index + 1;
		load(function () {
			if (settings.preload) {
				cache((index == (total - 1)) ? 0 : index + 1); // Cache the next next image
			}
		});
	};
	
	serene.prev = function () {
		index = (index == 0) ? total - 1 : index - 1;
		load(function () {
			if (settings.preload) {
				cache((index == 0) ? total - 1 : index - 1); // Cache the next previous image
			}
		});
	};
		
	$(document).ready(function () {
		init();
		
		if (typeof window.preload === 'function') {
			window.preload([
			    'assets/js/serene/images/backward.png',
			    'assets/js/serene/images/backward1.png',
			    'assets/js/serene/images/play.png',
			    'assets/js/serene/images/play1.png',
			    'assets/js/serene/images/pause.png',
			    'assets/js/serene/images/pause1.png',
			    'assets/js/serene/images/close.png',
			    'assets/js/serene/images/close1.png',
			    'assets/js/serene/images/forward.png',
			    'assets/js/serene/images/forward1.png',
			    'assets/js/serene/images/opacity-80-rep.png'
			]);
		}
	});
})(jQuery, window);

