/**
 * jQuery Zoom Plugin
 * Zoom a thumbnail to a fullsize image
 *
 * @version 1.0
 * @author Zach Waugh <zwaugh@gmail.com>
 * http://zachwaugh.com/projects/jquery-plugins/zoom.html
 * 
 * Copyright (c) 2009 Zach Waugh
 * Licensed under the MIT License - http://www.opensource.org/licenses/mit-license.html
 */
(function($) {
	$.fn.zoom = function (options) {
		// Handle public method calls
		// public methods are not chainable
		if (typeof(options) == 'string') {
			// Get object instance
			var instance = $.data(this[0], Plugin.name);
			
			// Call method if there is an instance and the method isn't private
			if (instance && options.charAt(0) != '_') {
				args = Array.prototype.slice.call(arguments, 1);
				return instance[options].apply(instance, args);
			} else {
				return undefined;
			}
			
		} else {
			return this.each(function () {
				var instance = new Plugin(this, options);
				
				// Store instance on DOM element
				$.data(this, Plugin.name, instance);
			});
		}
	};
	
	function Plugin(element, options) {
		// Store reference to original element
		this.element = $(element);
		
		// Extend defaults to create options
		this.options = $.extend({}, Plugin.defaults, options);
		
		// Private variables
		this._loaded = false;
		
		// Call Zoom initializer
		this._init();
	}

	Plugin.name = 'Zoom';
	
	Plugin.defaults = {
		duration: 400,
		imageDir: 'images/zoom/'
	};
	
	Plugin.prototype = {	
		/**
		 * Initialize and load image
		 * @param {jQuery Event Object} event
	 	 */
	 	_init: function () {
			var self = this;
			
			this._imageSrc = this.element.attr('href');
			
			// Bind click event
			this.element.click(function () { return self._zoom(); });
		},
		
		_zoom: function (event) {
			if ($('#zoom').is(':visible')) {
				this._hide();
				return false;
			}
			
			var self = this;

			if (!this._loaded) {
				// Show loader
				var loading_position = center({width: 52, height: 52});
		 		var loading = '<div id="zoom_loading" style="left:' + loading_position.left + 'px;top:' + loading_position.top + 'px;"></div>';
				$('body').append(loading);

				// Preload image
				var img = new Image();
				img.onload = function() {
					$('#zoom_loading').remove();
					
					var size = {width: img.width, height: img.height, src: self._imageSrc};

					// Cache image size when loaded
					self._size = size;
					self._loaded = true;

					self._show();
				};

				img.src = this._imageSrc;
			} else {
				self._show();
			}

			return false;
		},

		/**
		 * 
		 */
		_show: function () {
			var self = this;
			var imgWidth = this._size.width;
			var imgHeight = this._size.height;

			var imgSize = resizeImage({width: imgWidth, height: imgHeight});
			shadowWidth = imgSize.width * 0.075;
			shadowHeight = imgSize.height * 0.075;
			var size = {width: imgSize.width + shadowWidth, height: imgSize.height + shadowHeight};


			var thumb = this.element.find('img').eq(0);
			
			var offset = thumb.offset();
			offset.top -= shadowHeight / 2;
			offset.left -= shadowWidth / 2;
			
			this._offset = offset;
			
			var width = thumb.outerWidth();
			var height = thumb.outerHeight();
			
			thumbWidth = width + shadowWidth;
			thumbHeight = height + shadowHeight;

			this._thumbSize = {width: width, height: height, offsetWidth: thumbWidth, offsetHeight: thumbHeight};
			
			//Create html fragment
			var html = '<div id="zoom" style="opacity: 0.5; width:' + thumbWidth + 'px;height:' + thumbHeight + 'px;top:' + offset.top + 'px;left:' + offset.left + 'px;">';
			html += '<div id="zoom_content" style="left:' + shadowWidth / 2 + 'px; top:' + shadowHeight / 2 + 'px;width:' + width + 'px; height:' + height + 'px;">';
			html += '<a href="#" id="zoom_close" style="opacity: 0;"></a>';
			html += '<img src="' + this._imageSrc + '" width="100%" height="100%" alt="" />';
			html += '</div>';

			// Add to body
			$('body').append(html);

			// Center overlay on screen
			var position = center(size);
			$('body').one('click', function () { return self._hide(); });
			$('body').bind('keydown.' + Plugin.name, function (event) { return self._keyDown(event); });

			// Attach click event and fadeIn
			$('#zoom').click(function () { return self._hide(); });
			$('#zoom').stop().animate({width: size.width, height: size.height, opacity: 1, top: position.top, left: position.left}, {duration: this.options.duration, complete: function () {
				$('#zoom_close').animate({opacity: 1});
			}});

			$('#zoom_content').stop().animate({width: imgSize.width, height: imgSize.height}, {duration: this.options.duration});
		},

		/**
		 * Zoom out full size image and remove from DOM
		 * @param {jQuery Event Object} event
	 	 */
		_hide: function () {
			// Immediately hide shadow and close button
			$('#zoom_close').hide();

			// Fade out zoom container and remove from DOM
			$('#zoom').stop().animate({top: this._offset.top, left: this._offset.left, width: this._thumbSize.offsetWidth, height: this._thumbSize.offsetHeight, opacity: 0.5}, {duration: this.options.duration, complete: function () {
				$('#zoom').remove();
			}});

			$('#zoom_content').stop().animate({width: this._thumbSize.width, height: this._thumbSize.height}, {duration: this.options.duration});

			// Cleanup event bindings
			$('body').unbind('keydown.' + Plugin.name);
			
			return false;
		},
		
		_keyDown: function (event) {
			if (event.which == 27) {
				this._hide();
			}
			return false;
		}
	};
	
	/**
	 * Private Helper methods
	 */
	
	/**
	 * Center an image in the window
	 */
	function center(size) {
		var scrollTop = $(window).scrollTop();
		var scrollLeft = $(window).scrollLeft();
		var left = ($(window).width() - size.width) / 2 + scrollLeft;
		var top = (($(window).height() - size.height) / 2) + scrollTop;

		return {top: top, left: left};
	}

	/**
	 * Resize an image to ensure it's not bigger than window
	 * @param {Object} size - object with images width and height
	 */
	function resizeImage(size, padding) {
		var windowWidth = $(window).width();
		var windowHeight = $(window).height();
		var ratio = 1;
		padding = (padding === undefined) ? 100 : padding;
		var imgWidth = size.width;
		var imgHeight = size.height;

		if ((imgWidth + padding) >= windowWidth && (imgHeight + padding) >= windowHeight) {
			if (imgWidth > imgHeight) {
				ratio = imgHeight / imgWidth;
				imgWidth = windowWidth - padding;
				imgHeight = ratio * imgWidth;
				
				if ((imgHeight + padding) >= windowHeight) {
					ratio = imgWidth / imgHeight;
					imgHeight = windowHeight - padding;
					imgWidth = ratio * imgHeight;
				}
			} else {
				ratio = imgWidth / imgHeight;
				imgHeight = windowHeight - padding;
				imgWidth = ratio * imgHeight;
				
				if ((imgWidth + padding) >= windowWidth) {
					ratio = imgHeight / imgWidth;
					imgWidth = windowWidth - padding;
					imgHeight = ratio * imgWidth;
				}
			}
		} else if ((imgWidth + padding) >= windowWidth) {
			ratio = imgHeight / imgWidth;
			imgWidth = windowWidth - padding;
			imgHeight = ratio * imgWidth;
		} else if ((imgHeight + padding) >= windowHeight) {
			ratio = imgWidth / imgHeight;
			imgHeight = windowHeight - padding;
			imgWidth = ratio * imgHeight;
		}

		return {width: Math.ceil(imgWidth), height: Math.ceil(imgHeight)};
	}
})(jQuery);