/* vim: set tabstop=4 textwidth=120: */

/**
 * EventManager.class.js
 *
 * This class handles add and removing events from objects.  It allows for easy cross-browser access as well as easy
 * removal and cleanup to prevent memory leaks.
 *
 * CHANGELOG (last 6)
 * 		08/29/05 MDB - Initial Version (0.1)
 *
 * @author	Mike Bulman
 * @version	0.1 2005-08-29 16:51:10
 *
 */

function EventManager() {}
EventManager.prototype = {

	/**
	 * Stores all events registered through this object.
	 * @var	array
	 */
	_registry: null,

	// {{{  init()
	/**
	 * Constructor for event manager.
	 *
	 */
	init: function() {

		// If the object has not had init called...
		if (this._registry == null)
		{
			this._registry	= [];

			// Add listener for window unload to flush all handlers
			this.add(window, 'unload', this.flush, false);
		}
	},
	// }}} init()
	// {{{ add()
	/**
	 * Adds an listener to a specific object.
	 * Cross-browser event handling for IE5+, NS6+ and Mozilla/Gecko.
	 * By Scott Andrew
	 *
	 * @param	object	elm			Element to add the event to
	 * @param	string	evType		Event to handle (load, click, mouseover, etc)
	 * @param	object	fn			Callback function
	 * @param	bool	useCapture	Whether or not to use DOM's event capture (set to false)
	 * @return	mixed
	 *
	 */
	add: function(elm, evType, fn, useCapture) {

		this.init();

		// Add the event to the registry
		{
			// Determine what to store (whether the element has an id or not)
			this._registry[this._registry.length] = {elem: elm, type: evType, func: fn, capt: useCapture};
		}


		// Add the event listener
		{
			if (elm.addEventListener) {
				elm.addEventListener(evType, fn, useCapture);
				return true;
			} else if (elm.attachEvent) {
				var r = elm.attachEvent('on' + evType, fn);
				return r;
			} else {
				elm['on' + evType] = fn;
			}
		}
	},
	// }}} add()
	// {{{ flush()
	/**
	 * Clears all listeners in the registry from their objects.
	 *
	 * @return	null
	 *
	 */
	flush: function() {
		if (this._registry == null) return;

		for (var i=0,ii=this._registry.length; i<ii; i++) {
			if (this._registry[i]) {
				with (this._registry[i]) {
					this.remove(elem, type, func, capt);
				}
			}
		}

		this._registry = null;
	},
	// }}} flush()
	// {{{ flush_elem_recursive()
	/**
	 * Clear all listeners from an element and its children.
	 *
	 * @param	object	elm		Element to recurse from (inclusive)
	 * @return	null
	 *
	 */
	flush_elem_recursive: function(elm) {
		
		var children = elm.childNodes;
		for (var i=0,ii=children.length; i<ii; i++) {
			this.flush_elem_recursive(children[i]);
		}
		this.remove_by_elem(elm);
		
	},
	// }}} flush_elem_recursive()
	// {{{ get_events()
	/**
	 * Get all events for an element and event type.
	 *
	 * @param	object	elm		Element to look for
	 * @param	string	evType	Event type to look for
	 * @return	array	Array of functions
	 *
	 */
	get_events: function(elm, evType) {
		var retval = new Array;

		for (var i=0,ii=this._registry.length; i<ii; i++) {
			if (this._registry[i] != null && this._registry[i].type == evType && this._registry[i].elem == elm) {
				retval.push(this._registry[i].func);
			}
		}

		return retval;
	},
	// }}} get_events()
	// {{{ remove()
	/**
	 * Cross-browsers solution for removing an event listener for a given object.
	 *
	 * @param	object	elm			Element to remove the event from
	 * @param	string	evType		Event to remove
	 * @param	object	fn			Callback function
	 * @param	bool	useCapture	Whether or not to use DOM's event capture (set to false)
	 * @return	mixed
	 *
	 */
	remove: function(elm, evType, fn, useCapture) {
		if (elm.removeEventListener) {
			elm.removeEventListener(evType, fn, useCapture);
			return true;
		} else if (elm.detachEvent) {
			var r = elm.detachEvent('on' + evType, fn);
			return r;
		} else {
			elm['on' + evType] = '';
		}
	},
	// }}} remove()
	// {{{ remove_by_elem()
	/**
	 * Remove all handlers for a certain element.
	 *
	 * @param	object	elm		Element to remove handlers for
	 * @return	null
	 *
	 */
	remove_by_elem: function(elm) {
		for (var i=0,ii=this._registry.length; i<ii; i++) {
			if (this._registry[i] != null && this._registry[i].elem == elm) {
				this.remove(elm, this._registry[i].type, this._registry[i].func, this._registry[i].capt);
				this._registry[i] = null;
			}
		}
	},
	// }}} remove_by_elem()
	// {{{ remove_by_elem_type()
	/**
	 * Remove all handlers for a certain element and type.
	 *
	 * @param	object	elm		Element to look for
	 * @param	string	evType	Event type to look for
	 * @return	null
	 *
	 */
	remove_by_elem_type: function(elm, evType) {
		for (var i=0,ii=this._registry.length; i<ii; i++) {
			if (this._registry[i] != null && this._registry[i].type == evType && this._registry[i].elem == elm) {
				this.remove(elm, evType, this._registry[i].func, this._registry[i].capt);
				this._registry[i] = null;
			}
		}
	}
	// }}} remove_by_elem_type()

}

