/*
* Exposure
* Version 0.1 
* Copyright (c) 2010 Kristoffer Jelbring
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
(function($) {
	
	// TODO: Add code comments
	
	// TODO: Handle history
	// TODO: Slideshow
	
	// TODO: Cross-fade effect
	// TODO: Better 
	
	// TODO: Mobile support
	
	// TODO: Full screen option
	
	$$ = $.fn.exposure = function($args) {

		var $defaults = {
			target : '#exposure',
			showControls : true,
			controlsTarget : null,
			onThumb : function(thumb) {
				$.exposure.thumbEffects(thumb);	
			},
			onImage : function(image, imageData, thumb) {
				$.exposure.imageEffects(image, imageData, thumb);	
			},
			onNext : function() {},
			onPrev : function() {},
			onPageChanged : function() {},
			onEndOfLoop : function() {},
			pageSize : 5,
			keyboardNavigation : true,
			onKeyDown : function() { return true; },
			loop : true
		};
	
		var opts = $.extend($defaults, $args);
		for (var i in opts) {
			$.exposure[i]  = opts[i];
		}
		
		var wrapper = $('<div class="exposureWrapper"></div>');
		var dataContainer = $('<div class="exposureData"><div class="caption"></div><div class="extra"></div></div>');
		
		var target = $(opts.target).addClass('exposureTarget').append(wrapper).append(dataContainer);
		
		// Don't show controls if there is no controls target.
		if (!$.exposure.controlsTarget) {
			$.exposure.showControls = false;
		}
		
		// TODO: Properly position and design buttons	
		if ($.exposure.showControls) {
			var prevButton = $('<a class="exposurePrevPage" href="javascript:void(0);">Prev</a>').click($.exposure.prevPage);
			var nextButton = $('<a class="exposureNextPage" href="javascript:void(0);">Next</a>').click($.exposure.nextPage);
			
			// TODO: Optional last/first pages
			
			// TODO: Optional paging
			
			$($.exposure.controlsTarget).append(prevButton).append(nextButton);
		}
		
		// Bind keys for navigation (using Hotkeys Plugin)
		if ($.exposure.keyboardNavigation) {
			$(document).bind('keydown', 'left', function() { 
				if ($.exposure.onKeyDown()) {
					$.exposure.prevImage();
				}
			});
			$(document).bind('keydown', 'right', function() {
				if ($.exposure.onKeyDown()) {
					$.exposure.nextImage();
				}
			});
			$(document).bind('keydown', 'ctrl+left', function() {
				if ($.exposure.onKeyDown()) {
					$.exposure.prevPage();
				}
			});
			$(document).bind('keydown', 'ctrl+right', function() {
				if ($.exposure.onKeyDown()) {
					$.exposure.nextPage();
				}
			});
		}
		
		return this.addClass('exposureThumbs').each(function(){
			
			$(this).children('li').each(function() {
				var a = $(this).find('a')
				
				var src = a.attr('href');
				var thumbSrc = a.attr('rel');
				var caption = a.attr('title');
				var thumbData = a.html();
				
				var image = new $$.Image(src, thumbSrc, caption, thumbData);
				$.exposure.images.push(image);	
				
				$(this).remove();
				
				$.exposure.loadQueue.push(src);
			});
			
			$.exposure.preloadImage($.exposure.nextInLoadQueue());
			
			$.exposure.goToPage(1);
		});
	}
	
	$$.Image = function(source, thumbSource, caption, imageData) {
		this.source = source;
		this.thumbSource = thumbSource;
		this.caption = caption;
		this.imageData = imageData;
	}
	
	$$.findNextImage = function(selector) {
		if ($(selector).is(':last-child')) {
			if ($.exposure.loop) {
				return $(selector).siblings(':first-child');
			}
			return null;
		}
		return $(selector).next();  
	}
	$$.findPrevImage = function(selector) {
		return $(selector).is(':first-child') ? null : $(selector).prev();	    	   
	}
	
	$.extend({exposure :
		{
			createThumbForImage : function(image, pos) {
				var thumb = $.exposure.getThumb(image.source);
				if (!thumb.length) {				
					var container = $('<li></li>');
					
					$('.exposureThumbs').append(container);
					
					thumb = $('<img />').attr('src', image.thumbSource);
					if ($(this).hasClass('active')) {
						thumb.addClass('selected');	
					}
					container.append(thumb.css('display', 'block'));					
					
					thumb.attr('rel', image.source).attr('title', image.caption);
					thumb.data('data', image.imageData);
					
					thumb.click(function() {
						$.exposure.selectImage(image.source);
					});
					
					thumb.load(function() {
						var imageHeight = $(this).height();
						if (imageHeight > 0) {
							$(this).parent().height(imageHeight);
						}						
					});
					
					$.exposure.onThumb(thumb);
					
					return container;
				}
				return null;
			},
			numberOfPages : function() {
				return Math.ceil($.exposure.images.length / $.exposure.pageSize);
			},
			isAtFirstPage : function() {
				return $.exposure.currentPage == 1;
			},
			prevPage : function() {
				if (!$.exposure.isAtFirstPage()) {
					$.exposure.goToPage($.exposure.currentPage-1);
				}	
			},
			isAtLastPage : function() {
				return $.exposure.currentPage == $.exposure.numberOfPages();
			},
			nextPage : function() {
				if (!$.exposure.isAtLastPage()) {
					$.exposure.goToPage($.exposure.currentPage+1);
				}	
			},
			isValidPage : function(page) {
				return page > 0 && page <= $.exposure.numberOfPages();
			},
			goToPage : function(page) {
				if ($.exposure.isValidPage(page)) {
					$('.exposureThumbs li').hide();
					
					$.exposure.loadPage(page, true);
					
					$.exposure.currentPage = page;
					
					if ($.exposure.isAtFirstPage()) {
						$('.exposurePrevPage').hide();
					} else {
						$('.exposurePrevPage').show();
					}
					if ($.exposure.isAtLastPage()) {
						$('.exposureNextPage').hide();
					} else {
						$('.exposureNextPage').show();
					}
					
					$.exposure.onPageChanged();
				}
			},
			loadPage : function(page, showPage) {
				if ($.exposure.isValidPage(page)) {
					// Before creating this page make sure that all pages before it has been created first.
					if (page > 1) {
						$.exposure.loadPage(page-1);
					}
						
					var pageExists = page <= $.exposure.maxCreatedPage;
					
					var last = page * $.exposure.pageSize;
					var first = last - $.exposure.pageSize;
					
					if (showPage && pageExists) {
						if (page > $.exposure.currentPage) {
							// Set the first image on this page as active.					
							var firstImage = $.exposure.images[first];
							$.exposure.selectImage(firstImage.source, firstImage.caption, firstImage.imageData);
						} else {
							// We're moving backwards, set the last image on the page as active.
							var lastImage = $.exposure.images[last-1];
							$.exposure.selectImage(lastImage.source, lastImage.caption, lastImage.imageData);
						}
					}
						
					if (showPage || !pageExists) {
						var container = null;
						
						// TODO: Add nice fade effect for thumbs.
						for (var i = first; i < last && i < $.exposure.images.length; i++) {
							var image = $.exposure.images[i];
							container = pageExists ? $.exposure.getThumb(image.source).parent() : $.exposure.createThumbForImage(image, i);
							if (container) {
								if (i == first && !pageExists) {
									container.addClass('first');
									$.exposure.selectImage(image.source, image.caption, image.imageData);
								}
								if (showPage) {
									container.show();
								}
							}
						}
						if (container && !pageExists) {
							container.addClass('last');	
						}
						
						if ($.exposure.maxCreatedPage < page) {
							$.exposure.maxCreatedPage = page;	
						}
					}
				}
			},
			maxCreatedPage : -1,
			currentPage : 1,
			current : '',
			images : new Array(),
			loadQueue : new Array(),
			loaded : new Array(),
			nextInLoadQueue : function() {
				if ($.exposure.loadQueue.length > 0) {
					var next = $.exposure.loadQueue.shift();
					if ($.exposure.isLoaded(next)) {
						return $.exposure.nextInLoadQueue();
					}
					return next;
				}
				return null;
			},
			addToQueueAndLoadNext : function(image) {
				if (!$.exposure.isLoaded(image) && !$.exposure.isQueued(image)) {
					$.exposure.loadQueue.push(image)
				}
				if ($.exposure.loadQueue.length > 0) {
					$.exposure.preloadImage($.exposure.nextInLoadQueue());
				}
			},
			isQueued : function(src) {
				return $.inArray(src, $.exposure.loadQueue) > -1;	
			},
			isLoaded : function(src) {
				return $.inArray(src, $.exposure.loaded) > -1;	
			},
			preloadImage : function(src) {
				if (src) {
					return $.exposure.loadImage(src, function() {				
						if ($.exposure.loadQueue.length > 0) {
							$.exposure.preloadImage($.exposure.nextInLoadQueue());
						}
					});
				}
				return null;
			},
			loadImage : function(src, onload) {
				$.exposure.loaded.push(src);
				var img = $('<img />');
				if (typeof onload == 'function') {
					img.load(onload);
				}
				return img.attr('src', src);
			},
			getThumb : function(src) {
				return $('.exposureThumbs img[rel="'+src+'"]');
			},
			getCurrentThumb : function() {
				return $.exposure.getThumb($.exposure.current);	
			},
			getNextImage : function() {
				var next = $$.findNextImage($.exposure.getCurrentThumb().parent());
				if (next) {
					return next.find('img');
				}
				return null;
			},
			getPrevImage : function() {
				var prev = $$.findPrevImage($.exposure.getCurrentThumb().parent());
				if (prev) {
					return prev.find('img');	
				}
				return null;
			},
			nextImage : function() {
				if ($.exposure.getCurrentThumb().parent().hasClass('last')) {
					if ($.exposure.isAtLastPage()) {
						// Set current page to 0 to make sure that the first image on the first page is selected.
						$.exposure.currentPage = 0;
						$.exposure.goToPage(1);
					} else {
						$.exposure.goToPage($.exposure.currentPage+1);
					}
					$.exposure.onNext();
				} else {
					var next = $.exposure.getNextImage();
					if (next) {
						$.exposure.selectImage(next.attr('rel'));
						$.exposure.onNext();	
					}
				}
				var nextNext = $.exposure.getNextImage();
				if (nextNext) {
					$.exposure.addToQueueAndLoadNext(nextNext.attr('rel'));
				}
			},
			prevImage : function() {
				if ($.exposure.getCurrentThumb().parent().hasClass('first')) {
					$.exposure.goToPage($.exposure.currentPage-1);
					$.exposure.onPrev();
				} else {
					var prev = $.exposure.getPrevImage();
					if (prev) {
						$.exposure.selectImage(prev.attr('rel'));
						$.exposure.onPrev();
					}
				}
				var prevPrev = $.exposure.getPrevImage();
				if (prevPrev) {
					$.exposure.addToQueueAndLoadNext(prevPrev.attr('rel'));
				}
			},
			selectImage : function(src, caption, extraImageData) {
				var wrapper = $('.exposureWrapper');				
				if (src) {
					var thumb = $('.exposureThumbs img[rel="' + src + '"]');
					
					if (thumb.length) {
						thumb.parent().siblings().removeClass('active');
						thumb.parent().addClass('active');
					
						wrapper.parent().removeClass('exposureLoaded');
						if ($.exposure.isLoaded(src)) {
							wrapper.parent().addClass('exposureLoaded');
						}
					
						var img = $.exposure.loadImage(src, function() {			
							wrapper.empty().append($(this));
							$(this).click($.exposure.nextImage);
							$('.exposureTarget').width($(this).width()).height($(this).height());
							var imageDataContainer = wrapper.siblings('.exposureData');
							if (imageDataContainer.length) {
								var captionContainer = imageDataContainer.find('.caption');
								if (captionContainer.length) {
									captionContainer.empty();
									if (!caption) {
										caption	= thumb.attr('title');
									}
								}
								captionContainer.html(caption);
								
								var extraImageDataContainer = imageDataContainer.find('.extra');
								if (extraImageDataContainer.length) {
									extraImageDataContainer.empty();
									if (!extraImageData) {
										extraImageData = thumb.data('data');
									}
									extraImageDataContainer.html(extraImageData);
								}
							}
							
							imageDataContainer.offset({top:$(this).height()+'px'});
							imageDataContainer.length ? imageDataContainer.show() : imageDataContainer.hide();
							
							$.exposure.onImage($(this), imageDataContainer, thumb);
							
							$(this).removeAttr('width');
							$(this).removeAttr('height');	
						});
					} else {
						wrapper.siblings().andSelf().empty();
						$('.exposureThumbs li.active').removeClass('active');
					}
				} else {
					wrapper.siblings().andSelf().empty();
					$('.exposureThumbs li.active').removeClass('active');
				}
				
				$.exposure.current = src;
			},
			imageEffects : function(image, imageData, thumb) {				
				imageData.hide();
				image.hide().stop().fadeIn(1000);
				
				if (imageData.find('.caption').html().length > 0 || imageData.find('.extra').html().length > 0) {					
					var imageDataBottom = -imageData.outerHeight();
					
					var hoverOver = function(){imageData.stop().show().animate({bottom:0+'px'},{queue:false,duration:160});};
					var hoverOut = function(){imageData.stop().show().animate({bottom:imageDataBottom+'px'},{queue:false,duration:160})};
										
					$('.exposureWrapper').hover(hoverOver,hoverOut);
					imageData.hover(hoverOver,hoverOut);
				} else {
					$('.exposureWrapper').unbind('mouseenter mouseleave');	
				}

				thumb.parents('li').siblings().children('img.selected').stop().fadeTo(200, 0.3);
				
				thumb.fadeTo('fast', 1).addClass('selected');
			},
			thumbEffects : function(thumb) {
				var li = thumb.parents('li');				
				var fadeTo = li.hasClass('active') ? 1 : 0.3;
				
				thumb.css({display : 'none', opacity : fadeTo}).stop().fadeIn(200);
				
				thumb.hover(function() { 
					thumb.fadeTo('fast',1); 
				}, function() { 
					li.not('.active').children('img').fadeTo('fast', 0.3); 
				});
			}
		}			
	});
})(jQuery);


/*
* jQuery Hotkeys Plugin
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Based upon the plugin by Tzury Bar Yochay:
* http://github.com/tzuryby/hotkeys
*
* Original idea by:
* Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
*/
(function(jQuery){jQuery.hotkeys={version:"0.8",specialKeys:{8:"backspace",9:"tab",13:"return",16:"shift",17:"ctrl",18:"alt",19:"pause",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"del",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",109:"-",110:".",111:"/",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12",144:"numlock",145:"scroll",191:"/",224:"meta"},shiftNums:{"`":"~","1":"!","2":"@","3":"#","4":"$","5":"%","6":"^","7":"&","8":"*","9":"(","0":")","-":"_","=":"+",";":": ","'":"\"",",":"<",".":">","/":"?","\\":"|"}};function keyHandler(handleObj){if(typeof handleObj.data!=="string"){return}var origHandler=handleObj.handler,keys=handleObj.data.toLowerCase().split(" ");handleObj.handler=function(event){if(this!==event.target&&(/textarea|select/i.test(event.target.nodeName)||event.target.type==="text")){return}var special=event.type!=="keypress"&&jQuery.hotkeys.specialKeys[event.which],character=String.fromCharCode(event.which).toLowerCase(),key,modif="",possible={};if(event.altKey&&special!=="alt"){modif+="alt+"}if(event.ctrlKey&&special!=="ctrl"){modif+="ctrl+"}if(event.metaKey&&!event.ctrlKey&&special!=="meta"){modif+="meta+"}if(event.shiftKey&&special!=="shift"){modif+="shift+"}if(special){possible[modif+special]=true}else{possible[modif+character]=true;possible[modif+jQuery.hotkeys.shiftNums[character]]=true;if(modif==="shift+"){possible[jQuery.hotkeys.shiftNums[character]]=true}}for(var i=0,l=keys.length;i<l;i++){if(possible[keys[i]]){return origHandler.apply(this,arguments)}}}}jQuery.each(["keydown","keyup","keypress"],function(){jQuery.event.special[this]={add:keyHandler}})})(jQuery);

