/*  PopoutController (requires Prototype javascript framework, scriptaculous optional for effects)
 *  Jeff Weakland
 *  10/28/2010
 *  Version 1.0
 *--------------------------------------------------------------------------*/

/* 
 * Abstract base class 
 */
var PopoutController = Class.create({
	initialize: function (initObj) {
		if (this.constructor.superclass != null) {	
			/** required initialization parameters **/		
			this._ajaxURI = initObj.ajaxURI;			
			
			if (this._ajaxURI == null) {
				throw ("Missing required parameters: ajaxURI and baseClassName are required.");
			}
						
			/** optional initialization parameters **/
			this._arrowClassName = initObj.arrowClassName;
			this._popoutPrefix = initObj.popoutPrefix != null ? initObj.popoutPrefix : 'popout-';			
			this._padding = initObj.padding != null ? initObj.padding : 5; 
			this._enableEffects = initObj.enableEffects != null ? initObj.enableEffects : false;
			this._effectDuration = initObj.effectDuration != null ? initObj.effectDuration : 0.2;
			
			/** private for internal use only **/
			this._targetObj = null;
			this._popoutTimer = null;						
			this._generatedPopoutClassName = "generated-popout";
		}else {
			throw("PopoutController abstract class may not be constructed.");			
		}
	},
	
	showPopout: function(targetObj, queryString) {		
		// hide popouts that are currently showing just in case
		$$('div.' + this._generatedPopoutClassName).invoke('hide');
		this._targetObj = $(targetObj);
		var popoutId = this._popoutPrefix + $(targetObj).identify();		
		if (this._popoutTimer != null) this._cancelTimer();
		
		var _self = this; 
		this._popoutTimer = setTimeout(function() { _self._getPopout(popoutId, queryString); }, 500);				
	},	
	
	hidePopout: function(targetObj) {
		this._cancelTimer();
		var popoutId = this._popoutPrefix + $(targetObj).identify();		
		var popoutContainer = $(popoutId);	 
		if (popoutContainer != null) {
			if (this._enableEffects && !Prototype.Browser.IE) {
				try {
					popoutContainer.fade({duration: this._effectDuration, queue: {position: 'end', scope: popoutId} });
				}catch(e) {
					// scriptaculous probably isn't loaded show just hide the popout without effects
					popoutContainer.hide();
				}
			}else {
				popoutContainer.hide();
			}
		}
	},
	
	_activatePopout: function(popoutContainer, position) {
		popoutContainer.setStyle( {
			left : Math.floor(position.left) + 'px',
			top : Math.floor(position.top) + 'px'
		});
		
		if (this._enableEffects && !Prototype.Browser.IE) {
			try {
				popoutContainer.appear({duration: this._effectDuration, queue: {position: 'end', scope: popoutContainer.id} });
			}catch (e) {
				// scriptaculous probably isn't loaded show just show the popout without effects
				popoutContainer.show();
			}
		}else {
			popoutContainer.show();
		}
	},
	
	_cancelTimer: function() {
		clearTimeout(this._popoutTimer);
		this._popoutTimer = null;		
	},
	
	_getPopout: function(popoutId, queryString) {		
		var popoutContainer = $(popoutId);  	
				
		if (popoutContainer == null) {		
			var uri = this._ajaxURI +  "?" + queryString;
			var _self = this;
			new Ajax.Request(uri,
				{
					method : 'get',
					onSuccess : function(transport) {
						if (_self._popoutTimer != null) {
							popoutContainer = new Element('div', {
								'id': popoutId,			
								'class': _self._generatedPopoutClassName,
								'style': 'display:none; margin: 0; padding: 0; border: 0; z-index: 10000; position: absolute;'								
							});										
							popoutContainer.innerHTML = transport.responseText;											
							document.body.appendChild(popoutContainer);					
							_self._overlayPopout(popoutContainer);
						}
					}
				});
		} else if (this._popoutTimer != null) {
			// the div has already been created, just show it
			this._overlayPopout(popoutContainer);
		}
	},		
				
	_getViewportInfo: function() {				
		var viewportDimensions = document.viewport.getDimensions();
		var viewportOffsets = document.viewport.getScrollOffsets();
		var ViewportInfo = {
			width: viewportDimensions.width,
			height: viewportDimensions.height,
			offsetLeft: viewportOffsets.left,
			offsetTop: viewportOffsets.top 
		};
		return ViewportInfo;
	}, 
	
	_getTargetPositionInfo: function() {
		var objPosition = this._targetObj.cumulativeOffset();
		var objDimensions = this._targetObj.getDimensions();
		
		var TargetPositionInfo = {
			xPosRight: objPosition.left + objDimensions.width,
			xPosLeft: objPosition.left,
			xPosMiddle: Math.floor(objPosition.left + (objDimensions.width / 2)),
			yPosTop: objPosition.top,
			yPosBottom: objPosition.top + objDimensions.height,
			yPosMiddle: Math.floor(objPosition.top + (objDimensions.height / 2))
		};
		return TargetPositionInfo;
	},
	
	_getContainerDimensions: function(popoutContainer) {
		var popoutContainerDimensions = popoutContainer.getDimensions();		
		var ContainerDimensions = {
			width: popoutContainerDimensions.width,
			height: popoutContainerDimensions.height
		};
		return ContainerDimensions;		
	},
	
	_getArrowDimensions: function (popoutContainer, popoutArrow) {		
		var arrowWidth = 0;
		var arrowHeight = 0;
		if (popoutArrow != null) {
			//temporarily change container's display to block to calculate arrow dimensions		
			popoutContainer.setStyle({visibility: 'hidden', display: 'block'});
			var arrowDim = popoutArrow.getDimensions();			
			arrowWidth = arrowDim.width;
			arrowHeight = arrowDim.height;			
			popoutContainer.setStyle({visibility: '', display: 'none'});			
		}
		
		var ArrowDimensions = {
			width: arrowWidth,
			height: arrowHeight
		};
		return ArrowDimensions;
	}
});

var VerticalPopoutController = Class.create(PopoutController, {
	initialize: function($super, initObj) {
		$super(initObj);
		/** optional intialization parameters **/
		this._preferredPopoutDirection = ['above','below'].indexOf(initObj.preferredPopoutDirection) != -1 ? initObj.preferredPopoutDirection : 'below';
		this._leftOffset = initObj.leftOffset != null ? initObj.leftOffset : null;
		this._arrowMarginLeft = initObj.arrowMarginLeft != null ? initObj.arrowMarginLeft : 0;
		this._arrowMarginRight = initObj.arrowMarginRight != null ? initObj.arrowMarginRight : 0;		
		
		/** private for internal use only **/
		this._arrowTopClassName = this._arrowClassName + '-top';		
		this._arrowTopLeftClassName = this._arrowClassName + '-topleft';
		this._arrowTopRightClassName = this._arrowClassName + '-topright';
		
		this._arrowBottomClassName = this._arrowClassName + '-bottom';
		this._arrowBottomLeftClassName = this._arrowClassName + '-bottomleft';
		this._arrowBottomRightClassName = this._arrowClassName + '-bottomright';
	},
	
	_overlayPopout: function(popoutContainer) {												
		if (popoutContainer != null) {						
			var ViewportInfo = this._getViewportInfo();
			var TargetPositionInfo = this._getTargetPositionInfo();
			var ContainerDimensions = this._getContainerDimensions(popoutContainer);
												
			// set the position of the popout
			// initialize display to the middle of the page
			var popoutLeft = ViewportInfo.offsetLeft + (ViewportInfo.width / 2) - (ContainerDimensions.width / 2);
			var popoutTop = ViewportInfo.offsetTop + (ViewportInfo.height / 2) - (ContainerDimensions.height / 2);
																										
			var viewportSpaceRight = ViewportInfo.width - (TargetPositionInfo.xPosMiddle - ViewportInfo.offsetLeft);
			var viewportSpaceLeft =  TargetPositionInfo.xPosMiddle - ViewportInfo.offsetLeft; 
			var viewportSpaceAbove = TargetPositionInfo.yPosTop - ViewportInfo.offsetTop;
			var viewportSpaceBelow = (ViewportInfo.height + ViewportInfo.offsetTop) - TargetPositionInfo.yPosBottom ;
					
			// determine top position of popout
			var isArrowAbove = false;
			switch (this._preferredPopoutDirection) {
			case 'above' :
				if (viewportSpaceAbove > ContainerDimensions.height + this._padding) {					
					popoutTop = TargetPositionInfo.yPosTop - ContainerDimensions.height - this._padding;
				}else {
					isArrowAbove = true;
					popoutTop = TargetPositionInfo.yPosBottom + this._padding;
				}
				break;	
			case 'below' :
			default :
				if (viewportSpaceBelow > ContainerDimensions.height + this._padding) {
					isArrowAbove = true;
					popoutTop = TargetPositionInfo.yPosBottom + this._padding; 
				}else {
					popoutTop = TargetPositionInfo.yPosTop - ContainerDimensions.height - this._padding;
				}			
				break;
			}																				
			
			var containerOffsetLeft = this._leftOffset != null ? this._leftOffset : (ContainerDimensions.width / 2) * -1;
			if (viewportSpaceLeft + containerOffsetLeft < 0) {
				// adjust spacing left
				containerOffsetLeft = viewportSpaceLeft * -1;
			}else if (viewportSpaceRight < (ContainerDimensions.width + containerOffsetLeft)) {
				// adjust spacing right
				containerOffsetLeft = (ContainerDimensions.width - viewportSpaceRight) * -1;
			}
			popoutLeft = TargetPositionInfo.xPosMiddle + containerOffsetLeft;
									
			// position the arrow and set the arrow style
			var popoutArrow = null;												
			if (this._arrowClassName != null) {
				popoutArrow = popoutContainer.down('.'+ this._arrowClassName);
				if (popoutArrow != null) {
					var ArrowDimensions = this._getArrowDimensions(popoutContainer, popoutArrow);										
					var arrowLeft = (containerOffsetLeft * -1) - (ArrowDimensions.width / 2);
																																			
					//respect arrow margins
					if (arrowLeft < this._arrowMarginLeft) { 
						arrowLeft = this._arrowMarginLeft;						
						popoutArrow.className = isArrowAbove ? this._arrowClassName + ' ' + this._arrowTopLeftClassName : this._arrowClassName + ' ' + this._arrowBottomLeftClassName;
					}else if (arrowLeft > ContainerDimensions.width - ArrowDimensions.width - this._arrowMarginRight) { 
						arrowLeft = ContainerDimensions.width - ArrowDimensions.width - this._arrowMarginRight;
						popoutArrow.className = isArrowAbove ? this._arrowClassName + ' ' + this._arrowTopRightClassName : this._arrowClassName + ' ' + this._arrowBottomRightClassName;
					}else {
						popoutArrow.className = isArrowAbove ? this._arrowClassName + ' ' + this._arrowTopClassName : this._arrowClassName + ' ' + this._arrowBottomClassName;
					}
					popoutArrow.setStyle({left: Math.floor(arrowLeft) + 'px'});
					
					//adjust popoutTop for the arrow offset
					popoutTop = isArrowAbove ? popoutTop - this._getArrowOffset(popoutContainer, popoutArrow, 'below') : popoutTop + this._getArrowOffset(popoutContainer, popoutArrow, 'above');
				}
			}										
			
			this._activatePopout(popoutContainer, {top: popoutTop, left: popoutLeft});											
		}				
	},
					
	_getArrowOffset : function(popoutContainer, popoutArrow, popoutDirection) {
		var arrowOffset = 0;						
		if (popoutArrow != null) {
			//temporarily change container's display to block to calculate arrow dimensions		
			popoutContainer.setStyle({visibility: 'hidden', display: 'block'});
			arrowOffset = popoutDirection == 'above' ? new Number(popoutArrow.getStyle("bottom").sub("px", "")).valueOf() : new Number(popoutArrow.getStyle("top").sub("px", "")).valueOf();
			popoutContainer.setStyle({visibility: '', display: 'none'});			
		}
		return arrowOffset;		
	} 		
});

var HorizontalPopoutController = Class.create(PopoutController, {
	initialize: function($super, initObj) {
		$super(initObj);
		/** optional intialization parameters **/
		this._preferredPopoutDirection = ['left','right'].indexOf(initObj.preferredPopoutDirection) != -1 ? initObj.preferredPopoutDirection : 'right';
		this._topOffset = initObj.topOffset != null ? initObj.topOffset : null;
		this._arrowMarginTop = initObj.arrowMarginTop != null ? initObj.arrowMarginTop : 0;
		this._arrowMarginBottom = initObj.arrowMarginBottom != null ? initObj.arrowMarginBottom : 0;		
		
		/** private for internal use only **/
		this._arrowLeftClassName = this._arrowClassName + '-left';		
		this._arrowLeftTopClassName = this._arrowClassName + '-lefttop';
		this._arrowLeftBottomClassName = this._arrowClassName + '-leftbottom';
				
		this._arrowRightClassName = this._arrowClassName + '-right';
		this._arrowRightTopClassName = this._arrowClassName + '-righttop';
		this._arrowRightBottomClassName = this._arrowClassName + '-rightbottom';		 
	},
	
	_overlayPopout: function(popoutContainer) {												
		if (popoutContainer != null) {						
			var ViewportInfo = this._getViewportInfo();
			var TargetPositionInfo = this._getTargetPositionInfo();
			var ContainerDimensions = this._getContainerDimensions(popoutContainer);
												
			// set the position of the popout
			// initialize display to the middle of the page
			var popoutLeft = ViewportInfo.offsetLeft + (ViewportInfo.width / 2) - (ContainerDimensions.width / 2);
			var popoutTop = ViewportInfo.offsetTop + (ViewportInfo.height / 2) - (ContainerDimensions.height / 2);
																										
			var viewportSpaceRight = ViewportInfo.width - (TargetPositionInfo.xPosRight - ViewportInfo.offsetLeft);
			var viewportSpaceLeft =  TargetPositionInfo.xPosLeft - ViewportInfo.offsetLeft; 
			var viewportSpaceAbove = TargetPositionInfo.yPosMiddle - ViewportInfo.offsetTop;
			var viewportSpaceBelow = (ViewportInfo.height + ViewportInfo.offsetTop) - TargetPositionInfo.yPosMiddle ;
					
			// determine left position of popout
			var isArrowLeft = false;
			switch (this._preferredPopoutDirection) {
			case 'left' :
				if (viewportSpaceLeft > ContainerDimensions.width + this._padding) {					
					popoutLeft = TargetPositionInfo.xPosLeft - ContainerDimensions.width - this._padding;
				}else {
					isArrowLeft = true;
					popoutLeft = TargetPositionInfo.xPosRight + this._padding;
				}
				break;	
			case 'right' :
			default :
				if (viewportSpaceRight > ContainerDimensions.width + this._padding) {
					isArrowLeft = true;
					popoutLeft = TargetPositionInfo.xPosRight + this._padding; 
				}else {
					popoutLeft = TargetPositionInfo.xPosLeft - ContainerDimensions.width - this._padding;
				}			
				break;
			}
																		
			// determine the top position of the popout	
			var containerOffsetTop = this._topOffset != null ? this._topOffset : (ContainerDimensions.height / 2) * -1;
			if (viewportSpaceAbove + containerOffsetTop < 0) {
				// adjust spacing above
				containerOffsetTop = viewportSpaceAbove * -1;				
			}else if (viewportSpaceBelow < (ContainerDimensions.height + containerOffsetTop)) {
				// adjust spacing below
				containerOffsetTop = (ContainerDimensions.height - viewportSpaceBelow) * -1;				
			}	
			popoutTop = TargetPositionInfo.yPosMiddle + containerOffsetTop;						
			
			// position the arrow and set the arrow style
			var popoutArrow = null;												
			if (this._arrowClassName != null) {
				popoutArrow = popoutContainer.down('.'+ this._arrowClassName);
				if (popoutArrow != null) {
					var ArrowDimensions = this._getArrowDimensions(popoutContainer, popoutArrow);					
					var arrowTop = (containerOffsetTop * -1) - (ArrowDimensions.height / 2);
																																			
					//respect arrow margins
					if (arrowTop < this._arrowMarginTop) { 
						arrowTop = this._arrowMarginTop;						
						popoutArrow.className = isArrowLeft ? this._arrowClassName + ' ' + this._arrowLeftTopClassName : this._arrowClassName + ' ' + this._arrowRightTopClassName;
					}else if (arrowTop > ContainerDimensions.height - ArrowDimensions.height - this._arrowMarginBottom) { 
						arrowTop = ContainerDimensions.height - ArrowDimensions.height - this._arrowMarginBottom;						
						popoutArrow.className = isArrowLeft ? this._arrowClassName + ' ' + this._arrowLeftBottomClassName : this._arrowClassName + ' ' + this._arrowRightBottomClassName;
					}else {
						popoutArrow.className = isArrowLeft ? this._arrowClassName + ' ' + this._arrowLeftClassName : this._arrowClassName + ' ' + this._arrowRightClassName;
					}
					popoutArrow.setStyle({top: Math.floor(arrowTop) + 'px'});
					
					//adjust popoutLeft for the arrow offset
					popoutLeft = isArrowLeft ? popoutLeft - this._getArrowOffset(popoutContainer, popoutArrow, 'right') : popoutLeft + this._getArrowOffset(popoutContainer, popoutArrow, 'left');
				}
			}									
			
			this._activatePopout(popoutContainer, {top: popoutTop, left: popoutLeft});																	
		}				
	},				
	
	_getArrowOffset : function(popoutContainer, popoutArrow, popoutDirection) {
		var arrowOffset = 0;						
		if (popoutArrow != null) {
			//temporarily change container's display to block to calculate arrow dimensions		
			popoutContainer.setStyle({visibility: 'hidden', display: 'block'});
			arrowOffset = popoutDirection == 'right' ? new Number(popoutArrow.getStyle("left").sub("px", "")).valueOf() : new Number(popoutArrow.getStyle("right").sub("px", "")).valueOf();
			popoutContainer.setStyle({visibility: '', display: 'none'});			
		}
		return arrowOffset;		
	} 
});

