(function($){

	$.fn.gallery = function(opts){
		
		this.each(function(){
			
			var opts = $.extend({}, {
				thumbnail: 'li a',
				imgCont: '.mainImage',
				caption: '.caption',
				fadeTime: 300,
				timeout: 6000
			}, opts);
		
			var gallery = $(this);
			var ic = gallery.find(opts.imgCont)
			var working = false;
			var lastSuccessfull = 0;
			var thumbNo = 0;
			var mask;
			var timeout2;
			var timeout;
			
			//append the mask and make it invisible
			ic.append(mask = $('<div class="loadMask" />').css('opacity', 0));
			
			function change(imgURL){

				if(!imgURL){
					throw new TypeError('imgURL is null value, empty or undefined value!');
				}
				
				var timedout = false;
				working = true;
				
				gallery.trigger('gallery.changing', imgURL);
				
				//get starting img
				var sImg = ic.find('img');
				
				//create loader gif
				var loader = $('<img>').attr('src', '../images/ajax-loader.gif').addClass('loaderGif');

				//find out if this image is cached
				var preloaded = false;
				var img;
				$('<img>').load(function(){
					preloaded = true;
				})
				.attr('src', imgURL);

				setTimeout(function(){
					var loaded = preloaded;

					//fade the mask in if the image isn't already cached
					var startOpacity;

					clearTimeout(timeout);
					clearTimeout(timeout2);
					mask.stop().fadeTo(opts.fadeTime, ((loaded) ? 0 : 0.5), function(){
						mask.append(loader);
					});
					
					//fade the caption out if the image is not cached
					if (!loaded) {
						var caption = gallery.find(opts.caption);
						var captionParent = gallery.find(opts.caption).parent();
						captionParent.css('height', captionParent.height());
						caption.fadeOut(opts.fadeTime);
					}
					
					//if no response reset to last images
					timeout = setTimeout(function(){
						timedout = true;
						loader.remove();
						ic.append('<div class="galleryError"><div>Sorry! There was an error loading the selected image.</div></div>')
						
						setTimeout(function(){
							working = false;
							ic.find('.galleryError').remove();
							mask.fadeTo(0,0);
							caption.fadeIn().parent().css('height', '');
							thumbNo = lastSuccessfull;
							
							gallery.find(opts.thumbnail).
							parent()
							.removeClass('selected')
							.eq(lastSuccessfull)
							.addClass('selected');
						}, 2000);
						
					}, opts.timeout);						
					
					//create image & append it
					var img;
					ic.append(img = $('<img>')
					.load(function(){
						if (!timedout) {
							//kill timout
							clearTimeout(timeout);
							
							gallery.trigger('gallery.imageloaded', {
								'imgURL': imgURL,
								'thumbNo': thumbNo
							});
							lastSuccessfull = thumbNo;
							//onload, fade the mask out and new image in
							
							//hide mask
							loader.hide();
							mask.fadeTo(((loaded) ? 0 : opts.fadeTime * 2), 0);
							
							//use a seperate timeout for fading image and caption in,
							//so we can start before the mask has finished.
							setTimeout(function(){
								working = false;
								
								gallery.find(opts.caption).html(gallery.find('li a img').eq(thumbNo).attr('alt')).fadeIn().parent().css('height', '');
								
								img.fadeTo(opts.fadeTime, 1, function(){
									timeout2 = setTimeout(function(){sImg.remove()}, opts.fadeTime);
								});
							}, ((loaded) ? 0 : opts.fadeTime));
						}
					})
					.attr('src', imgURL)
					.css('opacity', 0));
				
				}, 50);
				
			};
			

		
			//attach click handlers to links
			var links = $(this).find(opts.thumbnail).each(function(i){
				
				function changeHandler(){
					var elem = this;
					//if this is not the current image and we are not already processing a change
					setTimeout(function(){
						if (i != thumbNo) {
							thumbNo = i;
							change(elem.href);
							links.parent().removeClass('selected');
							$(elem).parent().addClass('selected');
						};
					}, 0);
				}				
				
				$(this).mouseenter(changeHandler)
				.focus(changeHandler)
				.click(function(e){
					e.preventDefault();
				})
			});
		
		});
		
		return this;
		
	};


$(function(){
	$('div.gallery').gallery();
})

})(jQuery);

