/*
 * Name:
 *	event_handler.js
 *
 * Description:
 *	Defines generic, cross-browser functions for dynamically registering and unregistering
 *	JavaScript events on HTML DOM objects.
 *
 * Pre-conditions:
 *	None
 *
 * Post-conditions:
 *	Defines the following functions:
 *		- registerEvent			Associate a JavaScript event with an object.
 *		- unregisterEvent		Dissociate a JavaScript event from an object.
 *		- getEventTarget		Obtain the target of an event, given its implicit event argument.
 *
 * Log:
 *	Randall Betta		08/25/2006
 *		- Creation
 *
 */





/*
 * Name:
 *	registerEvent
 *
 * Description:
 *	Attaches a JavaScript event to the given HTML DOM element. In event models that support
 *	both options, events are fired in the bubbling phase by this function, not the capturing
 *	phase. Note that, under some schemes, multiple events (even duplicates) may be assigned to
 *	an object, and all will fire, so be sure to unregister any unneeded events!
 *
 * Pre-conditions:
 *	obj				REQUIRED	The DOM object to which the given event should be attached.
 *	eventName		REQUIRED	The name of the event as a string. May optionally include the on- prefix.
 *	handler			REQUIRED	A callback for the handler function. (NOT a string, NO parentheses!)
 *
 * Post-conditions:
 *	Returns true on successful event attachment, false otherwise.
 *
 * Log:
 *	Randall Betta		08/25/2006
 *		- Creation
 *
 */
function registerEvent(obj, eventName, handler) {
	
	var eventWithOn;
	var eventWithoutOn;
	
	// Define two forms of the event name: one with a leading on- prefix, the other without it.
	eventWithOn = (eventName.match(/^on/)) ? eventName : 'on' + eventName;
	eventWithoutOn = eventName.replace(/^on/, '');
	
	// Determine if the browser supports the Microsoft model or the W3C model for event attachment.
	
	if (obj.addEventListener) { // If: the W3C addEventListener method is defined.
		
		//
		// Use the W3C model.
		//
		
		// The event names should be lowercase without the on- prefix in W3C-compliant implementations.
		eventName = eventWithoutOn.toLowerCase();
		// Fire events during the bubbling phase; this must match the detachment function!
		obj.addEventListener(eventName, handler, false);
		return true;
	}
	else if (obj.attachEvent) { // Else if: the Microsoft attachEvent method is defined.
		
		//
		// Use the Microsoft model.
		//
		
		// The event names should be lowercase with the on- prefix in Microsoft's implementation.
		eventName = eventWithOn.toLowerCase();	
		obj.attachEvent(eventName, handler);
		return true;
	}
	else { // Else: fall back on the traditional model.
		
		//
		// Use the traditional event registration model.
		//
		
		// The event names should be lowercase with the on- prefix in the traditional implementation.
		eventName = eventWithOn.toLowerCase();
		obj[eventName] = handler;
		return true;
	} // End else: use the traditional model.
	
} // End function: registerEvent








/*
 * Name:
 *	unregisterEvent
 *
 * Description:
 *	Detaches a JavaScript event from the given HTML DOM element.
 *
 * Pre-conditions:
 *	obj				REQUIRED	The DOM object from which to remove the given event.
 *	eventName		REQUIRED	The name of the event as a string. May optionally include the on- prefix.
 *	handler			REQUIRED	A callback for the handler function. (NOT a string, NO parentheses!)
 *
 * Post-conditions:
 *	Returns true on successful event detachment, false otherwise.
 *
 * Log:
 *	Randall Betta		08/25/2006
 *		- Creation
 *
 */
function unregisterEvent (obj, eventName, handler) {
	
	// Define two forms of the event name: one with a leading on- prefix, the other without it.
	eventWithOn = (eventName.match(/^on/)) ? eventName : 'on' + eventName;
	eventWithoutOn = eventName.replace(/^on/, '');
	
	// Determine if the browser supports the Microsoft model or the W3C model for event detachment.
	if (obj.removeEventListener) { // If: the W3C model is supported.
		
		//
		// Use the W3C event detachment model.
		//
		
		// The event names should be lowercase without the on- prefix in the W3C implementation.
		eventName = eventWithoutOn.toLowerCase();
		// The original attachment function is presumed here to have 
		// specified that events fire during the bubbling phase. This must
		// match the actual value used during event attachment!
		obj.removeEventListener(eventName, handler, false);
		return true;
		
	}
	else if (obj.detachEvent) { // Else if: the Microsoft model is supported.
		
		//
		// Use the Microsoft event detachment model.
		//	
		
		// The event names should be lowercase with the on- prefix in the Microsoft implementation.
		eventName = eventWithOn.toLowerCase();
		obj.detachEvent(eventName, handler);
		return true;
	}
	else { // Fall back on the traditional event model.
		
		//
		// Use the traditional model.
		//
		
		// The event names should be lowercase with the on- prefix in the traditional implementation.
		eventName = eventWithOn.toLowerCase();
		obj[eventName] = null;
		return true;
	} // End else: use the traditional model.
	
} // End function: unregisterEvent

/*
 * Name:
 *	getEventTarget
 *
 * Description:
 *	Every event handler should take a single argument, which will either be made null by IE or set
 *	to an event object by W3C-compliant browsers. Given this object, this function returns a reference
 *	to the target element of the event.
 *
 * Pre-conditions:
 *	eventObj	REQUIRED	The implicit event object passed to an event handler (may possibly be null).
 *
 * Post-conditions:
 *	Returns a reference to the target of the event whose implicit event object is passed in as an argument.
 *	Returns false on failure.
 *
 * Log:
 *	Randall Betta		08/25/2006
 *		- Creation
 *
 */
function getEventTarget(eventObj) {
	
	var targetObj;
	
	//
	// Obtain a reference to the proper event-describing object.
	//
	
	// Under Internet Explorer, no implicit event object is passed to event handler. Instead, the
	// window.event object holds data for the last event to be raised.
	eventObj = (eventObj) ? eventObj : window.event;
	if (!eventObj) { // If: the event object can't be found.
		// Indicate failure.
		return false;
	} // End if: the event object can't be found.
	
	//
	// The event-describing object is known by this point. Obtain a reference to the DOM object that
	// was the original target of the event.
	//
	
	// W3C-compliant browsers call the event's object "target." IE calls it "srcElement."
	targetObj = (eventObj.target) ? eventObj.target : eventObj.srcElement;
	if (!targetObj) { // If: the target object can't be found.
		// Indicate failure.
		return false;
	} // End if: the target object can't be found.
	
	// Under Safari, a bug can cause events that fire on text element to store their target element
	// as the text node in the DOM, rather than its containing HTML node. Fix this.
	targetObj = (targetObj.nodeType == 3) ? targetObj.parentNode : targetObj;
	
	return targetObj;
	
} // End function: getEventTarget