var WEGHover = (function() {

	var _contentURL;
	var _showHoverTimeoutId = {};
	var _hideHoverTimeoutId = {};
	var _currentTarget = {};

	function _positionSmallT(vValues, spacing, shift) {
		return {
			contentPosX: vValues.targetCenterX - vValues.contentWidth * 0.5 - (shift.left || 0) + (shift.right || 0),
			contentPosY: vValues.targetOffsetY - vValues.contentHeight - 20 - (spacing || 0) - (shift.top || 0),
			arrowPosX: vValues.contentWidth * 0.5 - vValues.arrowWidth * 0.5,
			arrowPosY: vValues.contentHeight - 8
		};
	}

	function _positionSmallB(vValues, spacing, shift) {
		return {
			contentPosX: vValues.targetCenterX - vValues.contentWidth * 0.5 - (shift.left || 0) + (shift.right || 0),
			contentPosY: vValues.targetOffsetY + vValues.targetHeight + 20 + (spacing || 0) + (shift.bottom || 0),
			arrowPosX: vValues.contentWidth * 0.5 - vValues.arrowWidth * 0.5,
			arrowPosY: -20
		};
	}

	function _sizeAndPosition(jHoverPopup, targetElement, options) {
		var jTargetElement = jQuery(targetElement);
		var vTargetOffset = jTargetElement.offset();

		var jHoverArrow = jHoverPopup.find("#hover_arrow");
		var vHoverArrow = jHoverArrow[0];

		var vWindowWidth = jQuery(window).width();
		var vWindowHeight = jQuery(window).height();
		var vScrollTop = jQuery(window).scrollTop();
		var vScrollLeft = jQuery(window).scrollLeft();

		var vWidth = jHoverPopup.outerWidth();
		var vHeight = jHoverPopup.outerHeight();

		var vTop;
		var vLeft;
		var vArrowTop;
		var vArrowLeft;

		var vValues = {
			targetOffsetX: vTargetOffset.left,
			targetOffsetY: vTargetOffset.top,
			targetWidth: jTargetElement.outerWidth(),
			targetHeight: jTargetElement.outerHeight(),
			targetCenterX: vTargetOffset.left + jTargetElement.outerWidth() * 0.5,
			targetCenterY: vTargetOffset.top + jTargetElement.outerHeight() * 0.5,
			contentWidth: jHoverPopup.outerWidth(),
			contentHeight: jHoverPopup.outerHeight(),
			arrowWidth: jHoverArrow.width(),
			arrowHeight: jHoverArrow.height()
		};

		if (options.orientation === "TB") {
			// for now, only "small" hover is implemented for top/bottom,
			// and popping up outside the scroll viewport is not accounted for

			var vCalculations;

			// first determine if the popup should appear above or below the target
			// TODO: flip to the other side if outside the viewport
			if (options.favoredSide && options.favoredSide === "T") {
				vCalculations = _positionSmallT(vValues, options.spacing, options.shift || {});
				vHoverArrow.className = "hover_arrow_bottom_action";
				// we need to do this to force the iepngfix to run for IE 6
				vHoverArrow.style.backgroundImage = "url('/webapp/wcs/stores/B2BDirectStorefrontAssetStore/images/hover/hover_arrow_bottom_action.png')";
			}
			else {
				vCalculations = _positionSmallB(vValues, options.spacing, options.shift || {});
				vHoverArrow.className = "hover_arrow_top_action";
				// we need to do this to force the iepngfix to run for IE 6
				vHoverArrow.style.backgroundImage = "url('/webapp/wcs/stores/B2BDirectStorefrontAssetStore/images/hover/hover_arrow_top_action.png')";
			}

			jHoverArrow.css("left", vCalculations.arrowPosX + "px");
			jHoverArrow.css("top", vCalculations.arrowPosY + "px");
			jHoverPopup.css("left", vCalculations.contentPosX + "px");
			jHoverPopup.css("top", vCalculations.contentPosY + "px");
		}
		else {
			// This code only supports positioning the hover on the right or left of the target element.
			// The logic will check if there is room to put the hover inside the viewport on the right,
			// then on the left; if both sides go outside the viewport, the logic will put the hover
			// on the right but force it within the rightmost edge of the viewport (this change is in
			// response to bug #14568).

			var vOffsetY = jTargetElement.height() / 2;
			var vOffsetX = vHoverArrow.offsetWidth * 0.5;

			vTop = vTargetOffset.top - (vHeight * 0.5) + vOffsetY;
			vLeft = vTargetOffset.left + targetElement.offsetWidth + vOffsetX + (options.small ? 0 : 10);

			// find the "top" coordinate (within its parent) of the arrow
			vArrowTop = vHeight * 0.5 - vHoverArrow.offsetHeight + 14 /* 14 is the height of the hover aura */;

			// shift the hover up or down based on the view area
			if (vTop + vHeight - vScrollTop > vWindowHeight) {
				var vOldTop = vTop;
				vTop = vWindowHeight - vHeight + vScrollTop;
				vArrowTop += vOldTop - vTop;
			}
			if (vTop < vScrollTop) {
				var vOldTop = vTop;
				vTop = vScrollTop;
				vArrowTop += vOldTop - vTop;
			}

			// ensure that the arrowhead is a minimum distance from the top and bottom of the window
			if (vArrowTop > vHeight - vHoverArrow.offsetHeight - 16) {
				vArrowTop = vHeight - vHoverArrow.offsetHeight - 16;
			}
			else if (vArrowTop < 16) {
				vArrowTop = 16;
			}

			// find the "left" coordinate (within its parent) of the arrow
			vArrowLeft = -vHoverArrow.offsetWidth + 14 /* 14 is the width of the hover aura */;

			if (vLeft + vWidth - vScrollLeft > vWindowWidth) {
				// outside the right bounds of viewport; try to put the hover on the left side
				vLeft = vTargetOffset.left - vWidth - vOffsetX - (options.small ? 0 : 10);
				vArrowLeft = vWidth - 14;
			}

			if (vLeft < 0) {
				// the hover would appear beyond the leftmost edge of the viewport; put it back on the right
				// and shift it left so that it remains fully within the viewport area
				vLeft = vScrollLeft + vWindowWidth - vWidth;
				vArrowLeft = -vHoverArrow.offsetWidth + 14 /* 14 is the width of the hover aura */;
			}

			if (vLeft < vTargetOffset.left) {
				vHoverArrow.className = "hover_arrow_right";
				//we need to do this to force the iepngfix to run for IE 6
				vHoverArrow.style.backgroundImage = "url('/webapp/wcs/stores/B2BDirectStorefrontAssetStore/images/hover/hover_arrow_right.png')";
			}
			else {
				vHoverArrow.className = "hover_arrow_left";
				//we need to do this to force the iepngfix to run for IE 6
				vHoverArrow.style.backgroundImage = "url('/webapp/wcs/stores/B2BDirectStorefrontAssetStore/images/hover/hover_arrow_left.png')";
			}

			vHoverArrow.style.top = vArrowTop + "px";
			vHoverArrow.style.left = vArrowLeft + "px";

			jHoverPopup.css("top", vTop + "px");
			jHoverPopup.css("left", vLeft + "px");
		}

		var jHover = jHoverPopup.find(".hover");
		if (jHover.supersleight)
			jHover.supersleight({shim: "/webapp/wcs/stores/B2BDirectStorefrontAssetStore/images/blank.gif"});
	}

	function _showHover(targetElement, options) {
		clearTimeout(_hideHoverTimeoutId[options.id || "default"]); // interrupt any pending hover hide in case this call is for an already shown hover

		if (_currentTarget[options.id || "default"] != null) {
			if (_currentTarget[options.id || "default"] === targetElement)
				return; // nothing to do
			else
				_hideHover(options.id);  // trying to open a new hover; hide current one
		}

		_currentTarget[options.id || "default"] = targetElement;

		var jHoverPopup = options.small ? _hoverSmallPopupTemplate.clone() : _hoverPopupTemplate.clone();
		jHoverPopup.css("display", "block");
		if (options.id)
			jHoverPopup.attr("id", options.id);
		if (options.zindex)
			jHoverPopup.css("z-index", options.zindex);
		if (options.orientation == "TB")
			jHoverPopup.find("#hover_arrow").removeClass(options.small ? "hover_arrow_left_action" : "hover_arrow_left")
				.addClass(options.small ? "hover_arrow_top_action" : "hover_arrow_top");
		if (!options.nomouseover) {
			jHoverPopup.mouseover(function(evt) { WEGHover.unHideHover(options.id); });
		}
		jHoverPopup.mouseout(function(evt) { WEGHover.hideHover(options.id, options.hideDelay != undefined ? options.hideDelay : 500); });

		jQuery(document.body).addClass("ie6-hide-selects").append(jHoverPopup);

		var foundContent = false;
		if (options.contentId) {
			var vContent = jQuery("#" + options.contentId);
			if (vContent.length > 0) {
				var vHtml = jQuery("<div></div>").append(vContent.eq(0).clone().css("display", "")).html();
				vContent.css("display", "none");
				jHoverPopup.find("#hover_content_div").html(vHtml);
				_sizeAndPosition(jHoverPopup, targetElement, options);
				foundContent = true;
			}
		}

		var contentURL = targetElement.getAttribute("contentURL") || options.contentURL;

		var jTargetElement = jQuery(targetElement);
		var productNA = jTargetElement.hasClass("notAvailable");

		if (contentURL || productNA) {
			jHoverPopup.find("#hover_content_div").html('<img src="/webapp/wcs/stores/B2BDirectStorefrontAssetStore/images/ajax-loader-regular.gif"/>');
			_sizeAndPosition(jHoverPopup, targetElement, options);

			if(productNA)
			{
				jHoverPopup.find("#hover_content_div").html("<div><h2 class='hoverHead'>Product Not Available</h2></div><br/><p class='sfCompareProductDesc' style='width:95%;text-align:center;'>This product is either no longer sold at Wegmans <i>or</i> not currently listed in our on-line product catalog.</p>");
				_sizeAndPosition(jHoverPopup, targetElement, options);
			}
			else
			{
				var urlParts = contentURL.split("?");
				jQuery.ajax({
					type: "POST",
					url: urlParts[0],
					data: urlParts[1],
					success: function(_data, _textStatus) {
						if (_currentTarget[options.id || "default"] === targetElement) { // make sure this element still owns the hover
							jHoverPopup.find("#hover_content_div").html(_data);
							_sizeAndPosition(jHoverPopup, targetElement, options);
							if (options.onContentLoaded)
								options.onContentLoaded.call();
						}
					},
					cache: false
				});
			}

			foundContent = true;
		}

		if (!foundContent)
			_sizeAndPosition(jHoverPopup, targetElement, options);
	}

	function _hideHover(hoverId) {
		jQuery("#" + (hoverId || "WEGHover")).remove();
		jQuery(document.body).removeClass("ie6-hide-selects");
		delete _currentTarget[hoverId || "default"];
	}

	var _hoverPopupTemplate = jQuery(
		"<div id=\"WEGHover\" class=\"hover\">" +
			"<div id=\"hover_arrow\" class=\"hover_arrow_left png\">&nbsp;</div>" +
			"<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">" +
				"<tr>" +
					"<td class=\"hover_top_left\">&nbsp;</td>" +
					"<td class=\"hover_top\">&nbsp;</td>" +
					"<td class=\"hover_top_right\">&nbsp;</td>" +
				"</tr>" +
				"<tr>" +
					"<td class=\"hover_left\"><div>&nbsp;</div></td>" +
					"<td class=\"hover_content\" align=\"left\" style=\"padding:10px;\">" +
						"<table class=\"hover_mid_table\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\">" +
							"<tr>" +
								"<td valign=\"top\">" +
									"<div id=\"hover_content_div\">" +
									"</div>" +
								"</td>" +
							"</tr>" +
						"</table>" +
					"</td>" +
					"<td class=\"hover_right\"><div>&nbsp;</div></td>" +
				"</tr>" +
				"<tr>" +
					"<td class=\"hover_bottom_left\">&nbsp;</td>" +
					"<td class=\"hover_bottom\">&nbsp;</td>" +
					"<td class=\"hover_bottom_right\">&nbsp;</td>" +
				"</tr>" +
			"</table>" +
		"</div>");

	var _hoverSmallPopupTemplate = jQuery(
		"<div id=\"WEGHover\" class=\"hover\">" +
			"<div id=\"hover_arrow\" class=\"hover_arrow_top_action png\">&nbsp;</div>" +
			"<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">" +
				"<tr>" +
					"<td class=\"hover_top_left_action\">&nbsp;</td>" +
					"<td class=\"hover_top_action\">&nbsp;</td>" +
					"<td class=\"hover_top_right_action\">&nbsp;</td>" +
				"</tr>" +
				"<tr>" +
					"<td class=\"hover_left_action\"><div>&nbsp;</div></td>" +
					"<td class=\"hover_content\" align=\"left\">" +
						"<table class=\"hover_mid_table_action\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\">" +
							"<tr>" +
								"<td valign=\"top\">" +
									"<div id=\"hover_content_div\">" +
									"</div>" +
								"</td>" +
							"</tr>" +
						"</table>" +
					"</td>" +
					"<td class=\"hover_right_action\"><div>&nbsp;</div></td>" +
				"</tr>" +
				"<tr>" +
					"<td class=\"hover_bottom_left_action\">&nbsp;</td>" +
					"<td class=\"hover_bottom_action\">&nbsp;</td>" +
					"<td class=\"hover_bottom_right_action\">&nbsp;</td>" +
				"</tr>" +
			"</table>" +
		"</div>");

	return {
		/**
		 *	Main hover popup method.  Invoke from the onmouseover() event of the hover target
		 *  and pass the target element in as targetElement.  Pass in options as a property bag.
		 *
		 *	Note: this method will first look for a contentURL attribute of the target element,
		 *  then it will look for a contentURL passed in the options property bag.
		 *
		 *  Options:
		 *		id - a unique ID to assign to the hover popup (optional; all hovers that share the same ID will be
		 *			 treated as mutually exclusive)
		 *		contentId - the ID of the hover popup's internal content, if already on the page (optional)
		 *		contentURL - the URL from which to fetch content, if not supplied as an attribute
		 *					 of the target element (optional)
		 *		orientation - the orientation of the hover arrow; possible values are:
		 *					  "LR" (left/right side) (unrecognized values default to "LR")
		 *					  "TB" (top/bottom side)
		 *		small - a value of "true" results in a hover with smaller borders, arrow, and shadow (useful for action tooltips)
		 *		delay - time (in ms) to delay the hover popup (default is 500ms)
		 *		hideDelay - time (in ms) to delay the hover hide when the mouse leaves the hover itself (default is 500ms)
		 *					(for setting the delay when the mouse leaves the hover target, pass a value into hideHover())
		 *		nomouseover - a value of "true" means the hover popup will hide even if it is moused over
		 *		spacing - put the specified number of pixels between the arrow tip and the target (in the direction
		 *				  of the hover); effect is stacked with "shift", described next
		 *		shift - a bag of { left, up, right, down } (all optional) that will shift the hover arrow tip
		 *				the specified number of pixels in the specified direction(s) (note: shifts specified
		 *				in the direction of a hover will be honored, but shifts opposite the direction
		 *				of the hover will be ignored; for example, if a hover popups to the left, shifts to the right
		 *				will be ignored)
		 *		favoredSide - "L" for left, "R" for right (for "LR" orientation), or "T" for top, "B" for bottom (for "TB" orientation);
		 *					  will place the hover popup on the favored side of the target element
		 *		zindex - allows the hover popup to be placed at a specified z-index
		 *		onContentLoaded - a method to invoke when the popup content finishes loading (optional)
		 */
		showHover: function(targetElement, options) {
			if (options.id && (options.id === "default" || options.id === "WEGHover"))
				delete options.id;
			clearTimeout(_hideHoverTimeoutId[options.id || "default"]); // stop any pending hover hide
			var _options = options || {};
			if (_options.small)
				_options.orientation = "TB"; // small hover only supports top/bottom orientation for now
			if (!_options.orientation || _options.orientation !== "TB")
				_options.orientation = "LR";
			if (_options.delay != undefined && _options.delay == 0)
				_showHover(targetElement, _options);
			else
				_showHoverTimeoutId[options.id || "default"] = setTimeout(function() { _showHover(targetElement, _options); }, _options.delay != undefined ? _options.delay : 500);
		},

		/**
		 *	Used to force a hover popup to hide itself.  Usually called from a target element's
		 *  onmouseout() event.
		 */
		hideHover: function(hoverId, delay) {
			if (hoverId && (hoverId === "default" || hoverId === "WEGHover"))
				hoverId = undefined;
			clearTimeout(_showHoverTimeoutId[hoverId || "default"]); // stop any pending hover show
			if (delay != undefined && delay == 0)
				_hideHover(hoverId);
			else
				_hideHoverTimeoutId[hoverId || "default"] = setTimeout(function() { _hideHover(hoverId); }, delay != undefined ? delay : 500);
		},

		/**
		 *	Used to stop a hover popup from hiding.  Only effective within the hide timeout period.
		 */
		unHideHover: function(hoverId) {
			if (hoverId && (hoverId === "default" || hoverId === "WEGHover"))
				hoverId = undefined;
			clearTimeout(_hideHoverTimeoutId[hoverId || "default"]);
		}
	};
}());
