var wm_pop = {};

wm_pop.mousePosition = {"X":-1, "Y":-1};

wm_pop.directionEnum = {"N":1,"E":2,"S":3,"W":4,"NE":5,"NW":6,"SE":7,"SW":8}//it does not really give meaning to have an enum if the popMenu code is not in the same file as the popMenu client code because VS intellisense cannot (yet) pick up javascript in linked resources

//		wm_pop.addEvent = function(pSender, pEventName, pEventHandler){
//			if (pSender.addEventListener){
//				pSender.addEventListener(pEventName, pEventHandler, false);
//			}
//			else if (pSender.attachEvent)
//			{
//				pSender["e" + pEventName + pEventHandler] = pEventHandler;
//				pSender[pEventName + pEventHandler] = function() { pSender["e" + pEventName + pEventHandler](window.event); }
//				pSender.attachEvent( "on" + pEventName, pSender[pEventName + pEventHandler] );
//			}
//		}

//		wm_pop.removeEvent = function(pSender, pEventName, pEventHandler){
//			if (pSender.removeEventListener){
//				pSender.removeEventListener( pEventName, pEventHandler, false );
//			}
//			else if (pSender.detachEvent)
//			{
//				pSender.detachEvent( "on" + pEventName, pSender[pEventName + pEventHandler] );
//				pSender[pEventName + pEventHandler] = null;
//				pSender["e" + pEventName + pEventHandler] = null;
//			}
//		}

wm_pop.addEvent = function(pSender, pEventName, pEventHandler){
	if (pSender.addEventListener){
		pSender.addEventListener(pEventName, pEventHandler, false);
	}
	else if (pSender.attachEvent){
		pSender.attachEvent("on" + pEventName, pEventHandler);
	}
}

wm_pop.removeEvent = function(pSender, pEventName, pEventHandler){
	if (pSender.removeEventListener){
		pSender.removeEventListener(pEventName, pEventHandler, false);
	}
	else if (pSender.detachEvent){
		pSender.detachEvent(pEventName, pEventHandler);
	}
}

wm_pop.getCoordinates = function(pElmID, pContainer){//this function only returns the coordinates relative to the document (as opposed to wm library version that returns relative to both document & viewport)
	var elm = document.getElementById(pElmID);
	
	var elmIsPositioned = elm.style.position != ""; //the position value will be empty (not null) in case the position is not defined
	
	var elm_width = elm.offsetWidth;
	var elm_height = elm.offsetHeight;
	var pageOffsetX = parentX = elm.offsetLeft;
	var pageOffsetY = parentY = elm.offsetTop;
	while (elm = elm.offsetParent){
		if (elm == pContainer){break;}
		if (elm.offsetParent == null){break;}//The following is relevant ONLY for viewport calculations, which is NOT necessary to do for popMenu. This line is a leftover from wm_positioning.js which I have not deleted here only because the line makes the logic more beautiful. However, the idea is to avoid sending document element to wm_getScrolling twice (first time then document is the parent and second time as document.parent (null), because wm_getScrolling will regard null as document)
		pageOffsetX += elm.offsetLeft;
		pageOffsetY += elm.offsetTop;
		
		if (elm.style.position != ""){;//if an element or a container is positioned, IE will automatically append the body margin to the elements positioning. However if not positioned, IE will NOT append body margin (FF will always append body margin correct)
			elmIsPositioned = true;
		}
	}

	//problem: IE does NOT append body.margin to the position of elements that is NOT relative or absolute positioned (either itself or through a container), however the mouse position is appended the body.margin, therefore in case of non-positioned elements it is not possible determine if the mouse is over the element except calculating a hack
	var IEBodyMarginHack_left = 0;
	var IEBodyMarginHack_top = 0;
	if (!elmIsPositioned){
		var hackSpan = document.createElement("span");
		hackSpan.appendChild(document.createTextNode("\u00a0"));
		document.body.appendChild(hackSpan);
		hackSpan.style.border = "solid 0px blue";
		var left_noPosition = hackSpan.offsetLeft;
		var top_noPosition = hackSpan.offsetTop;
		hackSpan.style.position = "absolute";
		var left_withPosition = hackSpan.offsetLeft;
		var top_withPosition = hackSpan.offsetTop;
		document.body.removeChild(hackSpan);
		
		IEBodyMarginHack_left = left_withPosition - left_noPosition;//since withPosition & noPosition will have the same values in FF, the hack will not destroy positioning in FF
		IEBodyMarginHack_top = top_withPosition - top_noPosition;
	}

	var pm_left = pageOffsetX + IEBodyMarginHack_left;
	var pm_top = pageOffsetY + IEBodyMarginHack_top;
	var pm_right = pm_left + elm_width;
	var pm_bottom = pm_top + elm_height;
	
	return {"left":pm_left, "top":pm_top, "right":pm_right, "bottom":pm_bottom}
}

wm_pop.getViewportDimensions = function (){
	var x,y;
	if (self.innerHeight) // all except Explorer
	{
		x = self.innerWidth;
		y = self.innerHeight;
	}
	else if (document.documentElement && document.documentElement.clientHeight)
		// Explorer 6 Strict Mode
	{
		x = document.documentElement.clientWidth;
		y = document.documentElement.clientHeight;
	}
	else if (document.body) // other Explorers
	{
		x = document.body.clientWidth;
		y = document.body.clientHeight;
	}
	return {"height":y,"width":x};	
}

wm_pop.getScrolling = function(pElm){//supply null for document scrolling
	var scrollLeft = 0;
	var scrollTop = 0;
	
	if (pElm){
		scrollLeft = (pElm.scrollLeft) ? pElm.scrollLeft : 0;
		scrollTop = (pElm.scrollTop) ? pElm.scrollTop : 0;
	}
	else if (document.documentElement && (document.documentElement.scrollTop || document.documentElement.scrollLeft)){
		scrollLeft = document.documentElement.scrollLeft;
		scrollTop = document.documentElement.scrollTop;
	}
	else if (document.body){
		scrollLeft = document.body.scrollLeft;
		scrollTop = document.body.scrollTop;
	}
	else if (window.pageXOffset){
		scrollLeft = window.pageXOffset;
		scrollTop = window.pageYOffset;
	}
	else{
		//old browsers and minor browsers
	}
	
	return {"X":scrollLeft,"Y":scrollTop};
}

wm_pop.keepInViewportCoordinates = function(pTargetTop, pTargetRight, pTargetBottom, pTargetLeft){
	var viewportDimensions = wm_pop.getViewportDimensions();
	var viewportWidth = viewportDimensions.width;
	var viewportHeight = viewportDimensions.height;
	var scrolling = wm_pop.getScrolling();
	var maxLeft = viewportWidth - (pTargetRight - pTargetLeft) + scrolling.X;
	var maxTop = viewportHeight - (pTargetBottom - pTargetTop) + scrolling.Y;
	if (maxLeft < scrolling.X){maxLeft = scrolling.X;}
	if (maxTop < scrolling.Y){maxTop = scrolling.Y;}
	
	var targetLeft = pTargetLeft;
	var targetTop = pTargetTop;
	if (pTargetLeft > maxLeft){
		targetLeft = maxLeft;
	}
	if (pTargetTop > maxTop){
		targetTop = maxTop;
	}
	if (pTargetLeft < scrolling.X){
		targetLeft = scrolling.X;
	}
	if (pTargetTop < scrolling.Y){
		targetTop = scrolling.Y;
	}
	
	return {"targetLeft":targetLeft,"targetTop":targetTop};
}

wm_pop.setMousePosition = function(pEvt){//eventhandler for document.mousemove but only as long as a popMenu is active. After hiding the popMenu, this eventhandler will be removed for document.mousemove
	var evt = (pEvt) ? pEvt : window.event;
	
	//mouse coordinates document
	var X = 0, Y = 0;
	if (evt.pageX){
		X = evt.pageX;
		Y = evt.pageY;
	}
	else if (evt.clientX){
		X = evt.clientX + ((document.documentElement.scrollLeft) ? document.documentElement.scollLeft : document.body.scrollLeft);
		Y = evt.clientY + ((document.documentElement.scrollTop) ? document.documentElement.scrollTop : document.body.scrollTop);
	}
	else{
		X = Y = 0;
	}
	
	wm_pop.mousePosition.X = X;
	wm_pop.mousePosition.Y = Y;
}

wm_pop.mouseIsOverPopMenu = function(pSenderID, pPopMenuID){
	var X = wm_pop.mousePosition.X;
	var Y = wm_pop.mousePosition.Y;
	var popMenu_coordinates = wm_pop.getCoordinates(pPopMenuID);

	var mouseIsOverTarget = (X >= popMenu_coordinates.left && X <= popMenu_coordinates.right && Y >= popMenu_coordinates.top && Y <= popMenu_coordinates.bottom);

	//in case the mouse moves from popMenu to sender, the popMenu mouseout will start the hiding process and that process will not be circumvented by an eventual mouseover on the sender 
	//, therefore I need to test if the mouse is over sender as well.
	if (!mouseIsOverTarget){
		var sender_coordinates = wm_pop.getCoordinates(pSenderID);
		mouseIsOverTarget = (X >= sender_coordinates.left && X <= sender_coordinates.right && Y >= sender_coordinates.top && Y <= sender_coordinates.bottom);
	}

	return mouseIsOverTarget;
}

wm_pop.show = function(pSenderID, pPopMenuID, pConfig){
	wm_pop.addEvent(document, "mousemove", wm_pop.setMousePosition);
//			document.onmousemove = wm_pop.setMousePosition;

	var sender = document.getElementById(pSenderID);
	var popMenu = document.getElementById(pPopMenuID);
	document.body.appendChild(popMenu);

//			wm_pop.addEvent(popMenu, "mouseout", function(){wm_pop.hide(pSenderID, pPopMenuID);});
	popMenu.onmouseout = function(){wm_pop.hide(pSenderID, pPopMenuID);};

	var sender_coordinates = wm_pop.getCoordinates(pSenderID);
	var sender_left = sender_coordinates.left;
	var sender_top = sender_coordinates.top;
	var sender_right = sender_coordinates.right;// sender_left + sender.offsetWidth;
	var sender_bottom = sender_coordinates.bottom;// sender_top + sender.offsetHeight;
	var sender_width = sender_right - sender_left;
	var sender_height = sender_bottom - sender_top;

	var defaults = {
		positionReference : "elmBottomRight", //mouse, elmBottomLeft, elmBottomRight, elmTopLeft, elmTopRight, elmTop, elmRight, elmBottom, elmLeft (elmTop will set the reference point so that popMenu will horizontal center over the sender, while elmRight will place the popMenu so it vertically centers to the right of the sender - these positions cannot be used with all directions)
		direction : "SE", //SE, NE, SW, NW, N, E, S, W (SE : South East ...)
		hOffset : 0,
		vOffset : 0,
		hideDelay : 500, //delay in milliseconds before the popup hides after mouseout, set to -1 to never hide (that could be useful if popping up a box that have a close button, so that the user can move away the mouse and still see the popup and close the popup himself by clicking the close button)
		keepInViewport : true //in case the calculated position will place part of the popMenu outside the Viewport, the position will be recalculated to push the whole popMenu inside the Viewport (if possible)
	}

	for (var p in defaults){
		this[p] = (pConfig == null || typeof(pConfig[p]) == "undefined") ? defaults[p] : pConfig[p];
	}

	//dependency defaults - default offset depends on which side of the sender the popup will be positioned
	if (pConfig == null || (typeof(pConfig.vOffset) == "undefined" && typeof(pConfig.hOffset) == "undefined")){
		if (this.positionReference == "mouse"){
			this.hOffset = 2;
			this.vOffset = 2;
		}
		if (this.positionReference == "elmTopLeft"){
			this.hOffset = -5;
			this.vOffset = 0;
			this.direction = "SW"; //SW : South West so that popMenu is expanding down left away from the sender (default is always away from the sender and if not colliding with that principle then down)
		}
		else if (this.positionReference == "elmTopRight"){
			this.hOffset = 5;
			this.vOffset = 0;
		}
		else if (this.positionReference == "elmBottomLeft"){
			this.hOffset = -5;
			this.vOffset = 5;
			this.direction = "SW";
		}
		else if (this.positionReference == "elmBottomRight"){
			this.hOffset = 5;
			this.vOffset = 5;
		}
		else if (this.positionReference == "elmTop"){
			this.hOffset = 0;
			this.vOffset = -5;
		}
		else if (this.positionReference == "elmRight"){
			this.hOffset = 5;
			this.vOffset = 0;
		}
		else if (this.positionReference == "elmBottom"){
			this.hOffset = 0;
			this.vOffset = 5;
		}
		else if (this.positionReference == "elmLeft"){
			this.hOffset = -5;
			this.vOffset = 0;
		}
	}
	
	//dependency defaults - default direction depends on which side of the sender the popup will be positioned
	if (pConfig == null || typeof(pConfig.direction) == "undefined"){
		if (this.positionReference == "mouse"){
			this.direction = "SE";
		}
		if (this.positionReference == "elmTopLeft"){
			this.direction = "SW"; //SW : South West so that popMenu is expanding down left away from the sender (default is always away from the sender and if not colliding with that principle then down)
		}
		else if (this.positionReference == "elmTopRight"){
			this.direction = "SE";
		}
		else if (this.positionReference == "elmBottomLeft"){
			this.direction = "SW";
		}
		else if (this.positionReference == "elmBottomRight"){
			this.direction = "SE";
		}
		else if (this.positionReference == "elmTop"){
			this.direction = "N";
		}
		else if (this.positionReference == "elmRight"){
			this.direction = "E";
		}
		else if (this.positionReference == "elmBottom"){
			this.direction = "S";
		}
		else if (this.positionReference == "elmLeft"){
			this.direction = "W";
		}
	}

	//ok, place the popMenu
//			popMenu.style.visibility = "hidden"
	popMenu.style.display = "block"; //need to display blockmenu to get dimensions
	var popMenu_width = popMenu.offsetWidth;
	var popMenu_height = popMenu.offsetHeight;
	popMenu.style.display = "none";
	popMenu.style.visibility = "visible"

	var target_top = 0;
	var target_left = 0;
	var directionAccommodating_left = (this.direction == "SW" || this.direction == "NW" || this.direction == "W") ? popMenu.offsetWidth : 0;
	var directionAccommodating_top = (this.direction == "NW" || this.direction == "NE" || this.direction == "N") ? popMenu.offsetTop : 0;
	if (this.positionReference == "mouse"){
		//mouse NOT implemented
		target_left = (sender_right - directionAccommodating_left + this.hOffset);
		target_top = (sender_bottom - directionAccommodating_top + this.vOffset);
	}
	if (this.positionReference == "elmTopLeft"){
		target_left = (sender_left - directionAccommodating_left + this.hOffset);
		target_top = (sender_top - directionAccommodating_top + this.vOffset);
	}
	else if (this.positionReference == "elmTopRight"){//default direction is SE
		target_left = (sender_right - directionAccommodating_left + this.hOffset);
		target_top = (sender_top - directionAccommodating_top + this.vOffset);
	}
	else if (this.positionReference == "elmBottomLeft"){//default direction is SW
		target_left = (sender_left - directionAccommodating_left + this.hOffset);
		target_top = (sender_bottom - directionAccommodating_top + this.vOffset);
	}
	else if (this.positionReference == "elmBottomRight"){//default direction is SE
		target_left = (sender_right - directionAccommodating_left + this.hOffset);
		target_top = (sender_bottom - directionAccommodating_top + this.vOffset);
	}
	else if (this.positionReference == "elmTop"){//default direction is N
		target_left = (sender_left - ((popMenu_width - sender_width) / 2) + this.hOffset);//there is no directionAccommodation_left, because there can be no horizontal direction, but it is possible to push the popup out of centering by setting hOffset different from zero (zero is default in case positionReference == "elmTop")
		target_top = (sender_top - directionAccommodating_top + this.vOffset);
	}
	else if (this.positionReference == "elmRight"){//default direction is E
		target_left = (sender_right - directionAccommodating_left + this.hOffset);
		target_top = (sender_top - ((popMenu_height - sender_height) / 2) + this.vOffset);
	}
	else if (this.positionReference == "elmBottom"){//default direction is S
		target_left = (sender_left - ((popMenu_width - sender_width) / 2) + this.hOffset);//there is no directionAccommodation_left, because there can be no horizontal direction, but it is possible to push the popup out of centering by setting hOffset different from zero (zero is default in case positionReference == "elmTop")
		target_top = (sender_bottom - directionAccommodating_top + this.vOffset);
	}
	else if (this.positionReference == "elmLeft"){
		target_left = (sender_left - directionAccommodating_left + this.hOffset);
		target_top = (sender_top - ((popMenu_height - sender_height) / 2) + this.vOffset);
	}
	
	if (this.keepInViewport){
		var viewportAccommodatedCoordinates = wm_pop.keepInViewportCoordinates(target_top, target_left + popMenu_width, target_top + popMenu_height, target_left);//remember that popMenu does NOT have any offsetWidth or offsetHeight since the display property is "none"
		target_left = viewportAccommodatedCoordinates.targetLeft;
		target_top = viewportAccommodatedCoordinates.targetTop;
	}
	
	popMenu.style.left = target_left + "px";
	popMenu.style.top = target_top + "px";

	popMenu.style.display = "block";
}

wm_pop.hide = function(pSenderID, pPopMenuID){
	if (wm_pop.hideDelay == -1){//do not hide (eg. if the popMenu have a close button, then the desired logic may be that the popMenu does not auto hide)
		wm_pop.removeEvent(document, "mousemove", wm_pop.setMousePosition);
		var popMenu = document.getElementById(pPopMenuID);
		popMenu.onmouseout = null;
		return;
	}
	
	setTimeout("wm_pop.hideImmediately('" + pSenderID + "', '" + pPopMenuID + "')", wm_pop.hideDelay);
}

wm_pop.hideImmediately = function(pSenderID, pPopMenuID){
	if (wm_pop.mouseIsOverPopMenu(pSenderID, pPopMenuID)){return;}

	var popMenu = document.getElementById(pPopMenuID);
	if (popMenu){popMenu.onmouseout = null; popMenu.style.display = "none";}

	wm_pop.removeEvent(document, "mousemove", wm_pop.setMousePosition);
//			document.onmousemove = null;
}

wm_pop.hideForce = function(pPopMenuID){
	var popMenu = document.getElementById(pPopMenuID);
	if (popMenu){popMenu.onmouseout =  null; popMenu.style.display = "none";}
}

wm_pop.associate = function(pTargetID, pPopMenuID, pConfig){
	wm_pop.addEvent((document.getElementById(pTargetID)), "mouseover", function(){wm_pop.show(pTargetID, pPopMenuID, pConfig);});
	wm_pop.addEvent((document.getElementById(pTargetID)), "mouseout", function(){wm_pop.hide(pTargetID, pPopMenuID);});
}
