/**
 * @author Administrator
 * 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 atguments:
 * 
 * 	element: the DOM element. document. or window on which the handler
 * 		is to be added or removed.
 * 
 * eventType: a sreing 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 incoked when an event of the specified type
 * 	occurs on the specified element. This function willl be invoked as
 * 	a method of the element on which it is registerd. and the "this"
 * keyword will refer to that element. The handler function woll be
 * passed an event object as irs solw argument. This event object will
 * either be a DOM-standar Event object or a simulated one. If a 
 * simulated event object is passed, it will have the following DOM-
 * 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 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-registratoin 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 the support attachEvent(), Handler.add()
 * and Handler.remove() use attachEvent() and detachEvent(). To 
 * invoke the handler function with the correct the keyword, a closure is 
 * used. Since closure of this short 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 = {};
//Handler.add(element, eventType, handler)
//Handler.remove(element, eventType, handler)
//Handler._find(element, eventType, handler)
//Handler._counter (property)
//Handler._uid()
//Handler._removeAllHandlers()


//In DOM-cmopliant browsers, our functions are trivial wrappers around
//addEventListener() and removeEventListener().
if(document.addEventListener)
{
	//alert("Got You   addEventListener");
	Handler.add = function(element, eventType, handler)
	{
		element.addEventListener(eventType, handler, false);
	};
	Handler.remove = function(element, eventType, handler)
	{
		element.removeEventListener(eventType, handler, false);
	};
}
else if(document.attachEvent)
{
//In IE 5 and later, we use attacEvent() and detachEvent() with a number of 
//hacks to make them compatible with addEventListener() and removeEventListener().
	//alert("Got You   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)
		{
			if(debug)alert("test handler add .." + Handler._find(element, eventType, handler));
			return;
		}
		
		//To invoke handler function as a method of element,
		//we'v got to define the 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,
				type: e.type,
				target: e.srcElement,
				currentTarget: element,
				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 handler function as a method of the element, passing
			//the synthetic event object as it 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 kepping 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 the 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 handler has no "document" property, it is not
		//a window or 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
		//alert("add " + id);
		//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 in the element._handlers[] array.
		var i;
		while((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;
			//alert("remove " + i);
			//Look up the unique id of the 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.
			try
			{
				if(h.wrappedHandler !== null || h.wrappedHandler !== undefined)
				{
					element.detachEvent("on" + eventType, h.wrappedHandler);
				}	
			}
			catch(e)
			{
				alert(h.element + "\n" + h.eventType);
			}
			//Reomve 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 on with the right type and function.
		//We loop backwards 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 occured.
		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++;
	};
}
