/*
	ErrorTip 1.0
	
	@author Kevin Jantzer, Folium Partners, LLC
	@date 2011-01-25
	@revision 2011-02-24
	
	NOTES:
		- jQuery selector element must have an ID
	
	@todo - fix arrow position on IE
	@todo - fix left screen overflow for "flip"
	
	-= CHANGE LOG =-
	2011-02-24
		- adjusts position when too close to edge of screen
		- adjusts position depeding on element type (text input or checkbox)
		- arrow position: left, middle, and right
		- errortip will fade to specifed amount when input element is focused
*/
(function( $ ){
			
	// +===============================+
	// | ErrorTip INIT				   |
	// +===============================+
	$.fn.errortip = function(msg,options) {
	
		// Extend Options/Settings
		var settings = {
	      'showArrow'			: true,		// whether or not we show a down arrow
	      'color'				: 'red',	// create more color styles in the CSS file
	      
	      'fadeIn'				: 120,		// set to 0/false for no fade
	      'fadeOut'				: 1000,		// set to 0/false for no fade (used for autoFade)
	      
	      'maxWidth'			: 400,		// max width the error tip can be
	      
	      'autoFade'			: false,	// set to ms number (5000) for an auto fade out
	      'cancelAutoFade'  	: true,		// whether autoFade should be canceled when user hovers over tooltip
	      
	      'fadeOnFocus'			: true,		// whether the errortip should fadeout when the element is focused
	      'fadeOnFocusAmt'		: .3,		// the opacity the errortip should fade out to
	      'fadeOnFocusSpeed' 	: 300,		// how fast the fade on focus should be
	      
	      'posX'				: 'right',	// position horizontal (right or left)
	      'arrowPos'			: 'left', // the position of the arrow on the bottom
	      'offsetY'				: 0,		// fine tune the position of the error tip on the y axis
	      'offsetX'				: 0,		// fine tune the position of the error tip on the x axis
	      'overflow'			: 'flip',	// what should happen when error tip extens past the edge? (shift or flip)
	      
	      'returnTip'			: false	// alters chainability - 'true' returns the errotip object instead of jQuery selector.
	    };
	    
	    
	    
		if (options) { $.extend( settings, options ); }
		
		
		// ***TEMP*** arrow doesn't look right on IE
		if($.browser.msie) settings.showArrow = false;
	
	
		// Methods - i.e. $('#someID').errortip('clear')
		if(msg == 'clear'){
			clear(this,settings);
		}else{
			return create(this,msg,settings);
		}
		
	};
	
	
	// +===============================+
	// | ErrorTip CREATE			   |
	// +===============================+
	create = function(e,msg,settings){
		// set vars
		var docHeight = $(window).height(),
			docWidth = $(window).width(),
			id = e.attr('id'),
			style = '',
			arrow = '',
			mouseover = '',
			alreadyExists = $('[data-uniq_id="kjc_errortip_'+id+'"]').size(),
			theTipStyle = new Object();
			
		e.offset = e.offset();
		e.width = e.outerWidth();
		
		
		// if the errortip for this element already exists, let's remove it
		if(alreadyExists){
			
			// if we are displaying the same errortip msg
			if($('[data-uniq_id="kjc_errortip_'+id+'"] .kjc_errortip_msg').html() == msg){
				settings.fadeIn = 0;
				settings.fadeOut = 0;
			}
			clear(e,settings);
		}
	
		// set Style attribute
		style = 'position: absolute; z-index:10000; display:none;';
		style += 'max-width:'+settings.maxWidth+'px;';
		
		
		// create Arrow <div> if settings set to true
		if(settings.showArrow){
			arrow = '<div class="kjc_arrow"></div>';
		}
		
		if(settings.cancelAutoFade){
			mouseover = 'onmouseover="$.errortip.autoFade.stop(this,\''+id+'\')"';
		}
		
		// create the DOM element, append to the body tag, then fade in the errortip
		var theTip = $('<div class="kjc_errortip '+settings.color+'" '+
			'data-uniq_id="kjc_errortip_'+id+'" '+
			'data-id="kjc_errortip" '+
			mouseover+
			'style="'+style+'" ><span class="kjc_errortip_msg">'+msg+'</span>'+arrow+'</div>')
			
		theTip.appendTo('body');
		
		
		// default LEFT
		theTipStyle.left = e.offset.left + e.width + settings.offsetX;
		
		// default BOTTOM
		theTipStyle.bottom = (docHeight-e.offset.top+(-settings.offsetY)); 

		
		// The Error Tip Styles
		theTip.height = theTip.outerHeight();
		theTip.width = theTip.outerWidth();
		theTip.borderLeft = theTip.getCSS('borderLeftWidth');
		theTip.borderRight = theTip.getCSS('borderRightWidth');
	
	
		theTipStyle.left = theTipStyle.left - (theTip.borderLeft);
		
		
		// if our tip has an arrow, get arrow styles and adjust tip position
		if(settings.showArrow){
			var theTipArrow = theTip.find('.kjc_arrow');
			theTipArrow.borderLeft = theTipArrow.getCSS('borderLeftWidth');
			theTipArrow.borderRight = theTipArrow.getCSS('borderRightWidth');
			theTipArrow.width = theTipArrow.borderLeft + theTipArrow.borderRight;
			theTipArrow.left = theTipArrow.getCSS('left');
			theTipArrow.bottom = theTipArrow.getCSS('bottom');
			
			theTipStyle.left = theTipStyle.left - (theTipArrow.borderLeft + theTipArrow.left);
		}
		
		
		// ADJUST POSITION BASED ON ELEMENT TYPE
		if(e.is('input[type="checkbox"]')){
			theTipStyle.left = theTipStyle.left - 6;
			theTipStyle.bottom = theTipStyle.bottom + 6;
		}
		
		if(e.is('input[type="text"]') || e.is('input[type="password"]') || e.is('input[type="email"]') || e.is('textarea')){
			
			switch(settings.posX){
				case 'left':
					theTipStyle.left = theTipStyle.left - e.width + 6;
					theTipStyle.bottom = theTipStyle.bottom - 3;
					break;
				
				default:
					theTipStyle.left = theTipStyle.left - 10;
					theTipStyle.bottom = theTipStyle.bottom - 3;
					break;		
			}
			
			if(settings.fadeOnFocus){
				fadeOnFocus(e,id,settings);
			}
		}

		
		// ADJUST POSITION if overflowing screen edge
		var rightSide = docWidth - (theTipStyle.left + theTip.width);
		if(rightSide < 0){
			switch(settings.overflow){
				case 'flip':
					theTipStyle.left = theTipStyle.left - theTip.width + theTipArrow.width;
					theTipArrow.css('left',(theTip.width - (theTip.borderLeft + theTip.borderRight + theTipArrow.left + theTipArrow.width))+'px');
					break;
					
				default:
					theTipStyle.left = theTipStyle.left + rightSide-3;
					theTipArrow.css('left',(theTipArrow.left + ((rightSide-3)*-1))+'px');
					break;
			}
		}
		
		var leftSide = theTipStyle.left;
		if(leftSide < 0){
			switch(settings.overflow){
				case 'flip':
					theTipStyle.left = theTipStyle.left + theTip.width + theTipArrow.width;
					theTipArrow.css('left',(theTip.width - (theTip.borderLeft + theTip.borderRight + theTipArrow.left + theTipArrow.width))+'px');
					break;
					
				default:
					theTipStyle.left = theTipStyle.left + (leftSide*-1)+3;
					theTipArrow.css('left',(theTipArrow.left + (leftSide-3))+'px');
					break;
			}
		}
		
		
		switch(settings.arrowPos){
			case 'right':
				theTipStyle.left = theTipStyle.left - theTip.width + theTipArrow.width;
				theTipArrow.css('left',(theTip.width - (theTip.borderLeft + theTip.borderRight + theTipArrow.left + theTipArrow.width))+'px');
				break;
				
			case 'middle':
				theTipStyle.left = theTipStyle.left - (theTip.width/2) + theTipArrow.width;
				theTipArrow.css('left',((theTip.width/2) - (theTip.borderLeft + theTip.borderRight + theTipArrow.left + theTipArrow.width))+'px');
				break;
				
			default:
				// do nothing
				break;
		}

		
		// set the Error Tip position
		theTip.css('left',theTipStyle.left+'px');
		theTip.css('bottom',theTipStyle.bottom+'px');
		
		
			
		// show the error tip	
		theTip.fadeIn(settings.fadeIn);
		
		// Auto FadeOut
		if(settings.autoFade != false){
		
			// clear pre-existing timeout for this errortip (if it exists)
			clearTimeout($.errortip.autoFade['kjc_errortip_'+id]);
			
			// set timeout for autoFade
			$.errortip.autoFade['kjc_errortip_'+id] = setTimeout(function(){
				clear(e,settings);
			},settings.autoFade)
		}
		
		// What DOM element to return
		//		Defaults to returning jQuery selector object, but can be overridden to return errortip object
		if(settings.returnTip){
			return theTip;
		}else{
			return e;
		}
	}
	
	
	// +===============================+
	// | ErrorTip CLEAR				   |
	// +===============================+
	clear = function(e,settings){
		var id = e.attr('id');
		
		$('[data-uniq_id="kjc_errortip_'+id+'"]')
			.fadeOut(settings.fadeOut,function(){
				$(this).remove();
			});
	}
	
	
	// ========================================================================
	// Public Functions
	// ========================================================================
	
	$.errortip = new Object();
	
	/*
		ErrorTip Clear All - clears all 
	*/
	$.errortip.clear = function(){
		$('[data-id="kjc_errortip"]').remove();
	}
	
	
	
	// ========================================================================
	// Private Functions
	// ========================================================================
	
	// Holds Auto Fadeout Timeouts
	$.errortip.autoFade = new Array();	
	
	// If the errortip is auto fading out and ther user hovers over the errortip,
	// this will stop the fadeout and put the tooltip back to full opacity
	$.errortip.autoFade.stop = function(e,id){
		$(e).stop().css('opacity','1');
	}
	
	// gets the CSS style specified, trims off "px" and parses it as an integer
	$.fn.getCSS = function(style) {
		return parseInt(this.css(style).slice(0,-2));
	};
	
	
	function fadeOnFocus(e,id,settings){
		e.focus(function(){
			$('[data-uniq_id="kjc_errortip_'+id+'"]').fadeTo(settings.fadeOnFocusSpeed,settings.fadeOnFocusAmt);
		});
		
		e.blur(function(){
			$('[data-uniq_id="kjc_errortip_'+id+'"]').fadeTo(settings.fadeOnFocusSpeed,1);
		});
	}

		
})( jQuery );
