/**
 * Add and Remove events from objects
 */
PopJavaScriptFramework.v1B1.event = {
	/**
	 * Adds an event to an object
	 * @param {Object} invoker The object to attach an event to
	 * @param {String} type [load | blur | focus | etc]
	 * @param {Function} handler The function to call when the even fires
	 * @param {Object} scope[optional] The scope to apply the handler within, defaults to 'window'
	 */
	add: function( invoker, type, handler, scope ) {
		var scp = scope || window;
		var fn  = function() {
			handler.apply(scp,arguments);
		}
		var key = this._eventToKey(invoker,type,handler);
		this._registry[key] = fn;
		if (invoker.addEventListener) {
			invoker.addEventListener( type, fn, false );
		} else if (invoker.attachEvent) {
			invoker["e"+type+handler] = fn;
			invoker[type+handler] = function() { invoker["e"+type+handler]( window.event ); }
			invoker.attachEvent( "on"+type, fn );
		}
		// unset fn to prevent memory leaks (i think?)
		fn = null;
	},
	/**
	 * The exact same as event.add, but removing instead of adding
	 * @param {Object} invoker The object to attach an event to
	 * @param {String} type [load | blur | focus | etc]
	 * @param {Function} handler The function to call when the even fires
	 * @param {Object} scope[optional] The scope to apply the handler within, defaults to 'window'
	 */
	remove: function( invoker, type, handler, scope ) {
		var scp = scope || window;
		var key = this._eventToKey(invoker,type,handler);
		var fn = this._registry[key];
		if (typeof fn != 'undefined') {
			if (invoker.removeEventListener) {
				invoker.removeEventListener( type, fn, false );
			} else if (invoker.detachEvent) {
				invoker.detachEvent( "on"+type, fn );
				invoker[type+fn] = null;
				invoker["e"+type+fn] = null;
			}
			// unset fn to prevent memory leaks (i think?)
			fn = null;
			this._registry[key] = null;
		}
	},
	/**
	 * Interface for storing all attached events. Enables looking up anonymous functions
	 */
	_registry: {},
	/**
	 * Static variable for storing unique id's for auto-generating element id's
	 */
	_currentId: 0,
	/**
	 * Generates a unique KEY based on an Object, event type, and handler method
	 * @param {Object} oObject
	 * @param {String} type
	 * @param {Function} handler
	 */
	_eventToKey: function(oObject, type, handler) {
		var id,t,h;
		switch (true) {
			case oObject == window:
				id = 'window';
				break;
			case oObject == document:
				id = 'document';
				break;
			case oObject.getAttribute('id') == '':
				id = 'DOMEvent' + this._currentId;
				oObject.setAttribute('id',id);
				this._currentId++;
				break;
			default:
				id = oObject.getAttribute('id');
				break;
		}
		return id+'#'+type+'#'+handler;
	},
	/**
	 * Stop an event from firing
	 * This does no work in Safari prior to 2.0.? (find webkit version)
	 * @param {DOMEvent} DOMEvent
	 */
	stop: function(DOMEvent) {
		var e = DOMEvent;
		if (e) { // event object
			if (e.preventDefault) {	// W3C
				e.preventDefault();
				e.stopPropagation();
			} else {				// IE
				e.returnValue = false;
				e.cancelBubble = true;
			}
		} else {
		}
		return false;
	},
	/**
	 * Returns the DOMNode associated with an event
	 * @param {DOMEvent} DOMEvent
	 * @param {String} sNodeName [optional] The nodeName we're looking for
	 */
	getTarget: function(DOMEvent, sNodeName) {
		var e = DOMEvent, node;
		if (typeof e.target != 'undefined') { // w3c and safari
			node = (e.target.nodeType == 3) ? e.target.parentNode : e.target;
		} else if (typeof e.srcElement != 'undefined') { // IE
			node = e.srcElement;
		}
		if (typeof sNodeName != 'undefined') {
		    while (node.nodeName.toLowerCase() != sNodeName.toLowerCase()) {
		        node = node.parentNode;
		    }
		}
		return node;
	},
	/**
	 * Allows for the implementation of custom events on a per Object level
	 */
	CustomEvents: function() {
		/**
		 * Container object for custom events.
		 */
		this.events = {};
		/**
		 * Create a custom event
		 * @param {String} sEventName The name of your custom event
		 */
		this.create = function(sEventName) {
			this.events[sEventName] = [];
		}
		/**
		 * Fire a custom event
		 * @param {String} sEventName The name of the event to fire
		 */
		this.fire = function(sEventName) {
			if (typeof this.events[sEventName] == 'undefined') { return; }

			for (var i=0; i<this.events[sEventName].length; i++) {
				if (typeof this.events[sEventName][i] == 'undefined') { continue; }

				var id =		this.events[sEventName][i][0];
				var scope =		this.events[sEventName][i][1];
				var method =	this.events[sEventName][i][2];
				var arg =		this.events[sEventName][i][3] || [];
				scope[method].apply(scope, arg);

				if (typeof dbug != 'undefined' && dbug.customEventLogging === true) {
					dbug.log(id, sEventName, method);
				}
			}
			if (typeof dbug != 'undefined' && dbug.customEventLogging === true && this.events[sEventName].length == 0) {
				dbug.log(sEventName);
			}
		}
		/**
		 * Add a method to call when the event is fired
		 * @param {String} sEventName The name of the event to listen for
		 * @param {Object} oListenScope Optional, scope to call the method in. Default is Window
		 * @param {String} sListenMethod The method to call in the given scope
		 * @param {Array} aListenArguments
		 */
		this.addListener = function(sEventName, oListenScope, sListenMethod, aListenArguments) {
			var oListenScope=oListenScope, sListenMethod=sListenMethod, aListenArguments=aListenArguments;
			var sListenID = sEventName + '_' + Math.random().toString().split('.')[1];
			if (typeof sListenMethod == 'undefined') {
				sListenMethod = oListenScope;
				oListenScope = window;
			}
			if (typeof this.events[sEventName] != 'undefined') {
				this.events[sEventName].push([sListenID, oListenScope,sListenMethod,aListenArguments]);
				return sListenID;
			} else {
				return false;
			}
		}
		this.removeListener = function(sListenID) {
			var event_name = sListenID.split('_')[0];
			var ev = this.events[event_name];
			for (var i=0; i<ev.length; i++) {
				if (ev[i][0] == sListenID) {
					delete this.events[event_name][i];
					break;
				}
			}
		}
	}
}
$event = PopJavaScriptFramework.v1B1.event;
