JSLIB.namespace("JSLIB.event");
JSLIB.namespace("JSLIB.eventData");

// eventData class. use new to create new copy
JSLIB.eventData = function(registeredTarget, domEvent) {
    // browser event object
    this.domEvent=domEvent;
    this.registeredTarget=registeredTarget;
	if (domEvent) {
	    this.target=(domEvent.target)?domEvent.target:domEvent.srcElement;
	    this.type=domEvent.type;

	    // mouse specific
	    this.clientX=domEvent.clientX;
	    this.clientY=domEvent.clientY;
	    this.button=domEvent.button;

	    // keyboard specific
	    this.keyCode=domEvent.keyCode;
	    this.altKey=domEvent.altKey;
	    this.ctrlKey=domEvent.ctrlKey;
	    this.shiftKey=domEvent.shiftKey;
	}
	else {
	    this.target=registeredTarget;
	}
}

// object used as-is
JSLIB.event = new function () {

    return {
		// used by ondomready handling. may use onload as fall back but this var indicate whether DOMContentLoaded event handled already
		_domReadyDone:false,
		_domReadyTimer:null,
		
		// event cache to handle memory leak under <= IE6
		_eventCache:[],

		// cache events and they will be cleared upon page unload to deal with memory leak
		// two modes of operations:
		// 1. obj.onsomething = func; - JSLIB.event.cacheEvent(obj, "onsomething", null);
		// 2. eventListener; - JSLIB.event.cacheEvent(obj, "something", func);
		cacheEvent:function(elem, eventName, callbackFunc) {
			this._eventCache[this._eventCache.length] = [elem, eventName, callbackFunc];
		},
		
		// call in unload
		flushEvents:function() {
			for (var i = this._eventCache.length - 1; i >= 0; i --) {
				var obj = JSLIB.dom.get(this._eventCache[i][0]);
				if (!obj) {
					continue;
				}
				if (this._eventCache[i][2]) {
					this.removeListener(this._eventCache[i][0], this._eventCache[i][1], this._eventCache[i][2]);
				}
				else {
					obj.setAttribute(this._eventCache[i][1], null);
				}
			}
		},
		
        addListener:function(elem, eventName, callbackFunc) {
			var obj = JSLIB.dom.get(elem);
			if (eventName.toLowerCase() == "domready") {
				// idea from http://www.javascriptkit.com/dhtmltutors/domready.shtml
				// also from http://snipplr.com/view/2337/ondomready/
				if(obj.attachEvent && !window.opera){ // IE
					document.write('<script type="text/javascript" id="JSLIB_contentloadtag" defer="defer" src="//javascript:void(0)"><\/script>');
					var contentloadtag=document.getElementById("JSLIB_contentloadtag");
					contentloadtag.onreadystatechange=function(evt){
						if (this.readyState=="complete"){
							if (!JSLIB.event._domReadyDone) {
								JSLIB.event._domReadyDone = true;
								return callbackFunc(new JSLIB.eventData(obj, window.event));
							}
							else {
								return false;
							}
						}
					}
					this.cacheEvent(contentloadtag, "onreadystatechange", null);
				}
				else if (JSLIB.browser.isSafari) { // safari
					this._domReadyTimer = setInterval(function() {
							if (/loaded|complete/.test(document.readyState)) {
								window.clearInterval(JSLIB.event._domReadyTimer);
								if (!JSLIB.event._domReadyDone) {
									JSLIB.event._domReadyDone = true;
									return callbackFunc(new JSLIB.eventData(obj, null));
								}
								else {
									return false;
								}
							}
						}, 10);					
				}
				else if (obj.addEventListener) { // all other DOM compatible browsers
					var func;
					func = function(evt) {
							if (!JSLIB.event._domReadyDone) {
								JSLIB.event._domReadyDone = true;
								return callbackFunc(new JSLIB.eventData(obj, evt));
							}
							else {
								return false;
							}
						};
					obj.addEventListener("DOMContentLoaded", func, false);
					this.cacheEvent(obj, "DOMContentLoaded", func);
				}

				// use onload handler in case browser does not support DOMContentLoaded
				var func = function(evt) {
						if (!JSLIB.event._domReadyDone) {
							JSLIB.event._domReadyDone = true;
							return callbackFunc(evt);
						}
						return false;
					};
				this.__addListenerSub(obj, "load", func);
			}
			else {
				return this.__addListenerSub(elem, eventName, callbackFunc);
			}
		},
		
        __addListenerSub:function(elem, eventName, callbackFunc) {
            var obj = JSLIB.dom.get(elem);
            if (!obj) {
                return false;
            }

            var wrapfunc = function(evt) {
                var realEvent = evt || window.event;
                return callbackFunc(new JSLIB.eventData(obj, realEvent));
            };

            if(obj.attachEvent){ // IE
                obj.attachEvent("on"+eventName, wrapfunc);
				this.cacheEvent(obj, eventName, wrapfunc);
                return true;
            }
            else if(obj.addEventListener){ // Mozilla
                obj.addEventListener(eventName, wrapfunc, false);
				this.cacheEvent(obj, eventName, wrapfunc);
                return true;
            }
            return false;
        },

		removeListener:function(elem, eventName, callbackFunc) {
            var o = JSLIB.dom.get(elem);
            if (!o) {
                return false;
            }
			
			if(o.removeEventListener){
				o.removeEventListener(eventName, callbackFunc, false);
			}
			else if(o.detachEvent){
				o.detachEvent("on"+eventName, callbackFunc);
			};
			
			// should remove from event cache
		},
		
        preventDefault:function (ed) {
            if (JSLIB.browser.browser == "msie") {
                ed.domEvent.returnValue = false;
            }
            else if (ed.domEvent.preventDefault) {
                ed.domEvent.preventDefault();
            }
        },

        stopPropagation:function (ed) {
            if (JSLIB.browser.browser == "msie") {
                ed.domEvent.cancelBubble = true;
            }
            else if (ed.domEvent.stopPropagation) {
                ed.domEvent.stopPropagation();
            }
        }
    }
}

JSLIB.namespace("JSLIB.customEvent");
JSLIB.customEvent = function (properties) {
    JSLIB.callParentConstructor(JSLIB.object, this);
	if (properties) {
        this._listeners = [];
    }
}
JSLIB.inherits(JSLIB.object, JSLIB.customEvent);

// listener: object has properties 
//   callback (callback function), 
//   obj (callback will be under this scope)
//   data (parameter that will be passed to callback)
JSLIB.customEvent.prototype.subscribe = function (listener) {
    if (JSLIB.util.isObject(listener)) {
        var id = this._listeners.length;
        this._listeners[id] = listener;
        return id;
    }
    else {
        return -1;
    }
}

JSLIB.customEvent.prototype.unsubscribe = function (id) {
    if (this._listeners[id]) {
        delete this._listeners[id];
        this._listeners[id] = null;
    }
}

JSLIB.customEvent.prototype.fireEvent = function (id) {
    for (var i = 0; i < this._listeners.length; i ++) {
        if (this._listeners[i]) {
            if (this._listeners[i].callback) {
                if (this._listeners[i].obj) {
                    this._listeners[i].callback.call(this._listeners[i].obj, this._listeners[i].data);
                }
                else {
                    this._listeners[i].callback(this._listeners[i].data);
                }
            }
        }
    }
}



// remove all events but it is static function to avoid memory leak problem
function JslibEventUnload() {
	JSLIB.event.flushEvents();
}
JSLIB.event.addListener(window, "unload", JslibEventUnload);
