// Program: Tool Tip
// Author:  Joseph Hermens
// Date:    October 2005
// Email:   josephhermens@gmail.com
var agent = navigator.userAgent.toLowerCase();
ToolTip.IS_MSIE   = (document.all && !window.opera) ? true : false;
ToolTip.IS_OPERA  = (window.opera)                  ? true : false;
ToolTip.IS_NS4    = (window.layers)                 ? true : false;
ToolTip.IS_SAFARI = (agent.indexOf('safari') > - 1);
ToolTip.IS_DOM1   = (document.getElementById)       ? true : false;
ToolTip.ALLOW_DELAY = (ToolTip.IS_MSIE == false || parseFloat(agent.substring(agent.indexOf('msie ') + 5)) >= 6);

ToolTip.current = null; // the current tool tip that is displayed, used to remove old tool tips in case of a failure of the onmouseout event
// default paramaters for the tool tip
ToolTip.OFFSET_X     = 10;
ToolTip.OFFSET_Y     = 20;
ToolTip.CLASS_NAME   = '';
ToolTip.WIDTH        = 0;
ToolTip.HEIGHT       = 0;
ToolTip.BG_COLOR     = '#EEE';
ToolTip.FG_COLOR     = '#000';
ToolTip.BORDER_STYLE = 'solid';
ToolTip.BORDER_WIDTH = '1';
ToolTip.BORDER_COLOR = '#000';
ToolTip.Z_INDEX      = 1;
ToolTip.PADDING      = 2;
ToolTip.TIME_DELAY   = 100;
ToolTip.FADE_IN      = 0;

// default tag types that will be checked for title attributes to convert to tool tips when you call the ToolTip.initByTitle() from the onload attribute of the body tag
ToolTip.tags = new Array('a', 'input', 'img');

// called by ToolTip.initById(), ToolTip.initByTitle(), or could be called anywhere in your javascript code to add a tool tip to an element 
ToolTip.add = function(element, htmlORtip, params)
{
	// keeps old onmouseover and onmouseout handlers so that other actions defined there will still take place
	element.oldMouseOver = element.onmouseover;
	element.oldMouseOut  = element.onmouseout;
	var toolTip = new ToolTip(element, htmlORtip, params);
	
	element.onmouseover = function(e)
	{
		toolTip.show();
		if(this.oldMouseOver) this.oldMouseOver(e);
	};
	element.onmouseout = function(e)
	{
		toolTip.hide();
		if(this.oldMouseOut) this.oldMouseOut(e);
	};
}

// This function gets elements by id an adds a tool tip to them, needs to be called in the onload attribute of the body tag
ToolTip.initByID = function()
{
	var element = document.getElementById("some_id"); 
	if(element) 
		ToolTip.add(element, "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"#F00;width:100%;height:100%\">  <tr>    <td><img src=img1.jpg width=200 height=\"200\" /></td>    <td valign=\"top\">      <h4 style=\"margin-bottom:0px\">HTML ToolTip</h4>      with style<br />and picture of bunny    </td>  </tr></table>", "width=300; height=200; bgColor=#0FF; fgColor=#F00; borderColor=#DDD; ");

	// add tool tip created by the DOM
	var element2 = document.getElementById("domAdd_id"); 
	if(element2) 
	{
		element2.onmouseover = function() { this.style.color = '#F00'; }; // other optional onmouseover events
		element2.onmouseout  = function() { this.style.color = ''; };     // other optional onmouseout events
		
		var table = document.createElement('table'); table.style.position = 'absolute'; table.border = 1; table.style.backgroundColor = '#EEE';
		var tBody = document.createElement('tBody'); table.appendChild(tBody); table.style.width = '200px';
		var tr    = document.createElement('tr');    tBody.appendChild(tr);
		var td    = document.createElement('td');    td.colSpan = 2; td.appendChild(document.createTextNode('This is a table created by the DOM')); tr.appendChild(td);
		var tr    = document.createElement('tr');    tBody.appendChild(tr);
		var td    = document.createElement('td');    td.appendChild(document.createTextNode('row 2, cell 1')); tr.appendChild(td);
		var td    = document.createElement('td');    td.appendChild(document.createTextNode('row 2, cell 2')); tr.appendChild(td);
		var tr    = document.createElement('tr');    tBody.appendChild(tr);
		var td    = document.createElement('td');    td.colSpan = 2; td.appendChild(document.createTextNode('It has three rows, this one has a colspan of 2')); tr.appendChild(td);
		ToolTip.add(element2, table, 'width=200 height=100');
	}
}

// This function gets elements by tag name and converts title attribute to a tool tip, needs to be called in the onload attribute of the body tag
ToolTip.initByTitle = function()
{
	var elements, startHTML, startParams, startHTML2, startParams2, html, params;
	// goes through each of the tag types
	for(var n = 0; n < ToolTip.tags.length; n++)
	{
		elements = document.getElementsByTagName(ToolTip.tags[n]);
		// goes through each of the elements of the given tag type
		for(var x = 0; x < elements.length; x++)
		{
			if(elements[x].title != null && elements[x].title != '' && elements[x].title != 'undefined')
			{
				// searches the title attribute for html and params of tool tip
				var title = elements[x].title; html = ''; params = null;
				startHTML   = startHTML2   = title.indexOf('html');
				if(startHTML > -1)
					while(startHTML2 < title.length && title.charAt(startHTML2) != '=' && title.charAt(startHTML2) != ':')
						startHTML2++;
						
				startParams = startParams2 = title.indexOf('params');
				if(startParams > -1)
					while(startParams2 < title.length && title.charAt(startParams2) != '=' && title.charAt(startParams2) != ':')
						startParams2++;
						
				if(startHTML > -1 && startParams > -1)
				{
					html   = (startHTML < startParams) ? title.substring(startHTML2 + 1, startParams) : title.substr(startHTML2 + 1);
					params = (startParams < startHTML) ? title.substring(startParams2 + 1, startHTML) : title.substr(startParams2 + 1);
				}
				else if(startHTML > -1)
					html = title.substr(startHTML2 + 1);
				else if(startParams > -1)
				{
					params = title.substr(startParams2 + 1);
					html   = (startParams > 0) ? title.substr(0, startParams) : '';
				}
				else html = title;

				ToolTip.add(elements[x], html, params);
				elements[x].removeAttribute('title'); // get rid of title attribute so browser won't display it
			}
		}
	}
}

// use this in onmouseover handler if the content of the tool tip could change each time it is displayed, else use ToolTip.create()
ToolTip.refresh = function(element, html, params)
{
	var toolTip = new ToolTip(element, html, params);
	toolTip.show();
	if(element.onmouseout)
	{
		var oldOut = element.onmouseout.toString();
		var oldFunction = new  Function('e', oldOut.substring(oldOut.indexOf('{') + 1, oldOut.indexOf('}') - 1));
		element.onmouseout = function(e) { toolTip.hide(); oldFunction(e); element.onmouseout = oldFunction; };
	}
	else element.onmouseout = function(e) { toolTip.hide(); element.onmouseout = null; };
}

// created tool tip and changes the onmouseover and onmouseout handlers to show/hide the tool tip
ToolTip.create = function(element, html, params)
{
	var toolTip = new ToolTip(element, html, params);
	toolTip.show();
	
	// cuts the old onmouseover code up, removes the call to ToolTip.create, and creates functions that call any code defined before or after ToolTip.create in the onmouseover handler 
	var oldOver = element.onmouseover.toString();
	var start = oldOver.indexOf('ToolTip.create');
	var brackets = 0;
	for(var n = start; n < oldOver.length; n++)
	{
		if(oldOver.charAt(n) == '(')
			brackets++;
		else if(oldOver.charAt(n) == ')' && --brackets == 0)
		{
			element.oldMouseOver1 = new Function('e', oldOver.substring(oldOver.indexOf('{') + 1, start));
			element.oldMouseOver2 = new Function('e', oldOver.substring(n + 1, oldOver.lastIndexOf('}') - 1));
			element.onmouseover = function(e) // new onmouseover handler
			{
				element.oldMouseOver1(e);
				toolTip.show();
				element.oldMouseOver2(e);
			}
			break;
		}
	}
	// new onmouseout hides tool tip and also calls old handler
	if(element.onmouseout)
	{
		element.oldMouseOut = element.onmouseout;// = new  Function('e', oldOut.substring(oldOut.indexOf('{') + 1, oldOut.indexOf('}') - 1));
		element.onmouseout = function(e) { toolTip.hide(); if(this.oldMouseOut) this.oldMouseOut(e); };
	}
	else element.onmouseout = function(e) { toolTip.hide(); };
}

function ToolTip(element, htmlORtip, params)
{
	var ref = this;
	
	var paramObj = parseParams(params); // get params from the params string
	var offsetX      = (paramObj.offsetX     != null) ? parseInt(paramObj.offsetX)     : ToolTip.OFFSET_X;
	var offsetY      = (paramObj.offsetY     != null) ? parseInt(paramObj.offsetY)     : ToolTip.OFFSET_Y;
	var timeDelay    = (paramObj.timeDelay   != null) ? parseInt(paramObj.timeDelay)   : ToolTip.TIME_DELAY;
	var fadeIn       = (paramObj.fadeIn      != null) ? parseInt(paramObj.fadeIn)      : ToolTip.FADE_IN;
	var className    = (paramObj.className   != null) ? paramObj.className             : ToolTip.CLASS_NAME;
	var width        = (paramObj.width       != null) ? parseInt(paramObj.width)       : ToolTip.WIDTH;
	var height       = (paramObj.height      != null) ? parseInt(paramObj.height)      : ToolTip.HEIGHT;
	var bgColor      = (paramObj.bgColor     != null) ? paramObj.bgColor               : ToolTip.BG_COLOR;
	var fgColor      = (paramObj.fgColor     != null) ? paramObj.fgColor               : ToolTip.FG_COLOR;
	var borderStyle  = (paramObj.borderStyle != null) ? paramObj.borderStyle           : ToolTip.BORDER_STYLE;
	var borderWidth  = (paramObj.borderWidth != null) ? parseInt(paramObj.borderWidth) : ToolTip.BORDER_WIDTH;
	var borderColor  = (paramObj.borderColor != null) ? paramObj.borderColor           : ToolTip.BORDER_COLOR;
	var zIndex       = (paramObj.zIndex      != null) ? paramObj.zIndex                : ToolTip.Z_INDEX;
	var padding      = (paramObj.padding     != null) ? parseInt(paramObj.padding)     : ToolTip.PADDING;
	// new params can be added by simply adding a line here to get that paramater from the paramObj or default value
	// if the param relates to the display of the tool tip, it would also need to be added to the style a few lines below

	if(typeof(htmlORtip) == 'string')
	{
		var toolTip = document.createElement('span');
		if(width  > 0) toolTip.style.width  = width+'px';
		if(height > 0) toolTip.style.height = height+'px';
		if(className != '') toolTip.className = className;
		toolTip.style.borderStyle     = borderStyle;
		toolTip.style.borderWidth     = borderWidth+'px';
		toolTip.style.borderColor     = borderColor;
		toolTip.style.backgroundColor = bgColor;
		toolTip.style.color           = fgColor;
		toolTip.style.zIndex          = zIndex;
		toolTip.style.padding         = padding+'px '+padding+'px '+padding+'px '+padding+'px';
		toolTip.style.opacity         = opacity;
	
		toolTip.innerHTML = htmlORtip.replace(/\\"/g, '"');
	}
	else if(typeof(htmlORtip) == 'object') // if a DOM object was passed in rather than a string of text or html
		toolTip = htmlORtip;
	toolTip.style.position = 'absolute';
	
	// used to get scrollLeft and scrollTop for IE in trackMSIE()
	var doc = (document.compatMode && document.compatMode != 'BackCompat') ? document.documentElement : document.body;

	var timerID, opacity = 0, smoothness = 20; // used for fadeIn and timeDelay
	var hideTimerID, safariHideDelay = 50; // used with safari to delay hiding
	this.show = function()
	{
		if(ToolTip.IS_SAFARI && hideTimerID)
		{
			window.clearTimeout(hideTimerID);
			hideTimerID = null;
		}

		if(timerID == null)
		{
			if(ToolTip.current != null && ToolTip.current != ref) // in case a previous tool tip didn't get hidden
				ToolTip.current.hide();
				
			document.onmousemove = (ToolTip.IS_MSIE) ? trackIE : trackGECKO;
			if(timeDelay > 0 && ToolTip.ALLOW_DELAY)
				timerID = window.setTimeout(show, timeDelay);
			else show();
		}
		
		function show() // displays tool tip
		{
			timerID = null;
			ToolTip.current = ref;
			document.body.appendChild(toolTip);
			if(fadeIn > 0 && ToolTip.ALLOW_DELAY)
				fade();
		}
		function fade() // fades the tool tip in by changing opacity, for a smoother fade in increase the value of smoothness
		{
			// max opacity of 99 keeps firefox from blinking at the end
			opacity = (opacity < 100) ? (opacity + 100 / smoothness) : 99;
			if(opacity > 99) opacity = 99;
			toolTip.style['opacity']      = opacity / 100;
			toolTip.style['-moz-opacity'] = opacity / 100;
			toolTip.style['filter']       = 'alpha(opacity='+opacity+')';
			
			if(opacity < 99)
				window.setTimeout(fade, fadeIn / smoothness);
		}
	}
	this.hide = function()
	{
		if(ToolTip.IS_SAFARI && hideTimerID == null) // keeps Safari from hiding it while moving mouse over link
			hideTimerID = window.setTimeout(hide, safariHideDelay);
		else hide();
		function hide()
		{ 
			if(timerID)
			{
				window.clearTimeout(timerID);
				timerID = null;
			}
			document.onmousemove = null;
			if(toolTip.parentNode == document.body)
				document.body.removeChild(toolTip);
			ToolTip.current = null;
		}
	}
	
	var scrollX, scrollY, innerW, innerH, maxX, maxY, x, y;
	function trackIE()
	{
		scrollX = doc.scrollLeft;
		scrollY = doc.scrollTop;
		innerW  = doc.clientWidth;
		innerH  = doc.clientHeight;
		maxX = scrollX + innerW;
		maxY = scrollY + innerH;
		x = window.event.clientX + scrollX + offsetX;
		y = window.event.clientY + scrollY + offsetY;
		
		move();
	}
	function trackGECKO(e)
	{
		scrollX = window.pageXOffset;
		scrollY = window.pageYOffset;
		innerW  = window.innerWidth;
		innerH  = window.innerHeight;
		maxX = scrollX + innerW;
		maxY = scrollY + innerH;
		x = e.pageX + offsetX;
		y = e.pageY + offsetY;
		move();
	}
	
	function move()
	{
		toolTip.style.left = (x + width  + 2*padding < maxX ? x : x - width  - 2*offsetX - 2*padding)+'px';
		toolTip.style.top  = (y + height + 2*padding < maxY ? y : y - height - 2*offsetY - 2*padding)+'px';
	}
	
	// gets params from the string, name value pairs with name = value or name : value, pairs need to be seperated by a space,
	// semi-colen, or comma.  Quotes are unnecessary and will be stripped.  Neither names or values can contain a space 
	function parseParams(str)
	{
		var paramObj = new Object();
		if(str == null) return paramObj;
			
		var index = 0;
		var token;
		for(var n = 0; n < str.length; n++)
		{
			switch(str.charAt(n))
			{
				case ';':
				case ',':
				case ':': 
				case '=': 
				case ' ':
				case '"': 
				case "'": 
					if(index < n)
					{
						if(token)
						{
							paramObj[token] = str.substr(index, n - index);
							token = null;
						}
						else token = str.substr(index, n - index);
					}
					index = n + 1;
					break;
				default: break;
			}
		}
		if(index < str.length && token != null)
			paramObj[token] = str.substr(index, n - index);
		return paramObj;
	}
}