///<reference path="../TESCO.js" />
///<reference path="exception.js" />
/*

compatibility: WinIE5, WinIE5.5, WinIE6, Mozilla, Firefox, Netscape 7+, Opera 7+, Safari, Konqueror
		NOTES: 1) Does not support DOM0 events (e.g. MacIE)
			 2) Safari code added for onclick events to ensure event cancellation is supported for this event.
			This is added to the DOM0 onclick and does not retain any existing code. Therefore the original
			   event code will *not* be executed.

	Example:
	
	TESCO.system.event.attach(document.body, "click", 
		function(e) {
			//	click occured
			TESCO.system.event.detach(document.body, "click", arguments.callee);
		}
	}

*/

TESCO.system.event = new function() {

	this.NAME = "TESCO.system.event";

	var _listeners = [];
	var _unloadListeners = [];
	
	//	constants
	var NODE = 0, TYPE = 1, FN = 2, SCOPE = 3, ARGS = 4, USECAPTURE = 5, WRAPPER = 6; 
	
	function _getEvent(e) {
		if (e) {
			return e;
		} else if (window.event) {
			return window.event;
		} else {
			return {}
		}
	}
	
	function _getListenerIndex(node, type, fn, useCapture) {
		var _index = -1;
		for (var i = 0, L = _listeners.length; i < L; ++i) {
			var _listener = _listeners[i];
			if (_listener && 
				(_listener[FN] == fn) && 
				(_listener[NODE] == node) && 
				(_listener[TYPE] == type) && 
				(_listener[USECAPTURE] == useCapture)) {
				_index = i;
				break;
			}
		}
		return _index;
	}
	
	function _getScroll() {
		var _dd = document.documentElement, _db = document.body;
		if (_dd && _dd.scrollTop) {
			return [_dd.scrollLeft, _dd.scrollTop];
		} else if (_db) {
			return [_db.scrollLeft, _db.scrollTop];
		} else {
			return [0, 0];
		}
	}
	
	function _dispatch(e, node, scope, fn, args) {
		var _e = _getEvent(e);
		
		var _setPreventDefault = false;
		var _setStopPropagation = false;
		var _setRelatedTarget = false;
		var _setCurrentTarget = false;

        function _preventDefault() {
            if (!this.preventDefault) {
			    _setPreventDefault = true;
				if (!this.canceldisabled) {
					this.returnValue = false;
				}
		    } else {
		        this.preventDefault();
		    }
        }

        function _stopPropagation() {
            if (!this.stopPropagation) {
			    _setStopPropagation = true;
			    this.cancelBubble = true;
		    } else {
		        this.stopPropagation();
		    }
        }

		_e.stopEvent = function() {
			this.stop();
			this.prevent();
		}
		
		_e.stop = function() {
		    this.stopped = true;
		    _stopPropagation.call(this);
		}
		
		_e.prevent = function() {
		    this.prevented = true;
		    _preventDefault.call(this);
		}
		
		if (!_e.target && _e.srcElement) {
			_e.target = _e.srcElement;
		} else {
			/* Get around Safari text node bug */
			try {
				if (_e.target && _e.target.nodeType == 3) {
					_e.target = _e.target.parentNode;
				}
			} catch (err) {
				TESCO.system.Exception.handler.raise(err);
			}
		}
		
		if (!_e.currentTarget) {
		    _setCurrentTarget = true;
			_e.currentTarget = node;
		}

		if (!_e.pageX && _e.pageX !== 0) {
			_e.pageX = _e.clientX || 0;
			
			if (TESCO.system.browser.ie) {
				_e.pageX += _getScroll()[0];
			}
		}

		if (!_e.pageY && _e.pageY !== 0) {
			_e.pageY = _e.clientY || 0;
			
			if (TESCO.system.browser.ie) {
				_e.pageY += _getScroll()[1];
			}
		}
		
		if (!_e.relatedTarget && (_e.type === "mouseover") && _e.fromElement) {
		    _setRelatedTarget = true;
			_e.relatedTarget = _e.fromElement;
		} else if (!_e.relatedTarget && (_e.type === "mouseout") && _e.toElement) { 
		    _setRelatedTarget = true;
			_e.relatedTarget = _e.toElement;
		}

		_e.BUTTONLEFT = 1;
		_e.BUTTONMIDDLE = 2;
		_e.BUTTONRIGHT = 3;

		if (TESCO.system.browser.ie) {
			switch (_e.button) {
				case 1 :
					_e.which = _e.BUTTONLEFT;
					break;
				case 2 :
					_e.which = _e.BUTTONRIGHT;
					break;
				case 4 :
					_e.which = _e.BUTTONMIDDLE;
					break;
			}
		} else if (TESCO.system.browser.opera) {
			switch (_e.button) {
				case 2 :
					_e.which = _e.BUTTONRIGHT;
					break;
				case 3 :
					_e.which = _e.BUTTONMIDDLE;
					break;
			}
		}

		try {
			fn.call(scope, _e, args);
		} catch (e) {
			TESCO.system.Exception.handler.raise(e);
		} finally {
			if (_setPreventDefault) {
				_e.preventDefault = null;
			}
			if (_setStopPropagation) {
				_e.stopPropagation = null;
			}
			if (_setRelatedTarget) {
			    _e.relatedTarget = null;
			}
			if (_setCurrentTarget) {
			    _e.currentTarget = null;
			}
			_e.prevent = null;
			_e.stop = null;
			_e.stopEvent = null;
		
			if (TESCO.system.browser.clickBug && e.type == "click") {
				node.onclick = function(e) {
					return e.returnValue;
				}
			}
		}
	}
	
	function _attach(node, type, fn, args, scope, useCapture, unload) {
	    if (typeof node == "string") {
		    node = document.getElementById(node);
	    }
	    if (!node) {
		    throw new Error(TESCO.system.event.NAME + ".attach: node is undefined");
	    }
	    
	  	useCapture = useCapture || false;
	    scope = scope || node;
        var _wrapper = function(e) {
	        _dispatch(e, node, scope, fn, args);
			if (e.returnValue === false) {
				return false;
			}
        }
        if ("unload" == type && !unload) {   //  ignore the unload event used to purge events, below
			_unloadListeners.push([node, type, fn, scope, args, useCapture, _wrapper]);
		} else {
//				 else if (o.tagName && o.tagName != "IMG" && e == "load") {	// Element already loaded...
//						o.callback = f;
//						o.callback({ type: "load" }, args);
//					}
        	_listeners.push([node, type, fn, scope, args, useCapture, _wrapper]);
    		
	        if (node.addEventListener) {
		        node.addEventListener(type, _wrapper, useCapture);
	        } else if (node.attachEvent) {
		        node.attachEvent("on" + type, _wrapper);
	        }
	    }
	}
	
	this.attach = function(node, type, fn, args, scope, useCapture) {
		_attach(node, type, fn, args, scope, useCapture);
	}
	
	this.detach = function(node, type, fn, useCapture) {
		
	    if (typeof node == "string") {
		    node = document.getElementById(node);
	    } 
	    if (!node) {
		    throw new Error(this.NAME + ".detach: node is undefined");
	    }
		
		useCapture = useCapture || false;
	    var _listener = null;
	    var _index = _getListenerIndex(node, type, fn, useCapture);
	    if (_index >= 0) {
		    var _listener = _listeners[_index];
		    if (node.removeEventListener) {
			    node.removeEventListener(type, _listener[WRAPPER], _listener[USECAPTURE]);
		    } else if (node.detachEvent) {
			    node.detachEvent("on" + type, _listener[WRAPPER]);
		    }
		    delete _listeners[_index][WRAPPER];
		    delete _listeners[_index][FN];
		    delete _listeners[_index];
		    _listeners.splice(_index, 1);
	    }
	}
	
	function _purge(listeners) {
	    for (var i = listeners.length - 1; i >= 0; i--) {
		    _listener = listeners[i];
		    if (_listener) {
			    TESCO.system.event.detach(_listener[NODE], _listener[TYPE], _listener[FN], _listener[USECAPTURE]);
		    }
	    }
	}
	
	function _unload(e) {
	    var _listener;
        for (var i = 0; i < _unloadListeners.length; ++i) { //  fire all the unload events before they are deleted
		    _listener = _unloadListeners[i];
		    if (_listener) {
			    _listener[WRAPPER](e, _listener[NODE], _listener[SCOPE], _listener[FN], _listener[ARGS]);
		    }
	    }
	    _purge(_listeners);
	    _purge(_unloadListeners);
	}
	
	_attach(window, "unload", _unload, null, this, false, true);
}
