/* 
Description: 
	Position a tooltip and inner arrow relative to a triggering element and/or mouse coordinates.
	Meant to be used by other objects as a static method or mix-in.
created: 		5/2008
last modified: 	7/4/2008 by timw
version 0.42
	
*/

//var TipPositionManager = {
//function PositionTip(elTrigger, elTip, evt, hPrefs){
var TipPositionManager = Class.create({

	initialize: function(preferences){
		this.setOptions(preferences);
	},
	
	setOptions: function(preferences){
		this.prefs = Object.extend({
			orientation: 'east',	// prefer the tip element to be oriented North, South, East, or West
			xOffset: 0,				// horizontal distance between tip element and trigger
			yOffset: 0,				// vertical distance between tip element and trigger
			minMargin: 10, 			// minimum distance between tip element and edge of viewport
			useCentering: true,		// center horizontally for north/south; center vertically for east/west
			useArrow: true,			// use and arrow div to point from tip to target?
			arrowClassName: 'arrow', // classname of arrow (will create if it doesn't exist)
			arrowDimensions: {width: 10, height: 10} // dimension can be obtained when hidden, hardcoding
		}, preferences || {});		
	},
	
	setPosition: function(elTrigger, elTip, mouseCoords){
		var tipLeft, tipTop;
		var elTrigger = $(elTrigger);
		var elTip = $(elTip);
		var tipOrientClass = this.prefs.orientation;
		
		// make sure we don't already have an orientation class set on the tip
		$w('north south east west').each(function(orient){
			elTip.removeClassName(orient);
		});
		
		// get the arrow if it exists, create it if wanted
		var elArrow = elTip.down('.' + this.prefs.arrowClassName);
		
		if(this.prefs.useArrow && (typeof(elArrow) == 'undefined')){
			elArrow = new Element('div', {className: this.prefs.arrowClassName}) ;
			elTip.insert({top: elArrow});
		}
		
		//get various dimensions and positions
		//elTrigger.makePositioned(); // force hasLayout on trigger for ie, otherwise ie misreports trigOffset.top
		
		var trigDim = elTrigger.getDimensions();
		var trigOffset  = elTrigger.cumulativeOffset();
		
		// incorporate mouse coordinates if provided.
		var useMouse = (typeof mouseCoords == 'undefined') ? false : true;
		
		var tipDim = elTip.getDimensions();
		var viewportDim = document.viewport.getDimensions();
		var viewportOffset = document.viewport.getScrollOffsets();
		
		if(this.prefs.useArrow){
			var arrowTop = 0;
			var arrowLeft = 0;
			//var arrowDim = elArrow.getDimensions();//{width: 50, height:50};
			var arrowDim = this.prefs.arrowDimensions;
		}

		// calculate left position:
		switch(this.prefs.orientation){
			case 'north':
			case 'south':
				if(this.prefs.useCentering){
					if(useMouse){
						tipLeft = (viewportOffset.left + mouseCoords.left + this.prefs.xOffset) - Math.ceil(tipDim.width/2);
					}
					else{
						tipLeft = (trigOffset.left + this.prefs.xOffset) - Math.ceil(tipDim.width/2 - trigDim.width/2);
					}
				}
				else{
					if(useMouse){
						tipLeft = (mouseCoords.left + viewportOffset.left + this.prefs.xOffset);
					}
					else{
						tipLeft = (trigOffset.left + this.prefs.xOffset);
					}
				}			
				if(tipLeft < (this.prefs.minMargin + this.prefs.xOffset + viewportOffset.left)){ // too far to the left;
					tipLeft = this.prefs.minMargin + this.prefs.xOffset + viewportOffset.left;
				}

				if((tipLeft + tipDim.width + this.prefs.minMargin) > (viewportDim.width + viewportOffset.left)){ // too far to the right
					tipLeft = (viewportDim.width + viewportOffset.left) - (tipDim.width + this.prefs.minMargin);
				}
				if (this.prefs.useArrow) {
					if(useMouse){
						arrowLeft = ((mouseCoords.left + viewportOffset.left) - Math.ceil(arrowDim.width/2)) - tipLeft;
					}
					else{
						arrowLeft = trigOffset.left - tipLeft;
					}
					if(arrowLeft < 0){ // arrow is left of tip
						arrowLeft = 0;
					}
					else if((arrowLeft + arrowDim.width) > tipDim.width){ // arrow is right of tip
						arrowLeft = tipDim.width - arrowDim.width;	
					}
				}
				break;
			case 'east':
				if(useMouse){
					tipLeft = (viewportOffset.left + mouseCoords.left) + this.prefs.xOffset;
				}
				else{
					tipLeft = trigOffset.left + trigDim.width + this.prefs.xOffset;
				}
				
				if (this.prefs.useArrow) {arrowLeft = 0;}
				if((tipLeft + tipDim.width + this.prefs.minMargin - viewportOffset.left) > viewportDim.width){ // too far to the right
					if(useMouse){
						tipLeft = (viewportOffset.left +mouseCoords.left) - tipDim.width - this.prefs.xOffset;
					}
					else{
						tipLeft = (trigOffset.left) - tipDim.width - this.prefs.xOffset;
					}
					if (this.prefs.useArrow) {arrowLeft = tipDim.width - arrowDim.width;}
					tipOrientClass = 'west';
				}
				break;
			case 'west':
				if(useMouse){
					tipLeft = (viewportOffset.left + mouseCoords.left)  - (this.prefs.xOffset + tipDim.width);
				}
				else{
					tipLeft = trigOffset.left - (this.prefs.xOffset + tipDim.width);
				}
				
				if (this.prefs.useArrow) {arrowLeft = tipDim.width - arrowDim.width;}
				if(tipLeft < (this.prefs.minMargin + this.prefs.xOffset + viewportOffset.left)){ // too far to the left;
					if(useMouse){
						tipLeft = (viewportOffset.left + mouseCoords.left) + this.prefs.xOffset;
					}
					else{tipLeft = trigOffset.left + trigDim.width + this.prefs.xOffset;}
					if (this.prefs.useArrow) {arrowLeft = 0;}
					tipOrientClass = 'east';
				}
				break;
		}
		
		// calculate top position
		switch(this.prefs.orientation){
			case 'east':
			case 'west':
				if(this.prefs.useCentering){
					if(useMouse){
						tipTop = ((viewportOffset.top + mouseCoords.top) - this.prefs.yOffset - (Math.ceil(tipDim.height/2)));
					}
					else{
						tipTop = (trigOffset.top - this.prefs.yOffset - (Math.ceil(tipDim.height/2 - trigDim.height/2)));
					}
				}
				else{
					if(useMouse){
						tipTop = ((viewportOffset.top + mouseCoords.top) - this.prefs.yOffset);
					}
					else{
						tipTop = (trigOffset.top - this.prefs.yOffset);
					}
				}
				
				if(tipTop < (this.prefs.minMargin + viewportOffset.top)){ // too far above
					tipTop = viewportOffset.top + this.prefs.minMargin;
				}
				
				if((tipTop + tipDim.height) > (viewportDim.height + viewportOffset.top)){ // too far below
					tipTop -= (tipTop + tipDim.height + this.prefs.minMargin) - (viewportDim.height + viewportOffset.top);	
				}
				
				if (this.prefs.useArrow) {
					if(this.prefs.useCentering){
						if(useMouse){
							arrowTop = ((viewportOffset.top + mouseCoords.top) - tipTop) - Math.ceil(arrowDim.height/2);
						}
						else{
							// arrowDim.height was reporting as 0 when tip was not visible.
							arrowTop = (trigOffset.top - tipTop) + Math.ceil(trigDim.height/2 - arrowDim.height/2);	
						}
					}
					else{
						arrowTop = trigOffset.top - tipTop;
					}
					if(arrowTop < 0){// arrow is above tip
						arrowTop = 0;
					}
					else if((arrowTop + arrowDim.height) > tipDim.height){ // arrow is below tip
						arrowTop = tipDim.height - arrowDim.height;
					}
				}
				break;
				
			case 'north':
				if(useMouse){
					tipTop = (((viewportOffset.top + mouseCoords.top) - this.prefs.yOffset) - tipDim.height);
				}
				else{
					tipTop = ((trigOffset.top - this.prefs.yOffset) - tipDim.height);
				}
	
				if(this.prefs.useArrow){arrowTop = tipDim.height - arrowDim.height;}
				
				if((tipTop - this.prefs.minMargin) < viewportOffset.top){ // too far above
					if(useMouse){
						tipTop = ((viewportOffset.top + mouseCoords.top) + this.prefs.yOffset);
					}
					else{
						tipTop = (trigOffset.top + this.prefs.yOffset + trigDim.height);
					}
		
					if(this.prefs.useArrow){arrowTop = 0;}
					tipOrientClass = 'south';
				}
				break;
				
			case 'south':
				
				if(useMouse){
					tipTop = ((viewportOffset.top + mouseCoords.top) + this.prefs.yOffset);
				}
				else{
					tipTop = (trigOffset.top + this.prefs.yOffset + trigDim.height);
				}
				
				if((tipTop + this.prefs.minMargin + tipDim.height) > (viewportDim.height + viewportOffset.top)){ // too far below
					if(useMouse){
						tipTop = (((viewportOffset.top + mouseCoords.top) - this.prefs.yOffset) - tipDim.height);
					}
					else{
						tipTop = ((trigOffset.top - this.prefs.yOffset) - tipDim.height);
					}
	
					if(this.prefs.useArrow){arrowTop = tipDim.height - arrowDim.height;}
					tipOrientClass = 'north';
				}
				break;
		}
		
		elTip.setStyle({left: tipLeft+'px', top: tipTop+'px'});
		elTip.addClassName(tipOrientClass);
		if(this.prefs.useArrow){elArrow.setStyle({left: arrowLeft + 'px', top: arrowTop + 'px'});}
		
		return elTip;
	},
	// call before postiontip, passing tip as elParent
	/*
		Purpose: ie6 specific function to fix issue where selects appear on top of positioned elements.
		callType: 'activate' | 'deactivate' 	// to add or remove the iframe
		elParent: element reference 			// the element for the iframe to match position & dimensions.
		shimId: string							// optional id of iframe.
	*/
	iFrameShimFix: function (callType, elParent, shimId) {
		var id =  shimId || 'ie6_shim';
		if (callType == "activate") {
			var parentDim = elParent.getDimensions();
			var shim = document.createElement('iframe');
			shim.setAttribute('width',parentDim.width);
			shim.setAttribute('id', shimId);
			shim.setAttribute('height',parentDim.height);
			shim.style.position = 'absolute';
			shim.style.zIndex = '-1';
			shim.style.left = '0px';
			shim.style.top = '0px';
			elParent.appendChild(shim);
			shim.style.filter = 'alpha(opacity=0)';
		}
		
		if (callType == "deactivate") {
			if($(shimId)){ $(shimId).remove(); }
		}
	}

});


