(
	function($) {
		
		/* INVISIBLE IMAGE GALLERY */
		
		$.fn.imageGallery = function(options) {
				
			var defaults = {
				validExtensions: ["jpg", "gif", "png"],
				galleryBox: {
					background: '#000',
					width: 400,
					height: 400,
					opacity: 0.5
				},
				mouseInfoDiv: {
					previousCSS: {
						'background': 'url(images/imagegallery_previous.png) center center no-repeat',
						'opacity': .6
					},
					nextCSS: {
						'background': 'url(images/imagegallery_next.png) center center no-repeat',
						'opacity': .6
					},
					closeCSS: {
						'background': 'url(images/imagegallery_close.png) center center no-repeat',
						'opacity': .6
					},
					mouseDistance: {
						top: 7,
						left: 7
					}
				},
				fade: {
					background: '#000',
					opacity: .20
				},
				speed: {
					showGallery: 250,
					fadeIn: 150,
					fadeOut: 150,
					resize: 100,
					resizeDrag: 1.25
				},
				margin: {
					horizontal: 100,
					vertical: 100
				},
				phpFile: '',
				adjustToScreen: true,
				fadeId: 'imageGallery-fade',
				imageBoxId: 'imageGallery-image-box',
				imageHolderId: 'imageGallery-image-holder',
				mouseInfoId: 'imageGallery-info',
				nextImageId: 'imageGallery-next-image',
				previousImageId: 'imageGallery-previous-image',
				imageCountId: 'imageGallery-image-count',
				ignore: '',
				gallery: ''
			};
			
			var settings = $.extend({}, defaults, options);
			
			// keeps track on current image
			var currentImage = 0;
			
			// stores all gallery images
			var images = [];
			
			
			var imageLoaded = function () {
				// reset width & height attributes
				$(this).width('').height('');
				
				if ($('#' + settings.imageBoxId).width() != $(this).width() &&
					$('#' + settings.imageBoxId).height() != $(this).height()) {
					
					// if image size isn't set
					if (!images[currentImage].size) {
						images[currentImage].size = {
							'0': $(this).width(),
							'1': $(this).height()
						}
					}
				}
				
				var dimensions = getDimensions(images[currentImage]);
				
				// animate image box
				$('#' + settings.imageBoxId).animate(dimensions, settings.speed.resize, function () {
					$(this).find('img:first').css({
						opacity: 1,
						width: '100%',
						height: '100%'
					});
				});
			}
			
			
			var showImage = function(image) {
				
				if (settings.gallery == '')
					currentImage = getImageId(image);
				
				$('#' + settings.imageBoxId).append('<img src="' + $(image).attr('href') + '" style="opacity: 0;" />');
				
				$('#' + settings.imageBoxId + ' img:first').load(imageLoaded);
				
				$('#' + settings.imageBoxId).append('<div id="' + settings.previousImageId + '" style="position:absolute; top:0; left:0; width:50%; height:100%;background: #000;"></div><div id="' + settings.nextImageId + '" style="position:absolute; top:0; left:50%; width:50%; height:100%;background: #000;">&nbsp;</div>');
				
				$('#'+ settings.previousImageId +',#'+ settings.nextImageId).css('opacity', 0);
				
				$('#' + settings.nextImageId).click(nextImage).mouseover(mouseOverNextImage);
				$('#' + settings.previousImageId).click(previousImage).mouseover(mouseOverPreviousImage);
				
			}
			
			
			var previousImage = function() {
				
				if (images.length > 1) {
					currentImage = (currentImage == 0) ? images.length - 1 : currentImage - 1;
					loadCurrentImage();
				}
				else {
					hideGallery();
				}
				
			}
			
			
			var nextImage = function() {
				
				if (images.length > 1) {
					currentImage = (currentImage + 1 == images.length) ? 0 : currentImage + 1;
					loadCurrentImage();
				}
				else {
					hideGallery();
				}
				
			}
			
			
			var mouseOverPreviousImage = function() {
				var background = (images.length > 1) ? settings.mouseInfoDiv.previousCSS : settings.mouseInfoDiv.closeCSS;
				$('#' + settings.mouseInfoId).css(background);
				$('#' + settings.mouseInfoId).unbind('click').click(previousImage);
			}
			
			
			var mouseOverNextImage = function() {
				var background = (images.length > 1) ? settings.mouseInfoDiv.nextCSS : settings.mouseInfoDiv.closeCSS;
				$('#' + settings.mouseInfoId).css(background);
				$('#' + settings.mouseInfoId).unbind('click').click(nextImage);
			}
			
			
			var mouseOverFader = function() {
				$('#' + settings.mouseInfoId).css(settings.mouseInfoDiv.closeCSS);
				$('#' + settings.mouseInfoId).unbind('click').click(hideGallery);
			}
			
			
			var hideGallery = function() {
				// animate the image box
				$('#' + settings.imageBoxId + ', #' + settings.fadeId + ', #' + settings.mouseInfoId).animate({
					opacity: 0
				}, settings.speed.fadeOut, function() {
					$('#' + settings.imageBoxId + ', #' + settings.fadeId + ', #' + settings.mouseInfoId).remove();
					$(document).unbind('mousemove');
					$(window).unbind('resize');
				});
				
			}
			
			
			var showGallery = function(event) {
				
				currentImage = (settings.gallery == '') ? getImageId($(this)) : 0;
				
				// if fader div doesn't exsists
				if ($('#' + settings.fadeId).length == 0) {
					
					var _this = $(this);
					var thumb = $(this).find('img');
					var offset = thumb.offset();
					
					// add the fade layer
					$('body').append('<div id="' + settings.fadeId + '"></div>');
					
					// set hide function
					$('#' + settings.fadeId).click(hideGallery);
					
					$('#' + settings.fadeId).mouseover(mouseOverFader);
					
					// style the fade layer
					$('#' + settings.fadeId).css({
						'background-color': settings.fade.background,
						'opacity': 0,
						'position': 'fixed',
						'left': '0px',
						'top': '0px',
						'height': '100%',
						'width': '100%'
					});
					
					// animate the fade layer
					$('#' + settings.fadeId).animate({
						opacity: settings.fade.opacity
					}, settings.speed.fadeIn, function() {
					  	//animation done.
					});
					
					
					// add the image box
					$('body').append('<div id="' + settings.imageBoxId + '"></div>');
					
					// style the image box
					$('#' + settings.imageBoxId).css({
						'background-color': settings.galleryBox.background,
						'position': 'absolute',
						'left': offset.left + 'px',
						'top': offset.top + 'px',
						'height': thumb.height(),
						'width': thumb.width(),
						'opacity': settings.galleryBox.opacity,
						'overflow': 'hidden'
					});
					
					var dimensions = getDimensions(images[currentImage]);
					$.extend(dimensions, {opacity: 1});
					
					// animate the image box				
					$('#' + settings.imageBoxId).animate(dimensions, settings.speed.showGallery, function() {
						showImage(_this);
					});
					
					if (images.length > 1) {
						// add image count
						$('#' + settings.imageHolderId).append('<div id="' + settings.imageCountId + '">' + (currentImage + 1) + ' / ' + images.length + '</div>');
						
						$('#' + settings.imageCountId).css({
							'position': 'absolute',
							'left': offset.left + 'px',
							'top': (offset.top + thumb.height()) + 'px'
						});
					}
					
					
					// add mouse info div
					$('body').append('<div id="' + settings.mouseInfoId + '"></div>');
					
					// style the mouse info div
					$('#' + settings.mouseInfoId).css({
						'position': 'absolute',
						'left': event.pageX + settings.mouseInfoDiv.mouseDistance.left + 'px',
						'top': event.pageY + settings.mouseInfoDiv.mouseDistance.top + 'px',
						'height': settings.margin.horizontal / 2,
						'width': settings.margin.horizontal / 2
					});
					
					// let the mouse info div follow the cursor
					$(document).mousemove(followCursor);
					
					//resize image to fit screen
					if (settings.adjustToScreen)
						$(window).resize(fitToScreen);
					
				}
				else {
					loadCurrentImage();
				}
				
				return false;
			};
			
			
			var fitToScreen = function() {
				if ($('#' + settings.imageBoxId).length > 0) {
					
					var dimensions = getDimensions(images[currentImage]);
					
					$('#' + settings.imageBoxId).animate(dimensions, {
						queue: false,
						duration: settings.speed.resize
					});
				}
			}
			
			
			function followCursor(event) {
				var left = event.pageX + settings.mouseInfoDiv.mouseDistance.left;
				var top = event.pageY + settings.mouseInfoDiv.mouseDistance.top;
				var maxLeft = $(window).width() + $(document).scrollLeft() - $('#' + settings.mouseInfoId).width();
				var minLeft = $(document).scrollLeft();
				var maxTop = $(window).height() + $(document).scrollTop() - $('#' + settings.mouseInfoId).height();
				var minTop = $(document).scrollTop();
				
				// stay in sight
				left = (left > maxLeft) ? maxLeft : (left < minLeft) ? minLeft : left;
				top = (top > maxTop) ? maxTop : (top < minTop) ? minTop : top;
				
				$('#' + settings.mouseInfoId).css({
					'left': left + 'px',
					'top': top + 'px'
				});
			}
			
			
			function getDimensions(image) {
				
				// calculate max width and height - margin
				var maxWidth = $(window).width() - settings.margin.horizontal;
				var maxHeight = $(window).height() - settings.margin.vertical;
				
				// calculate width and height
				var destWidth = (image.size) ? image.size[0] : settings.galleryBox.width;
				var destHeight = (image.size) ? image.size[1] : settings.galleryBox.height;
				
				if (settings.adjustToScreen) {
					var percent = 1;
					
					if (destWidth > maxWidth) {
						percent = maxWidth / destWidth;
						destWidth = maxWidth;
						destHeight = destHeight * percent;
					}
					
					if (destHeight > maxHeight) {
						percent = maxHeight / destHeight;
						destHeight = maxHeight;
						destWidth = destWidth * percent;
					}
				}
				
				// calculate left and top
				var destLeft = ($(window).width() / 2) - (destWidth / 2) + $(document).scrollLeft();
				var destTop = ($(window).height() / 2) - (destHeight / 2) + $(document).scrollTop();
				
				// make sure that left & top never are below 0
				destLeft = (destLeft < 0) ? 0 : destLeft;
				destTop = (destTop < 0) ? 0 : destTop;
				
				return {
					width: destWidth,
					height: destHeight,
					left: destLeft + 'px',
					top:  destTop + 'px'
				}
			}
			
			
			function getImageId(image) {
				for (var i = 0; i < images.length; i++) {
					if ($("a").index(image) == $("a").index(images[i].jqObject))
						return i;
				}
				return -1;
			}
			
			
			function loadCurrentImage() {
				$('#' + settings.imageBoxId + ' img:first').css({
					opacity: 0,
					width: $('#' + settings.imageBoxId).width() + 'px',
					height: $('#' + settings.imageBoxId).height() + 'px'
				}).attr('src', images[currentImage].link);
			}
			
			
			
			/* INIT */
			
			return this.each(
				function() {
					
					if (settings.gallery != '') {
						images = settings.gallery.images;
						$(this).click(showGallery);
					}
					else {
						// add ignore selector if set
						var ignoreSelector = (settings.ignore != '') ? ':not(' + settings.ignore + ' a)' : '';
						
						$(this).find('a' + ignoreSelector).each(
							function () {
								var link = $(this).attr('href');
								var extension = link.split('.');
								extension = extension[extension.length-1];
								
								// If A links to a file with a valid extension and holds at least one image
								if ($.inArray(extension, settings.validExtensions) != -1 && $(this).find('img').length > 0) {
									
									var i = images.length;
									
									// fill images
									images[i] = {
										jqObject: $(this),
										link: link
									};
									
									// get image dimensions
									if (settings.phpFile != '') {
										$.getJSON(settings.phpFile, {task: 'getImageSize', url: link}, function(result, status) {
											if (status == 'success') {
												images[i].size = result;
											}
										});
									}
									
									$(this).click(showGallery);
									
								}
								
							}
						);
					
					}
					
				}
			);
		};
		
	}
)(jQuery);