/**
 * Contains the Javascript class Sitebox.Framework.Tooltip.
 *
 * @copyright		2000-2006 CARE Internet Services B.V. All Rights Reserved
 * @internal		$Workfile: Tooltip.js$
 * @internal		$Date: 03-08-07 08:19:15 AM$
 * @author			$Author: robert$
 * @version			$Revision: 1$
 * $NoKeyword$
 */


/**
 * Creates a new tooltip.
 *
 * TODO: Off-page positioning. (_show() / _getTooltipPosition()).
 *       Current implementation is a quick fix.
 *
 * @param string targetId
 * @param string title HTML
 * @param string body HTML
 * @param object parameters (optional)
 *
 * Available parameters:
 * - width =>		Sets the width of the tooltip.
 * - loadEvent =>	Sets the event when the tooltip should load.
 * 					Available: onmouseover, onclick, ondblclick. Default: onmouseover.
 * - opacity =>		Sets the tooltip opacity. Ranges from 0 (invisible) to 1 (visible)
 * - showDelay =>	Delay in milliseconds for showing the tooltip.
 * - hideDelay =>	Delay in milliseconds when hiding the tooltip.
 */
Sitebox.Framework.Tooltip = function(targetId, title, body, parameters)
{
	/**
	 * The tooltip title HTML.
	 */
	this._title = title;
	/**
	 * The tooltip body HTML.
	 */
	this._body = body;
	/**
	 * Parameters for tooltip display settings.
	 */
	this._parameters = parameters;
	/**
	 * The tooltip.
	 */
	this._tooltip;
	/**
	 * The target element for the tooltip.
	 */
	this._target = $(targetId);
	/**
	 * References to the actions. (context menu = right click)
	 */
	this._loadEvent;
	this._mouseOutEvent;
	this._mouseDownEvent;
	this._contextMenuEvent;
	this._mouseMoveEvent;
	/**
	 * The start position of the tooltip.
	 */
	this._startPosition;
	/**
	 * Timeouts for showing and hiding the tooltip.
	 */
	this._showTimeout;
	this._hideTimeout;
	/**
	 * Boolean whether the tooltip is currently visible.
	 */
	this._isVisible = false;

	/* Initialise the tooltip. */
	this._init();
}; // class Sitebox.Framework.Tooltip

/**
 * Creates the DOM elements for the tooltip and applies the parameters.
 */
Sitebox.Framework.Tooltip.prototype._init = function()
{
	var tooltipTitle = DIV({'class': 'sb-tooltip-title'});
	tooltipTitle.innerHTML = this._title;
	var tooltipBody = DIV({'class': 'sb-tooltip-body'});
	tooltipBody.innerHTML = this._body;
	this._tooltip = DIV({'class': 'sb-tooltip'}, tooltipTitle, tooltipBody);
	appendChildNodes(currentDocument().body, this._tooltip);

	var loadEvent = 'onmouseover';

	if (this._parameters)
	{
		if (this._parameters.width)
		{
			updateNodeAttributes(this._tooltip, {'style': {'maxWidth': 'none', 'width': this._parameters.width}});
		}
		if (this._parameters.loadEvent)
		{
			switch (this._parameters.loadEvent)
			{
				/* Three allowed events. */
				case 'onmouseover':
				case 'onclick':
				case 'ondblclick':
					loadEvent = this._parameters.loadEvent;
					break;
				default:
					Sitebox.logWarning("Event '" + this._parameters.loadEvent + "' is not a valid load event for a tooltip.")
					break;
			}
		}
		if (this._parameters.opacity)
		{
			setOpacity(this._tooltip, this._parameters.opacity);
		}
	}

	/* Add a mouse over event for the target element to display the tooltip. */
	this._loadEvent = connect(this._target, loadEvent, this, '_show');
}; // function Sitebox.Framework.Tooltip::_init

/**
 * Shows the tooltip, and adds event handlers.
 */
Sitebox.Framework.Tooltip.prototype._show = function(e)
{
	/* Clear a possible waiting show or hide action. */
	clearTimeout(this._showTimeout);
	clearTimeout(this._hideTimeout);

	/* If the tooltip is already visible, no need to continue. */
	if (this._isVisible)
	{
		return;
	}
	/* Check if there should be a delay. */
	var delay = 0;
	if (this._parameters && this._parameters.showDelay)
	{
		delay = this._parameters.showDelay;
	}
	var self = this;
	var mousePos = e.mouse().page;
	this._showTimeout = setTimeout(function()
	{
		showElement(self._tooltip);
		var startPos = self._getTooltipPosition(mousePos);
		if (self._startPosition)
		{
			startPos = self._startPosition;
			/* This line recalculates the start position to
			 * prevent off-page positioning.
			 */
			startPos = self._getTooltipPosition({'x': self._startPosition.x - 15, 'y': self._startPosition.y - 15});
		}
		setElementPosition(self._tooltip, startPos);
		/* Set flag visible on true. */
		self._isVisible = true;
	}, delay);

	/* These should be available at ALL times, and not only when
	 * this show timeout is executed, because the tooltip should
	 * not be shown at all if the target is clicked or the mouse
	 * in no longer hovering.
	 */
	this._mouseOutEvent = connect(this._target, 'onmouseout', this, '_hide');
	this._mouseDownEvent = connect(this._target, 'onmousedown', this, '_hideNow');
	this._contextMenuEvent = connect(this._target, 'oncontextmenu', this, '_hideNow');
	this._mouseMoveEvent = connect(currentDocument(), 'onmousemove', this, '_move');
}; // function Sitebox.Framework.Tooltip::_show

/**
 * Hides the tooltip immediately.
 */
Sitebox.Framework.Tooltip.prototype._hideNow = function(e)
{
	/* Clear a possible waiting show or hide action. */
	clearTimeout(this._showTimeout);
	clearTimeout(this._hideTimeout);

	/* Hide element and disconnect all related events. */
	hideElement(this._tooltip);
	disconnect(this._mouseMoveEvent);
	disconnect(this._mouseDownEvent);
	disconnect(this._contextMenuEvent);
	disconnect(this._mouseOutEvent);
	/* Set flag visible on false. */
	this._isVisible = false;
	/* Reset the start position. */
	this._startPosition = null;
}; // function Sitebox.Framework.Tooltip::_hideNow

/**
 * Hides the tooltip, but unlike _hideNow does apply the delay.
 */
Sitebox.Framework.Tooltip.prototype._hide = function(e)
{
	/* Clear a possible waiting show or hide action. */
	clearTimeout(this._showTimeout);
	clearTimeout(this._hideTimeout);

	/* If the tooltip is already hidden, no need to continue. */
	if (!this._isVisible)
	{
		return;
	}
	var delay = 0;
	if (this._parameters && this._parameters.hideDelay)
	{
		delay = this._parameters.hideDelay;
	}
	var self = this;
	this._hideTimeout = setTimeout(function()
	{
		self._hideNow();
	}, delay);
}; // function Sitebox.Framework.Tooltip::_hide

/**
 * Handles the mouse-move event.
 */
Sitebox.Framework.Tooltip.prototype._move = function(e)
{
	e.stop();

	/* Move the tooltip relative to the current mouse position. */
	var pos = this._getTooltipPosition(e.mouse().page);
	setElementPosition(this._tooltip, pos);

	/* If the tooltip is not yet visible, this mouve event should
	 * update the initial position of the tooltip.
	 */
	if (!this._isVisible)
	{
		this._startPosition = pos;
	}
}; // function Sitebox.Framework.Tooltip::_move

/**
 * Returns the correct tooltip position.
 *
 * @param Coordinates mousePos
 * @return Coordinates
 */
Sitebox.Framework.Tooltip.prototype._getTooltipPosition = function(mousePos)
{
	var offsetX = mousePos.x + 15;
	var offsetY = mousePos.y + 15;

	/* Start off-page positioning prevention. */
	var dim = elementDimensions(this._tooltip);
	if (offsetX + 200 > getViewportDimensions().w)
	{
		offsetX -= (dim.w + 15);
	}
	if (offsetY + dim.h > getViewportDimensions().h)
	{
		offsetY -= (dim.h + 15);
	}
	/* End off-page positioning prevention. */

	//return new MochiKit.DOM.Coordinates(offsetX, offsetY);
	return {x: offsetX, y: offsetY};
}; // function Sitebox.Backend.Dragger::_getTooltipPosition