/**
 * @author Remy Sharp, David Ordal
 * @date 2009-01-14
 * @url http://jqueryfordesigners.com/coda-popup-bubbles/
 * @license Creative Commons License - ShareAlike http://creativecommons.org/licenses/by-sa/3.0/
 *
 * See URL for markup examples and screencast
 *
 * Added 'showDelay' - David Ordal - 2008-12-15
 * Added new animation options - David Ordal - 2009-01-14
 *
 */

(function ($) {
	$.fn.bubble = function (options) {
		var defaults = {
			'trigger' : '.trigger',		 	// element that should trigger the popup
			'popup' : '.popup',			 	// element with the popup in it
			'popupPosition' : {top: -100, left: -32},
											// position of the popup element, relative to the trigger element.
											// NOTE: you should only specific one X and one Y direction here (e.g. {top:, right:}
											// or {bottom:, left:} ) You do not want to specify that your popup will be
											// both X pixels from the top of the trigger, AND x pixels from the bottom. Unexpected
											// results will almost certainly occur!
			'distance' : 10,			 	// distance the popup should travel
			'directionFadeIn': 'bottom', 	// side that the popup should fade in from. One of 'top', 'bottom', 'right' or 'left'
			'directionFadeOut': 'bottom',	// side that the popup should fade out to. One of 'top', 'bottom', 'right' or 'left'
			'hideDelay' : 500,			 	// delay before hiding the popup (ms)
			'effectTime' : 250,			 	// length of the fade in/fade out effect
			'showDelay'  : 300			 	// delay before showing the popup (ms)
		};
		
		// oppositeDirection(): return the opposite of the direction we're given
		var oppositeDirection = function(direction) {
			switch (direction) {
				case 'top':
					return 'bottom';
				case 'right':
					return 'left';
				case 'bottom':
					return 'top';
				case 'left':
					return 'right';
				default:
					return direction;
			}
		}
	
		var settings = $.extend({}, defaults, options);
		
		// assign 'auto' to any popupPositions that aren't a number or are missing
		settings.popupPosition.top = (isNaN(settings.popupPosition.top)) ? 'auto' : settings.popupPosition.top;
		settings.popupPosition.right = (isNaN(settings.popupPosition.right)) ? 'auto' : settings.popupPosition.right;
		settings.popupPosition.bottom = (isNaN(settings.popupPosition.bottom)) ? 'auto' : settings.popupPosition.bottom;
		settings.popupPosition.left = (isNaN(settings.popupPosition.left)) ? 'auto' : settings.popupPosition.left;
		
		// assign position variables if nothing set
		if (settings.popupPosition.top == 'auto' && settings.popupPosition.bottom == 'auto')
			settings.popupPosition.top = 0;
			
		if (settings.popupPosition.right == 'auto' && settings.popupPosition.left == 'auto')
			settings.popupPosition.left = 0;
		
		// figure out which direction we need to animate. This changes depending on how
		// the popup was positioned; if we positioned it relative to the top of the trigger,
		// then we need to animate it in the same way. Not doing that would potentially
		// mean setting both 'top:' and 'bottom' properties on the popup, which would cause rendering problems
		var animateInFrom; var animateOutFrom;
		var animateInDirection; var animateOutDirection;
				
		// first, do fade in. we have to offset the popup position slightly so that it will end up
		// in the right spot after the fade in
		if (!isNaN(settings.popupPosition[settings.directionFadeIn])) {
			animateInFrom = settings.directionFadeIn; 
			animateInDirection = '+'; 
			settings.popupPosition[settings.directionFadeIn] -= settings.distance;
		} else {
			animateInFrom = oppositeDirection(settings.directionFadeIn); 
			animateInDirection = '-';
			settings.popupPosition[oppositeDirection(settings.directionFadeIn)] += settings.distance;
		}

		// now do fade out. note the animation directions are backwards
		if (!isNaN(settings.popupPosition[settings.directionFadeOut])) {
			animateOutFrom = settings.directionFadeOut; animateOutDirection = '-';
		} else {
			animateOutFrom = oppositeDirection(settings.directionFadeOut); animateOutDirection = '+';
		}
		
		return this.each(function () {
			var hideDelayTimer = null;

			var trigger = $(settings.trigger, this);
			var popup = $(settings.popup, this);
			
			var readyToShow = false;

			$([trigger.get(0), popup.get(0)]).mouseover(function () {
				if (hideDelayTimer) clearTimeout(hideDelayTimer);

				readyToShow = true;

				setTimeout(function() {

					if (popup.is(':animated, :visible') || !readyToShow) {
						return;
					} else {
					
						var animateOptions = {};
						animateOptions['opacity'] = 1;
						animateOptions[animateInFrom] = animateInDirection + '=' + settings.distance + 'px';
						
						// show the popup, using an animation
						popup.css({
							display: 'block',
							top: settings.popupPosition.top,
							right: settings.popupPosition.right,
							bottom: settings.popupPosition.bottom,
							left: settings.popupPosition.left
						}).animate(animateOptions, settings.effectTime);
					}
				}, settings.showDelay);
			}).mouseout(function () {
				if (hideDelayTimer) clearTimeout(hideDelayTimer);

				readyToShow = false;

				hideDelayTimer = setTimeout(function () {
					hideDelayTimer = null;

					var animateOptions = {};
					animateOptions['opacity'] = 0;
					animateOptions[animateOutFrom] = animateOutDirection + '=' + settings.distance + 'px';

					// hide the popup, using an animation
					popup.animate(animateOptions, settings.effectTime, 'swing', function () {
						popup.css({
							display: 'none'
						});
					});
				}, settings.hideDelay);
			});
		});
	}
})(jQuery);