/**
 *
 *
 *
 * @package     PriceGrabber
 * @subpackage  Utils
 * @category    JavaScript
 *
 * @author      Philip Snyder <philip@pricegrabber.com>
 * @copyright   Copyright &copy; 2005 2006 2007, Philip Snyder, PriceGrabber.com
 * @version     v0.0.1a
 *
 */

/**
 * Class is an event manager that handles attaching events and
 * maintaining function/variable scope in a cross-browser manner.
 *
 * Note: Do not define your handler as an anonymous function as this
 * will cause rampant memory leaks in Internet Explorer.
 *
 * Basic example:
 *
 *    <script language="JavaScript" src="js/classes/EventManager.js" type="text/javascript"></script>
 *    <script language="JavaScript">
 *    function Document_KeyDown_Handler(e) {
 *        e = (e) ? e : ((window.event) ? window.event : null);
 *        if (e) alert(e.keyCode);
 *    }
 *    window.addEvent(document, 'keydown', Document_KeyDown_Handler, document, true);
 *    </script>
 *
 * @since     v0.0.1a
 * @return    EventManager
 */


function EventManager() {

    window.onload = null;
    window.onunload = null;
//    this.addEvent    = EventManager_AddEvent;
//    this.removeEvent = EventManager_RemoveEvent;
//    this.addOnLoad   = EventManager_AddOnLoad;
//    this.addOnUnload = EventManager_AddOnUnload;
//    this.addOnResize = EventManager_AddOnResize;
    
    
    
    this.alterEventObject = function(e) { };
    
    
	var registry      = {};
	var targetIdCount = 0;

	function registryKey(id, type, fn) {
		return id+'#'+type+'#'+fn;
	}

	function isInRegistry(key) {
		return registry[key] != null;
	}

	function invoke(key, evt) {
		if (!isInRegistry(key)) return null;
		if (window.eventManager) {
    		var _evt = window.eventManager.alterEventObject(evt);
    		if (typeof(_evt) == 'object') evt = _evt;
    		var handler = registry[key];
    		var scope   = handler.scope;
    		scope.__EventManager_Listener__ = handler.listener;
    		scope.__EventManager_Listener__(evt);
    		scope.__EventManager_Listener__ = null;
    		return true;
		} else {
		    return false;
		}
	}

	function targetId(target) {
		if (target == document)      return "__EventManager_ID_document";
		if (target == window)        return "__EventManager_ID_window";
		if (target+'' == xmlhttp+'') return "__EventManager_ID_xmlhttp";
		try {
    		var id = target.getAttribute('id') || target.uniqueID;
    		if (id == null) {
    			id = "__EventManager_ID_"+(targetIdCount++);
    			target.setAttribute('id', id);
    		}
		} catch (e) {
//		    throw new Error('target: "'+target+'" vs xmlhttp: "'+xmlhttp+'"');
		}
		return id;
	}


    this.addEvent = function(target, type, listener, scope, capture) {
    	var key, handler;
    	key   = registryKey(targetId(target), type, listener);
    	scope = scope || target;
    	if (isInRegistry(key)) window.removeEvent(target, type, listener);
    	handler = { listener: listener, scope: scope, capture: capture, invoker: function(evt) { invoke(key, evt); } };
    	registry[key] = handler;
    	if (target.addEventListener) target.addEventListener(type, handler.invoker, capture);
    	else if (target.attachEvent) target.attachEvent('on'+type, handler.invoker);
    	else                         return false;
    	if (typeof(window.addOnUnload) == 'function') window.addOnUnload( function() { /*alert('calling window.removeEvent.');*/ window.removeEvent(target, type, listener); target = type = listener = null; } );
    	scope = null;
    	return true;
    }

    this.removeEvent = function(target, type, listener) {
        var key, invoker, capture;
        key = registryKey(targetId(target), type, listener);
        if (!isInRegistry(key)) return false;
        invoker       = registry[key].invoker;
        capture       = registry[key].capture;
        registry[key] = null;
        if (target.removeEventListener) target.removeEventListener(type, invoker, capture);
        else if (target.detachEvent)    target.detachEvent("on" + type, invoker);
        else                            return false;
        return true;
    }


//    this.handleOnload = function() {
//        for (var i=0; i<window.eventManager.onload.length; i++) {
//            window.eventManager.onload[i]();
//        }
//    }
    
//    this.addOnLoad = function(func) {
//        if (window.onload != this.handleOnload) {
//            window.eventManager.onload[window.eventManager.onload.length] = window.onload;
//            window.onload = this.handleOnload;
//        }
//        window.eventManager.onload[window.eventManager.onload.length] = func;
////        if (typeof(window.onload) != 'function') window.onload = func;
////        else {
////            var oldLoad = window.onload;
////            window.onload = function() {
////                oldLoad();
////                func();
////            }
////        }
//    }
    
//    this.addOnUnload = function(func) {
//        if (typeof(window.onunload) != 'function') window.onunload = func;
//        else {
//            var oldUnload = window.onunload;
//            window.onunload = function() {
//                alert('running '+oldUnload);
//                oldUnload();
//                alert('running '+func);
//                func();
//            }
//        }
//    }
    
    this.addOnResize = function(func) {
        if (typeof(window.onresize) != 'function') window.onresize = func;
        else {
            var oldResize = window.onresize;
            window.onresize = function() {
                oldResize();
                func();
            }
        }
    }
    
    return this;
}


EventManager.prototype             = new Object;
EventManager.prototype.constructor = EventManager;
EventManager.superclass            = Object.prototype;









window.eventManager = new EventManager();
window.EventManager = window.eventManager;






















function EventManager_CleanWindow() {
    window.onafterprint   = null;
    window.onbeforeprint  = null;
    window.onbeforeunload = null;
    window.onblur         = null;
    window.ondragdrop     = null;
    window.onerror        = null;
    window.onfocus        = null;
    window.onhelp         = null;
    window.onload         = null;
    window.onmove         = null;
    window.onresize       = null;
    window.onscroll       = null;
    window.onunload       = null;
}
    



EventManager.instance = null;
EventManager.getInstance = function() {
    if (!EventManager.instance) EventManager.instance = new EventManager();
    return EventManager.instance;
}





/**
 * Methods opened up to the window object:
 *
 *   addEvent
 *   removeEvent
 *   addOnload
 *   addOnUnload
 *   addOnResize
 *
 */

window.addEvent = function(target, type, listener, scope, capture) {
    if (window.eventManager) window.eventManager.addEvent(target, type, listener, scope, capture);
    else return false;
}

window.removeEvent = function(target, type, listener) {
    if (window.eventManager) window.eventManager.removeEvent(target, type, listener);
    else return false;
}








/**
 * Handles for multiple onLoad functions.
 */
window.loadQueue = new Array();
// Alias the names so it makes sense for everyone
window.addOnLoad = window.addOnload = function Window_AddOnLoad(func) {
    if (!(window.onload instanceof Function)) {
        if (window.onload) {
            var oldOnload = window.onload;
            window.loadQueue.push(function() { oldOnload });
        }
        window.onload = Window_OnLoadQueue;
    } else if (window.onload !== Window_OnLoadQueue) {
        loadQueue.push(window.onload);
        window.onload = Window_OnLoadQueue;
    }
    var sz = window.loadQueue.length;
    window.loadQueue[sz] = func;
}
function Window_OnLoadQueue() {
    for (var i=0; i<window.loadQueue.length; i++) {
        var loadFunc = window.loadQueue[i];
        loadFunc();
    }
}
window.onload = Window_OnLoadQueue;








/**
 * Handles for multiple onUnload functions.
 */
window.unloadQueue = new Array();
function Window_OnUnloadQueue() {
    for (var i=0; i<window.unloadQueue.length; i++) {
        var unloadFunc = window.unloadQueue[i];
        unloadFunc();
    }
    /**
     * Google Maps API Version 2 introduced a javascript
     * closure cleanup function... call it if its defined.
     */ 
    if (typeof(GUnload) == 'function') GUnload();
    // Cleanup event manager memory space
    window.onload       = null;
    window.onunload     = null;
    window.onresize     = null;
    window.eventManager = null;
    window.EventManager = null;
}
// Alias the names so it make sense for everyone
window.addOnunload = window.addOnUnLoad = window.addOnUnload = function Window_AddOnUnload(func) {
    if (!(window.onunload instanceof Function)) {
        if (window.onunload) {
            var oldUnload = window.onunload;
            window.unloadQueue.push(function() { oldUnload });
        }
        window.onunload = Window_OnUnloadQueue;
    } else if (window.onunload !== Window_OnUnloadQueue) {
        window.unloadQueue.push(window.onunload);
        window.onunload = Window_OnUnloadQueue;
    }
    window.unloadQueue.push(func);
}
window.onunload = Window_OnUnloadQueue;










window.addOnResize = function(func) {
    if (window.eventManager) window.eventManager.addOnResize(func);
    else return false;
}



if (typeof window.addOnUnload == 'function') window.addOnUnload(EventManager_CleanWindow);









/**
 * This kinda sucks that I have to instantiate an XMLHttpRequest
 * in order to come up with a valid id for them in the event manager.
 */
var xmlhttp = null;
try {
    xmlhttp = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
    try {
        xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
    } catch (e) {
        try {
            xmlhttp = new XMLHttpRequest();
        } catch (e) {
            //this.request = new IframeHttpRequest();
            xmlhttp = null;
        }
    }
}
