﻿/****************************************************************
window.event handling system.
****************************************************************/
var bubbleEvents;
var nonBubbleEvents;
var customEvents;   
var supportsW3CDomEvents;
var stack;
var maxStack;
var downloaded_coreprototype_js;
var eventManagers;

var debugEnabled;
var totalCountEventsRaised;
var debugEvents;
var debugHandlers;
var nonProcessedNodeNames;

if(downloaded_coreeventmanager_js == null)
{
    eventManagers = [];
    bubbleEvents = ["click", "dblclick", "mousedown", "mouseup", "mouseover", "mousemove", "mouseout", "keypress", "keydown", "keyup", "resize", "scroll", "abort", "error", "select", "reset"];
    nonBubbleEvents = ["beforeunload", "blur", "change", "contextmenu", "cut", "focus", "paste", "load", "unload"];
    customEvents = [];      
    supportsW3CDomEvents = typeof(document.addEventListener) != "undefined";
    stack = [];
    maxStack = 0;

    debugEnabled = false;
    debugEvents = false;
    debugHandlers = true;
    totalCountEventsRaised = 0;
    nonProcessedNodeNames = new Array("debugConsole");    
}
else
{
    throw("Duplicate JS File Exception - coreeventmanager.js has been included more than once\r\n\r\n" + document.location.href);
}

if (typeof(document.readyState) == 'undefined')
{
    document.readyState = "loading";
}

function dereferenceDomObject(domObject)
{
    if(typeof(domObject) == "string")
    {
        domObject = document.getElementById(domObject);
    }
    
    return domObject;
}

function sendDebug(text)
{
    var debugConsole = document.getElementById("debugConsole");

    if (debugConsole != null)
    {
        debugConsole.innerHTML = "<p" + text + "</p>";
    }
}

function deepHandlerCount()
{
    var rv = 0;
    
    for (var i = 0; i < eventManagers.length; i++)
    {
        var eventManager = eventManagers[i];
        rv += eventManager.count();
    }

    return rv;
}

function isNonProcessedNode(element)
{
    var rv = false;   
       
    if ( (element == null || element == document || element == window) == false )
    {
        for(var i = 0; i <  nonProcessedNodeNames.length; i++)
        {    
            var nonProcessedNodeName = nonProcessedNodeNames[i];
            var nonProcessedNode = document.getElementById(nonProcessedNodeName);

            if (nonProcessedNode != null)
            {
                var traverseElement = element;
                
                while (traverseElement != null && traverseElement != document)
                {
                    if (traverseElement == nonProcessedNode)
                    {
                        rv = true;
                        break;
                    }

                    traverseElement = traverseElement.parentNode;
                }
            }                        
        }       
    }
    
    return rv;
}


function isBubbleEvent(eventName)
{
    for(var i = 0; i < bubbleEvents.length; i++)
    {
        if(eventName === bubbleEvents[i])
        {
            return true;
        }
    }
    
    return false;
}

function isNonBubbleEvent(eventName)
{
    for(var i = 0; i < nonBubbleEvents.length; i++)
    {
        if(eventName === nonBubbleEvents[i])
        {
            return true;
        }
    }
    
    return false;
}


function isCustomEvent(eventName)
{
    for(var name in customEvents)
    {
        if(eventName == name)
        {
            return true;
        }
    }
                    
    return false;        
}


/****************************************************************************************
Start Event Interception Framework
****************************************************************************************/

function getOrCreateEventManager(domObject)
{
    var rv = null;
                
    for(var i = 0; i < eventManagers.length; i++)
    {
        if(eventManagers[i].isDomObject(domObject))
        {
            rv = eventManagers[i];
            break;
        }
    }
    
    if(rv == null)
    {
        rv = new eventManager(domObject);
        eventManagers[eventManagers.length] = rv;
    }
    
    return rv;                        
}        
       
eventHandler = function(domObject, eventName)
{   
    var _isdisposed = false;
    var _domObject = domObject;
    var _eventName = eventName;
    var _listeners = [];
    var _declarativeHandler = null;
      
    this.dispose = function()
    {
        if(_isdisposed == false)
        {
            _isdisposed = true;
            _domObject = null;
            _listeners = null;
            _eventName = null;
            _declarativeHandler = null;
        }
    }

    this.count = function()
    {
        return _listeners == null ? 0 : _listeners.length;
    }
    
    
    this.exists = function(listener)
    {
        for(var i = 0; i < _listeners.length; i++)
        {
            if(_listeners[i] == listener)
            {
                return true;
            }                                       
        }
        
        return false;                 
    }
    
    this.addListener = function(listener)
    {          
        if(this.exists(listener) == false)
        {
            _listeners[_listeners.length] = listener;            
        } 
         
    }
    
    this.removeListener = function(listener)
    {           
        for(var i = 0; i < _listeners.length; i++)
        {
            if(_listeners[i] == listener)
            {
                _listeners.splice(i);
                break;
            }                 
        }         
    }   
    
    this.capture = function(event)
    {
        _declarativeHandler = _domObject["on" + event.type];
        _domObject["on" + event.type] = null;
    }
    
    this.execute = function(event)
    {  
        try
        { 
            try
            {
                if(_declarativeHandler != null)
                {
                    try
                    {
                        _declarativeHandler.apply(_domObject, new Array(event));
                    }   
                    catch(ex)
                    {
                        throw ex;
                    }       
                }
            }
            finally
            {  
                try
                {
                    for(var i = _listeners.length - 1; i > -1; i--)
                    {
                        if(_listeners[i] != null)
                        {
                            _listeners[i](event);
                        }
                    }
                }
                finally
                {
                    if(_declarativeHandler != null)
                    {
                        
                        if(_domObject["on" + eventName] == null)
                        {
                            // only if it wasn't reassigned during the call
                            _domObject["on" + eventName] = _declarativeHandler;
                        }
                        
                        _declarativeHandler = null;   
                    }               
                }
            }                                                       
        }
        finally
        {
            if(isNonBubbleEvent(event.type) == true)
            {
                destroyNonBubbleEvent();
            }
        }
    }                                           
}

eventManager = function(domObject)
{
    var _isdisposed = false;
    var _handlers = {};
    var _domObject = domObject;

    this.dispose = function()
    {
        _isdisposed = true;
        _domObject = null;

        for (var handlerName in _handlers)
        {
            var handler = _handlers[handlerName];

            if (handler != null)
            {
                handler.dispose();
            }
        }

        _handlers = null;
    }

    this.count = function()
    {
        var rv = 0;

        if (_handlers != null)
        {
            for (var handler in _handlers)
            {
                rv++;
            }
        }

        return rv;
    }

    this.isDomObject = function(domObject)
    {
        return _domObject === domObject;
    }

    this.getOrCreateHandler = function(forEventName)
    {
        var rv = _handlers[forEventName];

        if (rv == null)
        {
            if (debugEnabled && debugHandlers)
            {
                sendDebug("<p>countHandlers=" + deepHandlerCount() + ": domObject=" + _domObject + ": eventName=" + forEventName + "</p>");
            }

            rv = new eventHandler(_domObject, forEventName);
            _handlers[forEventName] = rv;
        }

        return rv;
    }

    this.addEventListener = function(eventName, listener)
    {
        var handler = this.getOrCreateHandler(eventName);
        handler.addListener(listener);
    }

    this.removeEventListener = function(eventName, listener)
    {
        var handler = this.getOrCreateHandler(eventName);
        handler.removeListener(listener);
    }

    this.getExecutor = function(eventName)
    {
        var handler = this.getOrCreateHandler(eventName);
        return handler;
    }
}

/****************************************************************************************
End Event Interception Framework
****************************************************************************************/

function registerEventListener(domObject, eventName, functionReference)
{           
    domObject = dereferenceDomObject(domObject);  
         
    if(isCustomEvent(eventName))
    {
        if(document === domObject && customEvents[eventName] != null && customEvents[eventName].getAttribute("customevent") != null)
        {        
            domObject = customEvents[eventName];
            eventName = "click";
        }                              
    }    
        
    if(supportsW3CDomEvents)
    {                                            
        var em = getOrCreateEventManager(domObject);                
        em.addEventListener(eventName, functionReference);                          
    } 
    else
    {
        domObject.attachEvent("on" + eventName, functionReference);
    }
}

function unregisterEventListener(domObject, eventName, functionReference)
{   
    domObject = dereferenceDomObject(domObject);  
            
    if(isCustomEvent(eventName))
    {
        if(document === domObject && customEvents[eventName] != null && customEvents[eventName].getAttribute("customevent") != null)
        {
            domObject = customEvents[eventName];
            eventName = "click";
        }                              
    }    
        
    if(supportsW3CDomEvents)
    {    
        var em = getOrCreateEventManager(domObject);                
        em.removeEventListener(eventName, functionReference);                           
    } 
    else
    {
        domObject.detachEvent("on" + eventName, functionReference);
    }
}

function reregisterEventListener(domObject, eventName, functionReference, captureEnabled)
{
    unregisterEventListener(domObject, eventName, functionReference, captureEnabled);
    registerEventListener(domObject, eventName, functionReference, captureEnabled);    
}   
           
function setupEvent(event)
{
    totalCountEventsRaised++;

    if ( isNonProcessedNode(event.target) )
    {
        return;
    }

    if(debugEnabled && debugEvents)
    {
        sendDebug("maxStack=" + maxStack + ": stack=" + stack.length + ": " + new Date().toString() + ":" + event.type + ":" + event.target);       
    }
    
    event.__baseStopPropagation = event.stopPropagation;    

    event.stopPropagation = function()
    {    
        event.__baseStopPropagation();
        destroyEvent(event);
    }
    
    stack.push(event);
    maxStack = stack.length > maxStack ? stack.length : maxStack;
    
    if( typeof(modifyEvent) == "function")
    {
        modifyEvent(event);
    }
        
    window.event = event;   
              
    var em;           
    var executor;    
                    
    if(event.eventPhase == 1)
    {                      
        em = getOrCreateEventManager(event.target);
        executor = em.getExecutor(event.type);   
        executor.capture(event);             
        event.target.addEventListener(event.type, executor.execute, false);
    } 
    // the browsers are unable to capture on window or document because at the root of the object level you are already AT_TARGET (2)
    else if((event.eventPhase == 2 || event.eventPhase == 0) && (event.target == window || event.target == document))
    {               
        //document events are actually defined on the window hence the eventManager key is actually the window despite the fact that most browsers target document
        //what you will notice in the browsers is that even though they target the document the this keyword is the window because thats where the event was registered                      
        em = getOrCreateEventManager(window);
        executor = em.getExecutor(event.type);   
        executor.capture(event);          
        executor.execute(event);
    }          
}

function destroyEvent(event)
{
    if(stack.length == 0)
    {
        event = null;
    }        
    else
    {
        event = stack.pop();
    }
        
    window.event = event;                
} 

function destroyNonBubbleEvent(event)
{ 
    destroyEvent(event);              
} 

function setupNonBubbleEvent(event)
{   
    setupEvent(event);            
}
            
function createCustomEvent(eventName)
{
    var ele = document.createElement("A");
    ele.setAttribute("customevent", "customEvent");
    ele.id = eventName;
    customEvents[eventName] = ele;               
}  

function fireCustomEvent(eventName)
{
    customEvents[eventName].fireEvent("click");
}  
    
function eventFxDocumentLoaded(event)
{
    try
    {
        setupNonBubbleEvent(event);         
        document.readyState = "complete";
        
        if(typeof(onSimulatedReadyStateChange) == 'function')
        {        
            onSimulatedReadyStateChange(event);
        }

        if(typeof(onPreLoad) == 'function')
        {        
            onPreLoad(event);
        } 
                     
        fireCustomEvent("simulatedreadystatechange");
        fireCustomEvent("preload");                                     
    }
    catch(ex)
    {
        throw("eventFxDocumentLoaded: " + ex.message + " : " + document.location.href + ": Error= " + ex.message);
    }
    finally
    {
    }        
}

function eventFxDocumentDispose(event)
{
    try
    {
        stack = [];
        customEvents = [];
        
        for(var i = 0; i < eventManagers.length; i++)
        {
            var em = eventManagers[i];
            
            if(em != null)
            {
                em.dispose();
            }
        }
        
        eventManagers = [];
    }
    catch(ex)
    {       
        throw("eventFxDocumentDispose: " + ex.message);
    }            
}

function eventFxDOMNodeRemoved(event)
{
    var domObject = event.target;

    for (var i = 0; i < eventManagers.length; i++)
    {
        if (eventManagers[i].isDomObject(domObject))
        {
            var em = eventManagers[i];
            eventManagers.splice(i);        
            em.dispose();            
            break;
        }
    }
}


/****************************************************************
BEGIN - START EVENTING SYSTEM
****************************************************************/
if(downloaded_coreprototype_js == null && supportsW3CDomEvents)
{
    try
    {
        createCustomEvent("simulatedreadystatechange");
        createCustomEvent("preload");

        for(var i = 0; i < bubbleEvents.length; i++)
        {
            window.addEventListener(bubbleEvents[i], setupEvent, true); 
            window.addEventListener(bubbleEvents[i], destroyEvent, false); 
        } 
        
        for(var i = 0; i < nonBubbleEvents.length; i++)
        {
            window.addEventListener(nonBubbleEvents[i], setupNonBubbleEvent, true); 
        } 
        
        window.addEventListener("DOMContentLoaded", eventFxDocumentLoaded, true);    
        window.addEventListener("unload", eventFxDocumentDispose, false);   
        window.addEventListener("DOMNodeRemoved", eventFxDOMNodeRemoved, false);           
    } 
    catch(ex)
    {
        throw("Failed to initialize eventing system from document " + document.location.href + ". Error = " + ex.message);
    }        
}
/****************************************************************
END - START EVENTING SYSTEM
****************************************************************/

function modifyEvent(event)
{
    event.__defineGetter__("srcElement", function(){return event.target;});
    
    event.__stopPropagation = false;
    event.__defineGetter__("cancelBubble", function()
                                           {
                                               return this.__stopPropagation;
                                           });
    event.__defineSetter__("cancelBubble", function(val)
                                           {
                                               if (val == true && this.__stopPropagation == false)
                                               {
                                                   event.stopPropagation();
                                                   this.__stopPropagation = true;
                                               }
                                           });
    
    event.__returnValue = null;
    event.__defineGetter__("returnValue", function()
                                          {
                                              return this.__returnValue;
                                          });
    event.__defineSetter__("returnValue", function(val)
                                          {
                                              if (val == false && this.__returnValue != false)
                                              {
                                                  this.preventDefault();
                                              }
                                              this.__returnValue = val;
                                          });

//    event.__keyCode = event.which;
//    event.__defineGetter__("keyCode", function()
//                                      {
//                                          return this.__keyCode;
//                                      });
//    event.__defineSetter__("keyCode", function(val)
//                                      {
//                                          this.__keyCode = val;
//                                          if (val == 0)
//                                          {
//                                              // cancel the event
//                                              this.cancelBubble = true; // todo: does this work?
//                                          }
//                                          else
//                                          {
//                                              // todo: forced change of key press, can this be done? clear and fire new event?
//                                          }
//                                      });
}

if (!window.attachEvent)
{
    window.attachEvent = function(type, listener) 
    { 
        try
        {
            return window.addEventListener(type.substr(2), listener, false); 
        }
        catch(e)
        {
            throw("Cross Browser attachEvent (window) Prototype Error\r\n\r\n" + e.message);
        }
    } 
    if(typeof HTMLDocument!="undefined")    
    {
        HTMLDocument.prototype.attachEvent = function(type, listener) 
        { 
            try
            {
                return this.addEventListener(type.substr(2), listener, false); 
            }
            catch(e)
            {
                throw("Cross Browser attachEvent (HTMLDocument) Prototype Error\r\n\r\n" + e.message);
            }
        } 
    }
    if(typeof HTMLElement!="undefined")    
    {
        HTMLElement.prototype.attachEvent = function(type, listener) 
        { 
            try
            {
                return this.addEventListener(type.substr(2), listener, false); 
            }
            catch(e)
            {
                throw("Cross Browser attachEvent (HTMLElement) Prototype Error\r\n\r\n" + e.message);
            }
        } 
    }
}

var downloaded_coreeventmanager_js = new Date();
