

//on startup, must scan DTs and DDs setting hover to nohover

//	so unscripting viewers will see the DL as a DL





// change the class of a dt when the mouse hovers on a related "trigger"

// the class is revised by changing beteween "nohover" and "hover" 

// 

// this is activated by a <DL> with attribute hotspots 

//	and value hotspots="hover" or hotspots="sticky"

// for hotspots="hover" the DT class word stays "hover" while the mouse is 

// in the DT or its DD. 

// for hotspots="sticky", the DT class word remains

// "hover" until the mouse enters the hotspot for another DL or the mouse

// leaves the DD altogether.

//

// to appoint a subordinate element as a trigger, a DT must have an attribute

// hotspotsTrigger whose value is the id of that element

//

// a client would use this facility by changing visibility via CSS 

// Example CSS:

// 	dl[hotspots] > dt.hover {background-color: yellow;}

//	dl[hotspots] > dt.hover + dd {visibility: visible;} 

//	dl[hotspots] > dt.nohover {background-color: gray;}

//	dl[hotspots] > dt.nohover + dd {visibility: hidden;} 

//

// this code began from http://www.alistapart.com/articles/cssmaps

// only xxxEvent and stripWhitespace remain from the original



var dbgmsg;



dbgmsg = new Function("msg", 

	(typeof debugWin == "undefined") ? "" : "debugMsg(msg)");



var hotSpots = {



	// object-specific variables

	hotDT: null,  // the currently hovering DT, if any



	/* constructor - sets events for triggers */

	init: function(){

		var i, j;

		var DLs = document.getElementsByTagName("DL");

		for (i = 0; i< DLs.length; i++) {

			var attr = DLs[i].getAttribute("hotspots");

			if (attr != null) hotSpots.activate(DLs[i], attr);

		}

	},

	// we have a DL with attribute 'hotspots'; add events

	activate: function(DL, spottype) {

      		hotSpots.stripWhitespace( DL );

		if (spottype == "sticky")  // for "sticky" activate DL itself

			hotSpots.addEvent( DL, 'mouseout', hotSpots.mouseoutDL );

		// scan the immediate child DTs

		var kid;

		for (kid = DL.firstChild; kid != null; kid = kid.nextSibling) {

			if (kid.tagName == "DD") {

				hotSpots.addEvent(kid, 'mouseout', 

					hotSpots.mouseoutDTDD);

				hotSpots.setHover(kid, "nohover");

				continue; // (not a DT)

			}

			if (kid.tagName != "DT") continue;



			// kid is a DT

			hotSpots.setHover(kid, "nohover");

			var trig = kid.getAttribute("hotspotsTrigger");

			if (trig != null)  // convert trig to object inside kid

				trig = kid.getElementById(trig)

			if (trig != null) {

				hotSpots.addEvent( trig, 'mouseover',

					hotSpots.mouseoverDT);

				trig.addAttribute("hotSpotsDT", kid);

			}

			else // make the kid itself be the trigger

				hotSpots.addEvent( kid, 'mouseover',

					hotSpots.mouseoverDT);



			if (spottype != "sticky") 

				hotSpots.addEvent(kid, 'mouseout', 

					hotSpots.mouseoutDTDD);

		}

	},



	//	hotSpots.addEvent( elt, 'focus', hotSpots.startHover );

	//	hotSpots.addEvent( elt, 'blur', hotSpots.stopHover );





	// SET DT HOVER CLASS



	// call stopHover(), add hover attr to DT, and set hotDT

	startHover: function(DT) {

		hotSpots.stopHover();

		hotSpots.setHover(DT, "hover")

		hotSpots.hotDT = DT;

		return true;

	},



	// if there is a hover DT, change it to nohover; clear hotDT

	stopHover: function() {

		if (hotSpots.hotDT == null)

			return;

		hotSpots.setHover(hotSpots.hotDT, "nohover");

		hotSpots.hotDT = null;

		return true;

	},



	// elt.className is revised to include newhover,

	//   replacing any existing "hover" or "nohover" 

	setHover: function(elt, newhover) {

dbgmsg(elt.tagName + " has class " + elt.className);

dbgmsg("    set  it to " + newhover);

		var classList = elt.className;

		if (classList == null) 

			{ elt.className = newhover; return; }

		var hloc = classList.indexOf("hover");

		if (hloc == -1) // no hover yet; add it

			{ elt.className = classList + " " + newhover; return; }

		var len = 5;

		if (hloc >= 2 && classList.substring(hloc-2,hloc) == "no") 

			{ len = 7; hloc = hloc - 2; }

		elt.className = classList.substring(0, hloc) + newhover 

			+ classList.substring(hloc+len);



		// if elt has a sibling DD, set its hover as well

		// (by recursing, may also set further sib DDs)

		var sib = elt.nextSibling;

		if (sib==null || sib.tagName != "DD") return;

		hotSpots.setHover(sib, newhover); 



		// if the elt is a DT and has a "hotfunc", eval the function

		if (elt.tagName == "DT") {

			var func = elt.getAttribute("hotfunc");

			if (func != null) {

				var hovering = newhover == "hover";

				eval(func+"(elt,hovering)");

			}

		}

	},





	/* EVENT HANDLERS */

	// this function is called for mouseover a trigger 

	// it starts hover unless we are already hovering for this DT

	mouseoverDT: function (evt) {

		var attr = this.getAttribute("hotSpotsDT");

		var DT = (attr == null) ? this : attr;

		if (DT != null && DT != hotSpots.hotDT)

			hotSpots.startHover(DT);

	},



	// for mouseout, we need to determine whether the mouse is

	// in fact actually staying within hotDT and its DL

	// this function searches upward from the element that will

	// receive the mouse after the event 

	// return the enclosing DL or DD, or else the DL itself

	// if the mouse will not be in "hotDL" (hotDT's parent), return null

	findDL: function (evt) {

		if (hotSpots.hotDT == null) return null;

		var elt = (window.event) ? evt.toElement : evt.relatedTarget;

		if (elt == null) return null; // ?? probably not in hotDL

		if (elt.nodeType != 1) 

			elt = elt.parentNode;

		var prior = null;

		var DL = hotSpots.hotDT.parentNode;

		while (elt != null && elt != DL)

			{ prior = elt; elt = elt.parentNode; }

		if (elt == null) return null;      // not inside hotDL

		if (prior != null) return prior;   // return DT or DD

		return elt;                        // return hotDL itself

	},

	

	// called by mouseout from hotDT's parent (in sticky mode)

	// if the new location of the mouse is not within this DL, stop hover

	mouseoutDL: function(evt) {

		var DL = hotSpots.findDL(evt);

		if (DL == null) hotSpots.stopHover();

	},

	

	// this method is called for mouseout from hotDT or its DD

	// it will stop hover if the mouse is going to be outside one of those

	mouseoutDTDD: function(evt) {

		var DX = hotSpots.findDL(evt);

		if (DX == null)	hotSpots.stopHover();

		if (DX == hotSpots.hotDT) return;

		if (DX.tagName == "DD" && hotSpots.hotDT.nextSibling == DX)

			return;

		hotSpots.stopHover();

	},





	// UNDERLYING EVENT SUPPORT



	// add event handlers to a global list

	addEvent: function(element, type, handler) {

		// assign each event handler a unique ID

		if (!handler.$$guid) handler.$$guid = hotSpots.addEvent.guid++;

		// create a hash table of event types for the element

		if (!element.events) element.events = {}

		// create a hash table of event handlers for each element/event pair

		var handlers = element.events[type];

		if (!handlers) {

			handlers = element.events[type] = {}

			// store the existing event handler (if there is one)

			if (element["on" + type]) {

				handlers[0] = element["on" + type];

			}

		}

		// store the event handler in the hash table

		handlers[handler.$$guid] = handler;

		// assign a global event handler to do all the work

		element["on" + type] = hotSpots.handleEvent;

	},



	// apply an event handler from the global list

	handleEvent: function(event) {

		var returnValue = true;

		// grab the event object (IE uses a global event object)

		event = event || hotSpots.fixEvent(window.event);

		// get a reference to the hash table of event handlers

// dbgmsg("event " + (hotSpots.evtCount++) + " is " + event.type 

//	+ " on a " + this.tagName);

		var handlers = this.events[event.type];

		// execute each event handler

		for (var i in handlers) {

			this.$$handleEvent = handlers[i];

			if (this.$$handleEvent(event) === false) {

				returnValue = false;

			};

		};

		return returnValue;

	},



	fixEvent: function(event) {

		// add W3C standard event methods

		event.preventDefault = hotSpots.fixEvent.preventDefault;

		event.stopPropagation = hotSpots.fixEvent.stopPropagation;

		return event;

	},





	stripWhitespace: function( el ){

		for(var i = 0; i < el.childNodes.length; i++){

			var node = el.childNodes[i];

			if( node.nodeType == 3 && !/\S/.test(node.nodeValue)) {

				node.parentNode.removeChild(node);

			}

		}

	}

}

hotSpots.fixEvent.preventDefault = function() {this.returnValue = false;}

hotSpots.fixEvent.stopPropagation = function() {this.cancelBubble = true;}

hotSpots.addEvent.guid = 1;

hotSpots.evtCount = 1;





/* LOAD SCRIPT */

// ala http://dean.edwards.name/my/busted3.html



	function initPage() {

		if (arguments.callee.done) return;

		arguments.callee.done = true;

		// kill the timer (for Safari)

		if (_timer) {

			clearInterval(_timer);

			_timer = null;

		}

		hotSpots.init();  // payload : init the page map

	}

	

	/* for Mozilla */

	if (document.addEventListener) {

		document.addEventListener("DOMContentLoaded", 

				initPage, false);

	}

	

	/* for Internet Explorer */

	/*@cc_on @*/

	/*@if (@_win32)

		document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");

		var script = document.getElementById("__ie_onload");

		script.onreadystatechange = function() {

			if (this.readyState == "complete") {

				initPage(); // call the onload handler

			}

		}

	/*@end @*/

	

	/* for Safari */

	if (/WebKit/i.test(navigator.userAgent)) { // sniff

		var _timer = setInterval(function() {

			if (/loaded|complete/.test(document.readyState)) {

				initPage(); // call the onload handler

			}

		}, 10);

	}

	

	/* for other browsers */

	window.onload = initPage;






