

(function($){
	$.fn.BionicTooltip = function(options){
		
		var __this__ = this;
		
		var toolTipClass = "bTooltip";
		
		var defaults = { 
				speed: 200,
				distance: 0,
				contentOffsetX: 0,
				contentOffsetY: 0,
				onShow:null,
				onHide:null,
				onMouseOverTrigger:null,
				onMouseOutTrigger:null,
				exclusive:'true',
				hideTimeout:500,
				transitions: true,
				transition: 'fade', // TODO option not read yet
				easingFunction: 'linear',
				postConstructor:null
		};
		
		this.options = $.extend(defaults, options);
		this.tooltip = null;
		var trigger = $(this);
		
		
		var log = function(message){
			try{
				console.log(message);
			}catch(e){}
		}
		
		/*
		 * FIXME IE6/7 suffering from PNG+opacity filter collision,
		 * with transitions enabled
		 */
		var show = function(){
			if (__this__.tooltip.css('opacity')=='1' && __this__.tooltip.css('display')=='block'){
				return;
			}
			/*
			 *  FIXME  __this__.tooltip.css("top") is returning 0 for FF 3.6
			 *  this is apparently documented behavior... if the element is
			 *  hidden by display:none, getting the css:top isn't reliable
			 *  Until I address this animating by distance is disabled
			 */
			//var currTop = __this__.tooltip.css("top");
			//currTop = currTop.substring(0,currTop.length-2);
			//var newTop = (parseInt(currTop)+__this__.options.distance)+"px";
			if (__this__.options.transitions){
				__this__.tooltip.css('opacity',0);
				__this__.tooltip.css("display","block");
				//__this__.tooltip.stop().animate({opacity:1.0,top:newTop},__this__.options.speed,__this__.options.easingFunction,function(){
				__this__.tooltip.stop().animate({opacity:1.0},__this__.options.speed,__this__.options.easingFunction,function(){
					try{
						__this__.options.onShow();
					}catch(e){}
				});
			}else{ // show without any transitions
				__this__.tooltip.css("display","block");
				try{
					__this__.options.onShow();
				}catch(e){}
			}
		}
		
		var hide = function(){
			/*
			 *  FIXME  __this__.tooltip.css("top") is returning 0 for FF 3.6
			 *  this is apparently documented behavior... if the element is
			 *  hidden by display:none, getting the css:top isn't reliable
			 *  Until I address this animating by distance is disabled
			 */
			//var currTop = __this__.tooltip.css("top");
			//currTop = currTop.substring(0,currTop.length-2);
			//var newTop = (parseInt(currTop)-__this__.options.distance)+"px";
			if (__this__.options.transitions){
				__this__.tooltip.css('opacity',1);
				//__this__.tooltip.stop().animate({opacity:0,top:"40px"},__this__.options.speed,__this__.options.easingFunction,function(){
				__this__.tooltip.stop().animate({opacity:0},__this__.options.speed,__this__.options.easingFunction,function(){
					__this__.tooltip.css("display","none");
					try{
						__this__.options.onHide();
					}catch(e){}
				});
			}else{ // show without any transitions
				__this__.tooltip.css("display","none");
				try{
					__this__.options.onHide();
				}catch(e){}
			}
		}
		
		var bindEvents = function(){
			
			trigger.bind('mouseenter',function(){
				try{
					/* try onMouseOverTrigger callback */
					__this__.options.onMouseOverTrigger(this);
				}catch(e){}
				clearTimeout(__this__.hideTimout);
				if (__this__.options.exclusive=='true'){
					$(document).find("."+toolTipClass).not(__this__.tooltip).css("display","none");
				}
				show();
			});
			
			trigger.bind('mouseleave',function(){
				try{
					__this__.options.onMouseOutTrigger(this);
				}catch(e){}
				clearTimeout(__this__.hideTimout);
				__this__.hideTimout = setTimeout(function(){
					hide();
				},__this__.options.hideTimeout);
			});
			
			__this__.tooltip.bind('mouseleave',function(){
				clearTimeout(__this__.hideTimout);
				__this__.hideTimout = setTimeout(function(){
					hide();
				},__this__.options.hideTimeout);
			});
			
			__this__.tooltip.bind('mouseenter',function(){
				clearTimeout(__this__.hideTimout);
			});
		}

		/*
		 * class constructor
		 */
		var constructor = function(){
			
			// expect to find a .bTooltip sibling of the called object
			var userTooltip = trigger.siblings('.'+toolTipClass).first();
			
			if(userTooltip.length==0){
				log("BionicTooltip: aborting, no immediate siblings matching class "+toolTipClass);
				return;
			}
			
			// hide user tooltip
			userTooltip.css("display","none");
			// copy user tooltip
			userTooltipCopy = userTooltip.children().clone();
			// re-construct proper tooltip markup
			var tooltipFragment = '<div class="bTooltipInner"></div>';
			userTooltip.empty();
			userTooltip.append(tooltipFragment);
			var tooltipInner = userTooltip.children('.bTooltipInner').first();
			tooltipInner.append(userTooltipCopy);
			tooltipInner.css("margin-top",__this__.options.contentOffsetY);
			tooltipInner.css("margin-left",__this__.options.contentOffsetX);
			__this__.tooltip = userTooltip;
			__this__.tooltip.css("z-index","9000");
			
			/* detect browser version */
			if($.browser.msie){
				userAgent = $.browser.version;
				userAgent = userAgent.substring(0,userAgent.indexOf('.'));
				version = userAgent;
				if (version<=8){
					__this__.options.transitions = false;
				}
			}
			
			/* bind mouse events */
			bindEvents();
			
			try{
				__this__.options.postConstructor(trigger,__this__.tooltip);
			}catch(e){}
		}();
		
	};
})(jQuery);

