﻿var EventMan = {
	objGuid: 1,
	handlerGuid: 1,
	eventGuid: 0,
	events: {},
	defaultParams: {
		scope: null,
		userdata: null,
		capture: false
	},
	customEvents: {},
	
	addEvent: function(obj, type, handler, params) {
		this.eventGuid++;
		params = params || this.defaultParams;
		
		if (!handler.$$guid) handler.$$guid = this.handlerGuid++;

		var internalHandler = this._makeInternalHandler(obj,type,handler,params);
		
		if (type == "mousewheel") {
			if (/Gecko/i.test(navigator.userAgent)) {
				type = "DOMMouseScroll";
			}
		}

		this.events[this.eventGuid] = { obj: obj, type: type, handler: internalHandler, capture: params.capture };
		
		if (obj.addEventListener) {
			obj.addEventListener(type, internalHandler, params.capture);
		} else if (obj.attachEvent) {
			obj.attachEvent("on" + type, internalHandler);
		} else if (this._hasCustomEvent(obj,type)) {
			this._registerCustomHandler(obj,type,internalHandler);
		}
		return this.eventGuid;
	},
	
	removeEvent: function(id) {
		var e = this.events[id];
		var params = e.params || this.defaultParams;
		if (e.obj.removeEventListener) {
			e.obj.removeEventListener(e.type,e.handler,params.capture);
		} else if (e.obj.detachEvent) {
			e.obj.detachEvent("on" + e.type,e.handler);
		} else if (this._hasCustomEvent(e.obj,e.type)) {
			this._unregisterCustomHandler(e.obj,e.type,e.handler);
		}
		delete this.events[id];
		delete e;
	},
	
	_makeInternalHandler: function(obj, type, handler, params) {
		var internalHandler = function(e) {
			e.$$eventType = type;
			e = e || window.event;
			if (!e.$$customEvent) {
				e = EventMan._fixEvent(e);
			}
			handler.call((params.scope ? params.scope : obj), e, obj, params.userdata);
			return !e.cancelled;
		};
		internalHandler.originalHandler = handler;
		return internalHandler;
	},
		
	_clearHandlers: function() {
		for (var id in this.events) {
			this.removeEvent(id);
			delete this.events[id];
		}
	},
	
	_hasCustomEvent: function(obj, type) {
		if (obj.$$guid) {
			return (this.customEvents[obj.$$guid][type] != undefined);
		}
		return false;
	},
	
	_registerCustomEvent: function(ce) {
		var obj = ce.obj;
		if (!obj.$$guid) {
			obj.$$guid = this.objGuid++;
		}
		if (!this.customEvents[obj.$$guid]) {
			this.customEvents[obj.$$guid] = {};
		}
		var customEvents = this.customEvents[obj.$$guid];
		customEvents[ce.type] = ce;
	},
	
	_registerCustomHandler: function(obj, type, handler, params) {
		var customEvents = this.customEvents[obj.$$guid];
		customEvents[type].handlers[handler.originalHandler.$$guid] = handler;
	},
	
	_unregisterCustomHandler: function(obj, type, handler, params) {
		var customEvents = this.customEvents[obj.$$guid];
		delete customEvents[type].handlers[handler.originalHandler.$$guid];
	},
	
	_fixEvent: function(e) {
		// Quick test for IE, just to speed up other browsers (Opera will have to go through this too...)
		if (document.all) {
			// Implement W3C event model for IE
			if (!e.target) e.target = e.srcElement;
			if (!e.stopPropagation) e.stopPropagation = function() {
				this.cancelBubble = true;
			}
			if (!e.preventDefault) e.preventDefault = function() {
				this.returnValue = false;
			}
			if (e.fromElement) e.relatedTarget = e.fromElement;
			if (e.toElement) e.relatedTarget = e.toElement;
		}
		
		if (e.$$eventType == "mousewheel") {
			var delta = 0;
			if (e.wheelDelta) {
				delta = e.wheelDelta / 120;
				if (window.opera) {
					delta = -delta;
				} 
			} else if (e.detail) {
				delta = -e.detail / 3;
			}
			e.delta = delta;
		}
		// This allows e.cancel() to cancel an event in every way. stops bubbling/propagation and 
		// prevents default - both by preventDefault()/returnValue and the actual return value of the handler.
		e.cancelled = false;
		e.cancel = function() {
			this.cancelled = true;
			this.stopPropagation();
			this.preventDefault();
		}
		return e;
	}
}

var CustomEvent = Class.create();

extendObject(CustomEvent.prototype, {
	init: function(obj, type) {
		this.obj = obj;
		this.type = type;
		this.$$customEvent = true;
		this.handlers = {};
		EventMan._registerCustomEvent(this);
	},
	
	fire: function() {
		for (var key in this.handlers) {
			if (this.handlers[key]) {
				this.handlers[key](this);
			}
		}
	}
});

EventMan.addEvent(window,"unload",EventMan._clearHandlers,{ scope: EventMan });
