// A "tool tip" is a (yellow) box containing arbitrary HTML which is shown near to
// some "target" element while the mousepointer is over it.  Many elements on a page
// may be given their own tooltips, with an attribute such as
//   onmouseover="showToolTip(event, '<b>hello world</b>', 200);"
// but only one can be visible at a time, so we lazily create and reuse a per-page <div>

// An "info box" is a balloon-like box containing arbitrary HTML which is shown near to
// some "target" element after a mouse click on it, until another click is handled by
// the <body> (whereupon it is hidden) or another element's info box is shown (whereupon
// it is hidden and then reshown)

// FIXES:
//	* ensure that the tooltip is always above the infobox (assign distinct large zIndex values)

// TO DO:
//	* ensure that the infobox also hides and reshows overlapping SELECTs in IE4-6

var hintToolTipDiv = null; // the tool tip <div> element (after successful on-demand initialization)
var hintToolTipStarted = 'no'; // until initialisation, then 'yes' or 'fail'
var hintHiddenToolTipSelects = new Array();

var hintInfoBoxDiv = null; // the info box <div> element (after successful on-demand initialization)
var hintInfoBoxStarted = 'no'; // until initialisation, then 'yes' or 'fail'
var hintHiddenInfoBoxSelects = new Array();

/***********************************************
* Show Hint script- © Dynamic Drive (www.dynamicdrive.com)
* This notice MUST stay intact for legal use
* Visit http://www.dynamicdrive.com/ for this script and 100s more.
***********************************************/

/* I've preserved the above notice, although there's little left of the (rather crummy) original now ;-) */

function consume(event) { // FF3 seems (wrongly) to propagate option mouseovers to parent select, so D101 calls this to consume them
	if (event.srcElement != null) { // not a DOM Level 2 browser
		event.cancelBubble = true; // prevent this event being propagated (as it would by default) to ancestor elements
	} else {
		event.stopPropagation(); // ditto
	}		
}

function showToolTip(event, hintHTML, hintWidth) { // shows hintHTML in a box of Width until mousepointer leaves target
	var ie4 = event.srcElement != null; // not a DOM Level 2 browser
	var div; // temp for creating hintToolTipDiv
	//
	// lazily create the ToolTip <div> for this page
	if (hintToolTipStarted == 'fail') {
		return; // without attempting to do anything
	} else if (hintToolTipStarted == 'no') {
		div = document.createElement('div');
		div.setAttribute('id', 'hintToolTipDiv');
		div.style.zIndex = 900;
		document.body.appendChild(div);
		hintToolTipStarted = (hintToolTipDiv = document.getElementById('hintToolTipDiv')) == null ? 'fail' : 'yes';
	}
	showHintX(hintHTML, (ie4 ? event.srcElement : event.target), hintWidth, hintToolTipDiv);
	//
	// arrange for this <div> to be hidden at next 'mouseout' event received by target
	if (ie4) {
		// hide overlapping selects (IE4..6 only)
		var c = document.getElementsByTagName('select');
		var i;
		for (i = 0; i < c.length; i++) {
			if ((c[i].style.visibility == '' || c[i].style.visibility == 'visible') && overlaps(c[i], hintToolTipDiv)) { // wot if it's 'inherit'?
				c[i].style.visibility = 'hidden';
				hintHiddenToolTipSelects.push(c[i]);
			}
		}
		event.srcElement.onmouseout = hideToolTip; // tell IE4+ to hide ToolTip at next mouseout
	} else {
		event.target.onmouseout = hideToolTip; // tell DOM 2 browser to hide ToolTip at next mouseout
	}
}

function showInfoBox(event, hintHTML, hintWidth) { // shows HTML in a box of Width until shown elsewhere or background is clicked
	var ie4 = event.srcElement != null; // not a DOM Level 2 browser
	var div; // temp for creating hintInfoBoxDiv
	//
	// lazily create the InfoBox <div> for this page
	if (hintInfoBoxStarted == 'fail') { // we've already tried & failed (how?) to create hintInfoBoxDiv
		return; // without attempting to do anything
	} else if (hintInfoBoxStarted == 'no') { // this is the first call
		div = document.createElement('div');
		div.setAttribute('id', 'hintInfoBoxDiv');
		div.style.zIndex = 800; // beneath hintToolTipDiv
		document.body.appendChild(div);
		if ((hintInfoBoxDiv = document.getElementById('hintInfoBoxDiv')) == null) { // somehow (how?) failed to create this element
			hintInfoBoxStarted = 'fail';
		} else {
			hintInfoBoxStarted = 'yes';
			// arrange to hide InfoBox when user clicks anywhere on page
			chainFunc(document.body, 'onclick', hideInfoBox); // call once only, iff hintInfoBoxDiv was created
		}
	}
	showHintX(hintHTML, (ie4 ? event.srcElement : event.target), hintWidth, hintInfoBoxDiv);
	//
	if (ie4) {
		// hide overlapping selects (IE4..6 only)
		var c = document.getElementsByTagName('select');
		var i;
		for (i = 0; i < c.length; i++) {
			if ((c[i].style.visibility == '' || c[i].style.visibility == 'visible') && overlaps(c[i], hintInfoBoxDiv)) { // wot if it's 'inherit'?
				c[i].style.visibility = 'hidden';
				hintHiddenInfoBoxSelects.push(c[i]);
			}
		}
		event.cancelBubble = true; // prevent this 'click' being propagated (as it would by default) to ancestor elements
	} else {
		event.stopPropagation(); // ditto
	}
}	

function showHint(hintHTML, obj, hintWidth) { // deprecated, legacy version
	var div;		// temp for creating hintToolTipDiv
	if (hintToolTipStarted == 'fail') {
		return; // without attempting to do anything
	} else if (hintToolTipStarted == 'no') {
		div = document.createElement('div');
		div.setAttribute('id', 'hintToolTipDiv');
		document.body.appendChild(div);
		hintToolTipStarted = (hintToolTipDiv = document.getElementById('hintToolTipDiv')) == null ? 'fail' : 'yes';
	}
	showHintX(hintHTML, obj, hintWidth, hintToolTipDiv);
	obj.onmouseout = hideToolTip;
}

function showHintX(hintHTML, obj, hintWidth, hintDiv) { // private method serving above three public methods
	var posn;		// temp for element origin

	var vX0;		// left edge of viewport
	var vX1;		// right   "
	var vY0;		// top     "
	var vY1;		// bottom  "
	var vW;			// width of viewport
	var vH;			// height  "
	var eX0;		// left edge of element for which hint is to be shown
	var eX1;		// right edge  "
	var eY0;		// top edge    "
	var eY1;		// bottom edge "
	var eW;			// width of element
	var eH;			// height "
	var eXmid;		// horizontal midpoint of element
	var eYmid;		// vertical "
	var lW;			// width of rectangular region of viewport to left of element
	var lH;			// height ditto
	var tW;			// width ditto to topwards of element
	var tH;			// height ditto
	var rW;			// width ditto to right of element
	var rH;			// height ditto
	var bW;			// width ditto to bottomwards of element
	var bH;			// height ditto
	var where;		// chosen location of hint div

	vX0 = getViewportLeft();
	vW = getViewportWidth();
	vX1 = vX0 + vW;
	vY0 = getViewportTop();
	vH = getViewportHeight();
	vY1 = vY0 + vH;
	posn = elementTopLeft(obj);
	eX0 = posn[0];
	eY0 = posn[1];
	eW = obj.offsetWidth;
	eH = obj.offsetHeight;
	eX1 = eX0 + eW;
	eY1 = eY0 + eH;
	eXmid = (eX0 + eX1)/2;
	eYmid = (eY0 + eY1)/2;

	// width, height of viewport regions around element (any of these may be <=0)
	lW = eX0 - vX0;
	lH = vY1 - vY0;
	tW = vX1 - vX0;
	tH = eY0 - vY0;
	rW = vX1 - eX1;
	rH = vY1 - vY0;
	bW = vX1 - vX0;
	bH = vY1 - eY1;

	// lay out the hint box:
	hintDiv.style.width = (hintWidth == null ? '300px' : hintWidth);
	hintDiv.innerHTML = hintHTML;

	// get its size and choose where best to put it:
	pW = hintDiv.offsetWidth;
	pH = hintDiv.offsetHeight;
	if (pW <= vW && pH <= tH) { // there is room within viewport above element
		where = "top";
	} else if (pH <= vH && pW <= lW) { // there is room within viewport to left of element
		where = "left";
	} else if (pH <= vH && pW <= rW) { // there is room within viewport to right of element
		where = "right";
	} else if (pW <= vW && pH <= bH) { // there is room within viewport below 
		where = "bottom";
	} else if (exposedFraction(pW, pH, rW, rH) > exposedFraction(pW, pH, bW, bH)) {
		where = "right";
	} else {
		where = "bottom";
	}

	// compute hint box position within its chosen location:
	if (where == "top" || where == "bottom") {
		if (eXmid - pW / 2 < vX0) { // if, horiz centred wrt element, right edge is outside viewport
			pX0 = vX0;
		} else if (eXmid + pW / 2 > vX1) { // else if, similarly aligned, left edge is outside viewport
			pX0 = vX1 - pW;
		} else {
			pX0 = eXmid - pW / 2; // else centre it
		}
	} else if (where == "left") {
		if (vX0 > eX0 - 5 - pW) {
			pX0 = vX0;
		} else {
			pX0 = eX0 - pW - 5;
		}
	} else if (where == "right") {
		if (vX1 < eX1 + pH) { // not enough room at right for popup width
			pX0 = eX1; // show immediately at right of element
		} else if (vX1 < eX1 + pH + 5) { // 0..5 pixels to spare
			pX0 = eX1 + (vX1 - eX1 - pW); // split the difference
		} else {
			pX0 = eX1 + 5; // show 5 pixels to right of element
		}
	} else { // shouldn't happen
		pX0 = vX1 + 1; // hide beyond viewport
	}

	if (where == "left" || where == "right") {
		if (eYmid - pH / 2 < vY0) {
			pY0 = vY0;
		} else if (eYmid + pH / 2 > vY1) {
			pY0 = vY1 - pH;
		} else {
			pY0 = eYmid - pH / 2;
		}
	} else if (where == "top") {
		if (vY0 > eY0 - 5 - pH) {
			pY0 = vY0;
		} else {
			pY0 = eY0 - pH - 5;
		}
	} else if (where == "bottom") {
		if (vY1 < eY1 + pH) { // not enough room at bottom for popup height
			pY0 = eY1; // show immediately below element
		} else if (vY1 < eY1 + 5 + pH) { // 0..5 pixels to spare
			pY0 = eY1 + (vY1 - eY1 - pH) / 2; // split the difference
		} else {
			pY0 = eY1 + 5; // show 5 pixels below element
		}
	} else { // shouldn't happen
		pY0 = vY1 + 1; // hide beyond viewport
	}

	// position and show the hint box:
	hintDiv.style.left = pX0 + 'px';
	hintDiv.style.top = pY0 + 'px';
	hintDiv.style.visibility = 'visible';
}

function hideToolTip() {
	hintToolTipDiv.style.visibility = 'hidden';
	hintToolTipDiv.style.left = '-500px'; // for good measure
	// show any temporarily hidden selects (only happens with non-DOM2 browsers i.e. IE4-6)
	var hs;
	while ((hs = hintHiddenToolTipSelects.pop()) != undefined) {
		hs.style.visibility = 'visible';
	}
}

function hideInfoBox() {
	hintInfoBoxDiv.style.visibility = 'hidden';
	hintInfoBoxDiv.style.left = '-500px'; // for good measure
	// show any temporarily hidden selects (only happens with non-DOM2 browsers i.e. IE4-6)
	var hs;
	while ((hs = hintHiddenInfoBoxSelects.pop()) != undefined) {
		hs.style.visibility = 'visible';
	}
}

function getViewportWidth() { // by Michael van Ouwerkerk; tweaked by PS
	if (document.documentElement && document.documentElement.clientWidth) {
		return document.documentElement.clientWidth;
	} else if (document.body && document.body.clientWidth) {
		return document.body.clientWidth;
	} else if (window.innerWidth) {
		return window.innerWidth - 18;
	} else {
		return 0;
	}
};

function getViewportHeight() { // by Michael van Ouwerkerk; tweaked by PS
	if (document.body && document.body.clientHeight) {
		return document.body.clientHeight;
	} else if (window.innerHeight) {
		return window.innerHeight - 18;
	} else if (document.documentElement && document.documentElement.clientHeight) {
		return document.documentElement.clientHeight;
	} else {
		return 0;
	}
};

function getViewportLeft() { // by Michael van Ouwerkerk; tweaked by PS
	if (document.documentElement && document.documentElement.scrollLeft) {
		return document.documentElement.scrollLeft;
	} else if (document.body && document.body.scrollLeft) {
		return document.body.scrollLeft;
	} else if (window.pageXOffset) {
		return window.pageXOffset;
	} else if (window.scrollX) {
		return window.scrollX;
	} else {
		return 0;
	}
};

function getViewportTop() { // by Michael van Ouwerkerk; tweaked by PS
	if (document.documentElement && document.documentElement.scrollTop) {
		return document.documentElement.scrollTop;
	} else if (document.body && document.body.scrollTop) {
		return document.body.scrollTop;
	} else if (window.pageYOffset) {
		return window.pageYOffset;
	} else if (window.scrollY) {
		return window.scrollY;
	} else {
		return 0;
	}
};

function elementTopLeft(element) { // by Mark Wilton-Jones: despite name, returns [left,top]
	var posX;
	var posY;
	if (element.offsetParent) {
		for (posX = 0, posY = 0; element.offsetParent; element = element.offsetParent) {
			posX += element.offsetLeft;
			posY += element.offsetTop;
		}
		return [ posX, posY ];
	} else {
		return [ element.x, element.y ];
	}
}

function exposedFraction(aW, aH, bW, bH) { // area fraction of a which intersects with b (same origin)
	if (aW <= 0 || aH <= 0 || bW <= 0 || bH <= 0) {
		return 0.0;
	} else {
		return (aW <= bW ? 1.0 : bW / aW) * (aH <= bH ? 1.0 : bH / aH);
	}
}

function overlaps(e1, e2) { // returns true iff element e1 overlaps element e2 to any extent
	var e1TopLeft = elementTopLeft(e1);
	var e1Left = e1TopLeft[0];
	var e1Top = e1TopLeft[1];
	var e1Bottom = e1Top + e1.offsetHeight;
	var e1Right = e1Left + e1.offsetWidth;
	var e2TopLeft = elementTopLeft(e2);
	var e2Left = e2TopLeft[0];
	var e2Top = e2TopLeft[1];
	var e2Bottom = e2Top + e2.offsetHeight;
	var e2Right = e2Left + e2.offsetWidth;
	//
	return !(e1Bottom < e2Top || e2Bottom < e1Top || e1Right < e2Left || e2Right < e1Left); // not completely clear of each other
}

