<!--
/*
  -------------------------------------------------------------------------------
 |  The content of this file constitutes Licensed Code.                          |
 |  Copyright (C) 2005-2007 Azalea Technology, LLC. All rights reserved.         |
 |-------------------------------------------------------------------------------|
 |  Unauthorized removal of this notice is considered a violation of the         |
 |  license agreement under which this Code may be used. This work is protected  |
 |  under United States copyright law and the similar law(s) of other countries  |
 |  under which such as work is afforded legal protection, and upon conviction   |
 |  of such a violation in a court of applicable jurisdiction, such person(s)    |
 |  may be subject to the maximum allowable penalty as permitted under such law. |
 |-------------------------------------------------------------------------------|
 |  You acknowledge and agree that information presented to you through this     |
 |  site (the "Web Site") is protected by all applicable copyrights, trademarks, |
 |  service marks, patents or other proprietary rights and laws, and by virtue   |
 |  of accessing the Web Site, except as expressly authorized by the Azalea      |
 |  Technology, LLC., you agree not to modify, rent, lease, loan, sell,          |
 |  distribute, store, or create derivative works based on the Web Site, in      |
 |  whole or in part.                                                            |
 |-------------------------------------------------------------------------------|
 |  Decrypting or otherwise decoding the following programming language code is  |
 |  strictly prohibited except as expressly authorized by Azalea Technology,     |
 |  LLC. Upon conviction of such a violation in a court of applicable            |
 |  jurisdiction, such person(s) may be subject to the maximum allowable penalty |
 |  as permitted under such law.                                                 |
  -------------------------------------------------------------------------------
         Purpose: Event function library
      Programmer: Benjamin Roberts
                  Azalea Technology, LLC.
                  P.O. Box 131150
                  Tyler, TX 75713-1150
  -------------------------------------------------------------------------------
  Handler.js -- Portable event handler registration functions

  This module defines event handler registration and deregistration functions
  Handler.add() and Handler.remove(). Both functions take three arguments:

    element: the DOM element, document, or window on which the handler
       is to be added or removed.

    eventType: a string that specifies the type of event for which the
       handler is to be invoked.  Use DOM-standard type names, which do
       not include an "on" prefix. Examples: "click", "load", "mouseover".

    handler: The function to be invoked when an event of the specified type
       occurs on the specified element.  This function will be invoked as
       a method of the element on which it is registered, and the "this"
       keyword will refer to that element.  The handler function will be
       passed an event object as its sole argument.  This event object will
       either be a DOM-standard Event object or a simulated one. If a 
       simulated event object is passed, it will have the following DOM-
       compliant properties: type, target, currentTarget, relatedTarget,
       eventPhase, clientX, clientY, screenX, screenY, altKey, ctrlKey,
       shiftKey, charCode, stopPropagation(), and preventDefault()

  Handler.add() and Handler.remove() have no return value.

  Handler.add() ignores duplicate registrations of the same handler for
  the same event type and element.  Handler.remove() does nothing if called
  to remove a handler that has not been registered.

  Implementation notes:

  In browsers that support the DOM standard addEventListener() and
  removeEventListener() event registration functions, Handler.add() and
  Handler.remove() simply invoke these functions, passing false as the 
  third argument (meaning that the event handlers are never registered as
  capturing event handlers).

  In versions of Internet Explorer that support attachEvent(), Handler.add()
  and Handler.remove() use attachEvent() and detachEvent(). To
  invoke the handler function with the correct this keyword, a closure is
  used.  Since closures of this sort cause memory leaks in Internet Explorer,
  Handler.add() automatically registers an onunload handler to deregister
  all event handlers when the page is unloaded. To keep track of
  registered handlers, Handler.add() creates a property named _allHandlers on
  the window object and creates a property named _handlers on any element on
  which a handler is registered.
  -------------------------------------------------------------------------------
*/

var Handler = {};

// In DOM-compliant browsers, our functions are trivial wrappers around
// addEventListener() and removeEventListener().
if(document.addEventListener){
	Handler.add = function(element, eventType, handler){
		element.addEventListener(eventType, handler, false);
	};

	Handler.remove = function(element, eventType, handler){
		element.removeEventListener(eventType, handler, false);
	};
}

// In IE 5 and later, we use attachEvent() and detachEvent(), with a number of
// hacks to make them compatible with addEventListener and removeEventListener.
else if(document.attachEvent){
	Handler.add = function(element, eventType, handler){
		// Don't allow duplicate handler registrations
		// _find() is a private utility function defined below.
		if(Handler._find(element, eventType, handler) != -1) return;

		// To invoke the handler function as a method of the
		// element, we've got to define this nested function and register
		// it instead of the handler function itself.
		var wrappedHandler = function(e){
			if(!e) e = window.event;

			// Create a synthetic event object with partial compatibility
			// with DOM events.
			var event = {
				_event: e, // In case we really want the IE event object
				type: e.type, // Event type
				target: e.srcElement, // Where the event happened
				currentTarget: element, // Where we're handling it
				relatedTarget: e.fromElement?e.fromElement:e.toElement,
				eventPhase: (e.srcElement==element)?2:3,

				// Mouse coordinates
				clientX: e.clientX, clientY: e.clientY,
				screenX: e.screenX, screenY: e.screenY,

				// Key state
				altKey: e.altKey, ctrlKey: e.ctrlKey,
				shiftKey: e.shiftKey, charCode: e.keyCode,

				// Event management functions
				stopPropagation: function(){this._event.cancelBubble = true;},
				preventDefault: function(){this._event.returnValue = false;}
			}

			// Invoke the handler function as a method of the element, passing
			// the synthetic event object as its single argument.
			// Use Function.call() if defined; otherwise do a hack
			if(Function.prototype.call)
				handler.call(element, event);
			else{
				// If we don't have Function.call, fake it like this
				element._currentHandler = handler;
				element._currentHandler(event);
				element._currentHandler = null;
			}
		};

		// Now register that nested function as our event handler.
		element.attachEvent("on" + eventType, wrappedHandler);

		// Now we must do some record keeping to associate the user-supplied
		// handler function and the nested function that invokes it.

		// We have to do this so that we can deregister the handler with the
		// remove() method and also deregister it automatically on page unload.

		// Store all info about this handler into an object
		var h = {
			element: element,
			eventType: eventType,
			handler: handler,
			wrappedHandler: wrappedHandler
		};

		// Figure out what document this handler is part of.
		// If the element has no "document" property, it is not
		// a window or a document element, so it must be the document
		// object itself.
		var d = element.document || element;
		// Now get the window associated with that document
		var w = d.parentWindow;

		// We have to associate this handler with the window,
		// so we can remove it when the window is unloaded
		var id = Handler._uid();  // Generate a unique property name
		if(!w._allHandlers) w._allHandlers = {};  // Create object if needed
		w._allHandlers[id] = h; // Store the handler info in this object

		// And associate the id of the handler info with this element as well
		if(!element._handlers) element._handlers = [];
		element._handlers.push(id);

		// If there is not an onunload handler associated with the window,
		// register one now.
		if(!w._onunloadHandlerRegistered){
			w._onunloadHandlerRegistered = true;
			w.attachEvent("onunload", Handler._removeAllHandlers);
		}
	};

	Handler.remove = function(element, eventType, handler) {
		// Find this handler in the element._handlers[] array.
		var i = Handler._find(element, eventType, handler);
		if(i == -1) return;  // If the handler was not registered, do nothing

		// Get the window of this element
		var d = element.document || element;
		var w = d.parentWindow;

		// Look up the unique id of this handler
		var handlerId = element._handlers[i];
		// And use that to look up the handler info
		var h = w._allHandlers[handlerId];
		// Using that info, we can detach the handler from the element
		element.detachEvent("on" + eventType, h.wrappedHandler);
		// Remove one element from the element._handlers array
		element._handlers.splice(i, 1);
		// And delete the handler info from the per-window _allHandlers object
		delete w._allHandlers[handlerId];
	};

	// A utility function to find a handler in the element._handlers array
	// Returns an array index or -1 if no matching handler is found
	Handler._find = function(element, eventType, handler) {
		var handlers = element._handlers;
		if(!handlers) return -1; // if no handlers registered, nothing found

		// Get the window of this element
		var d = element.document || element;
		var w = d.parentWindow;

		// Loop through the handlers associated with this element, looking
		// for one with the right type and function.
		// We loop backward because the most recently registered handler
		// is most likely to be the first removed one.
		for(var i = handlers.length-1; i >= 0; i--){
			var handlerId = handlers[i]; // get handler id
			var h = w._allHandlers[handlerId]; // get handler info
			// If handler info matches type and handler function, we found it.
			if(h.eventType == eventType && h.handler == handler)
				return i;
		}
		return -1;  // No match found
	};

	Handler._removeAllHandlers = function() {
		// This function is registered as the onunload handler with 
		// attachEvent.  This means that the this keyword refers to the
		// window in which the event occurred.
		var w = this;

		// Iterate through all registered handlers
		for(id in w._allHandlers){
			// Get handler info for this handler id
			var h = w._allHandlers[id]; 
			// Use the info to detach the handler
			h.element.detachEvent("on" + h.eventType, h.wrappedHandler);
			// Delete the handler info from the window
			delete w._allHandlers[id];
		}
	}

	// Private utility to generate unique handler ids
	Handler._counter = 0;
	Handler._uid = function(){ return "h" + Handler._counter++; };
}

//-->
