/* ------------------------------------------------------------------------ *
 * SAS Institute, Inc.                                                      *
 *                                                                          *
 * Copyright (c) 2002 SAS Institute, Inc.  All rights reserved.             *
 *                                                                          *
 *                                                                          *
 * Contains methods that support event handling and registration across     *
 * browsers (IE 5.5+ & NS 6.2+ ).                                           *
 *                                                                          *
 *                                                                          *
 * Dependencies: none                                                       *
 *                                                                          *
 *                                                                          *
 * @author Chaunston Avery (chaver)                                         *
 * ------------------------------------------------------------------------ */



//Cross browser event handler registration method
//
//@parm element - the object on which to register the handler
//@parm eventType - the event type which the handler is registered to handle. Types
//      should not include the 'on' prefix (e.g. specify 'click' rather than 'onclick').
//@parm listener - the function or object to register as the event handler. If listener is an
//      object it should have a 'handleEvent' method.
//@parm captures - specifies whether the handler should capture any events it handles
//
function registerEventHandler ( element, eventType, listener, captures )
{
    var listenerFunc = null;
    var msg = null;
    var retval = true;

    if( typeof listener == "function" ) {
        listenerFunc = listener;
    }
    else if( typeof listener != "function" && listener.handleEvent != null ) {
        listenerFunc = function () { listener.handleEvent(event); };
    }
    else {
        msg = 'registerEventHandler could not register object' + listener;
        retval = false;
    }

    if (element == null) {
    } else if( element.addEventListener != null && retval ) {
        //Dom 2 way
        //alert(" registering event of type " + eventType + " using addEventListener");
        element.addEventListener( eventType, listenerFunc, captures );
    }
    else if( element.attachEvent != null && retval ) {
        //proprietary IE way
        retval = element.attachEvent( "on" + eventType, listenerFunc );
        msg = retval ? msg : "Failed to register event using attachEvent";
        //if( retval ) { alert("successful registration of on" + eventType + " handler "); }
    }
    else {
        msg = "ERROR: Failed to locate native event handler registration function!";
        msg += "\nregisterEventHandler("+element+", "+eventType+", "+listener+", "+captures+")";
    }

    if( msg != null ) {
        alert(msg);
    }

    return retval;

}

//Cross browser method to remove event handlers from an object
//
//@parm element - the object on which to the handler was registered
//@parm eventType - the event type which the handler is registered to handle. Types
//      should not include the 'on' prefix (e.g. specify 'click' rather than 'onclick').
//@parm listener - the function to remove as an event handler.
//@parm captures - true if a capturing event handler is to be removed
//
//NOTE: THIS METHOD HAS NOT BEEN TESTED
//
function unRegisterEventHandler ( element, eventType, listener, captures )
{
    var listenerFunc = null;
    var msg = null;
    var retval = true;

    if( typeof listener == "function" ) {
        listenerFunc = listener;
    }
    else {
        msg = 'unRegisterEventHandler could not unregister listener' + listener;
        alert( msg );
        retval = false;
        return retval;
    }

    if( element.removeEventListener != null && retval ) {
        //Dom 2 way
        //alert(" unregistering handler of type " + eventType + " using removeEventListener");
        element.removeEventListener( eventType, listenerFunc, captures );
    }
    else if( element.detachEvent != null && retval ) {
        //proprietary IE way
        //alert(" unregistering handler of type " + eventType + " using detachEvent");
        element.detachEvent( "on" + eventType, listenerFunc );
    }
    else {
        msg = "ERROR: Failed to locate native event handler unregistration function!";
        msg += "\nunregisterEventHandler("+element+", "+eventType+", "+listener+", "+captures+")";
    }

    if( msg != null ) {
        alert(msg);
    }

    return retval;

}

//Cross browser event dispatcher. Causes all handlers registered on the element for the particular
//event type to be invoked.
//
//@parm eventTarget - the object whose handlers will receive the dispatched event
//@parm eventType - the event type which the handler is registered to handle. Types
//      should not include the 'on' prefix (e.g. use specify 'click' rather than 'onclick').
//@parm listener - the function or object to register as the event handler. If listener is an
//      it should have a 'handleEvent' method.
//@parm captures - specifies whether the handler should capture any events it handles
//

eventNotify.eventFactory = new cwEventFactory();
function eventNotify ( eventTarget, eventType, isBubbleAllowed, cancelable )
{
    var evt = eventNotify.eventFactory.createEvent(eventTarget, eventType, isBubbleAllowed, cancelable);
    eventNotify_lw( eventTarget, evt );
}

// lightweight version of eventNotify that takes a pre-constructed event and
// dispatches to event handlers registered on eventTarget
//
//@parm eventTarget - the object on whose handlers will receive the dispatched event
//@parm evt - the event to dispatch. The evt must have been created using cwEventFactory's createEvent() method
function eventNotify_lw ( eventTarget, evt, isCustomEvent )
{

    if( eventTarget.dispatchEvent != null )
    {
        //Dom 2 way
           //alert("begin event dispatch");
           //eventTarget.dispatchEvent(evt);
        var retCode = eventTarget.dispatchEvent(evt);
           //alert("end event dispatch");
    }
    else if( eventTarget.fireEvent != null )
    {
        //proprietary IE way
           //alert("begin event dispatch");

        if( !isCustomEvent )
        {
            evt.type = "on" + evt.type;
        }
            //alert("dispatching event of type: " + evt.type);
        eventTarget.fireEvent( evt.type, evt );
            //alert("end event dispatch");
    }
    else
    {
        alert("Unable to fire event of type: " + evt.type + " for object " + eventTarget );
    }
}

// -------------------------------------------------------------------
// cwEventFactory methods
// ----------------------------------------------------------------
cwEventFactory.prototype.createEvent = function (eventTarget, eventType, bubblingAllowed, cancelable, eventModule)
{
    var evt = null;
    var eModule = (eventModule != null) ? eventModule : "HTMLEvents";
    var isBubblingAllowed = (bubblingAllowed != null) ? bubblingAllowed : true;
    var isCancelable = (cancelable != null) ? cancelable : false;

    if( document.createEvent != null ) {
        //Dom 2 way
        evt = document.createEvent(eModule);
        evt.initEvent(eventType, isBubblingAllowed, isCancelable );
        evt.sas_target = eventTarget;
    }
    else if( document.createEventObject != null ) {
        evt = document.createEventObject();
        evt.type = eventType;
        evt.srcElement = eventTarget;
        evt.cancelBubble = !isBubblingAllowed;
        evt.sas_target = eventTarget;
    }
    else {
        alert("ERROR: EventFactory could not create event of type: " + eventType + " using native event creation mechanism" );
    }

    return evt;
}

function cwEventFactory()
{
   //return this;
}

//Function which when assigned as an object method, allows that object to register
//listeners who wish to respond to specific types of events dispatched from the object.
//This function is useful for custom javascript objects which are not HTML elements and
//do not have any pre-defined addEventListener method.
//
//@parm eventType - the event type for which the listener is registered to handle
//@parm listener - the object on which will respond to events of the given type from this object
//
// Example: see cwDispatchEvent
//
function cwAddEventListener( eventType, listener )
{
    //alert("Entering cwAddEventListener. Event type = " + eventType );
    if( this.eventTypes == null ) {
        //create the array of listener types
        this.eventTypes = new Array();
    }

    if( this.eventTypes[eventType] == null ) {
        //create the array for the specified listener type
        this.eventTypes[eventType] = new Array();
    }

    var listeners = this.eventTypes[eventType];
    if( !isObjInArray(listener, listeners) ) {
        listeners[listeners.length] = listener;
        return true;
    }
    else {
        return false;
    }



}

//Function which when assigned as an object method, allows that object to remove registered
//event listeners.  This function is useful for custom javascript objects which are not
//HTML elements and do not have any pre-defined addEventListener method.
//
//@parm eventType - the event type for which the listener is registered to handle
//@parm listener - the object which will be removed so that it can no respond to events of
//  type eventType from this object.
//
function cwRemoveEventListener( eventType, listener )
{
    //alert("Entering cwRemoveEventListener");
    if( this.eventTypes == null || this.eventTypes[eventType] == null ) {
        return false;
    }

    var listeners = this.eventTypes[eventType];
    for( var i=listeners.length; i>=0; i-- ) {
        if( listeners[i] == listener ) {
            listeners.splice(i, 1); // remove the listener from the list & ensure the list remains contiguous
            return true;
        }
    }

    return false;
}

//Function which when assigned as an object method, allows the object to dispatch events to
//registered event listeners.  This function is useful for custom javascript objects which
//are not HTML elements and do not have any pre-defined dispatchEvent method.
//
//@parm event - the event to dispatch
//
// Example:
//    var myTree = new Tree();
//    myTree.addEventListener = cwAddEventListener;
//    myTree.removeEventListener = cwRemoveEventListener;
//    myTree.dispatchEvent = cwDispatchEvent;
//    registerEventHandler ( myTree,  "TREE_SELECTION", mvRightStateHandler, true );
//
//    window.treeClick = function(nodeId, classPrefix) {
//
//         var node = arrayOfObjects[nodeID];
//         var tree = node.tree;
//         ...
//
//         //assign focus to newly selected node
//         node.navObj.getElementsByTagName("a")[0].focus();
//         eventNotify( tree             /* object on which event occurred */,
//                      "TREE_SELECTION" /* type of event that occurred */,
//                      false            /* event shouldn't bubble since it is specific to the tree */
//                    );
//    }
//
function cwDispatchEvent( event )
{
    //alert("Entering cwDispatchEvent. Event type = " + event.type );
    if( event.type == null ) {
       var exc = new EventException();
       exc.code = EventException.UNSPECIFIED_EVENT_TYPE_ERROR;
       throw exc;
    }

    var bListsExist = (this.eventTypes != null) && (this.eventTypes[event.type] != null);
    if( !bListsExist ) {
        return; // no registered listeners to dispatch to
    }

    var listeners = this.eventTypes[event.type];
    for( var i=0; i<listeners.length; i++ ) {
        //alert("dispatching event to handler(" + i + ")" );
        listeners[i](event); //execute the handler
    }

    //alert("Exiting cwDispatchEvent..." );
}

function isObjInArray( obj, array )
{
    var bFound = false;
    for( var i=0; i<array.length; i++ ) {
        if( obj == array[i] ) {
            bFound = true;
            break;
        }
    }
    return bFound;
}

//------------------------------------------------------------------------------
// Returns true if the event parameter is a paste event, false otherwise.
//------------------------------------------------------------------------------
isPasteEvent.prototype.PASTE_CODE = 24; //CTRL-V, 0x18 = hex
isPasteEvent.prototype.CHAR_v_CODE = 118; //'v', 0x76 = hex
function isPasteEvent(event)
{
    var keyCode = ( event.which != null ) ? event.which : event.keyCode;
    return ( event.type == "paste"
             || keyCode == this.PASTE_CODE
             || (keyCode == this.CHAR_v_CODE && event.ctrlKey != null && event.ctrlKey) );
}

//------------------------------------------------------------------------------
// Returns true if the event parameter is a cut event, false otherwise.
//------------------------------------------------------------------------------
isPasteEvent.prototype.CUT_CODE = 22; //CTRL-V, 0x16 = hex
isPasteEvent.prototype.CHAR_c_CODE = 120; //'c', 0x78 = hex
function isCutEvent(event)
{
    var keyCode = ( event.which != null ) ? event.which : event.keyCode;
    return ( event.type == "cut"
             || keyCode == this.CUT_CODE
             || (keyCode == this.CHAR_c_CODE && event.ctrlKey != null && event.ctrlKey) );
}

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// _BEGIN CLASS DEF'N FOR WRSEventUtil
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
function WRSEventUtil() {}

WRSEventUtil.preventDefaultBehavior = function(evt)
{
    if( evt.preventDefault != null )
    {   //dom method
        evt.preventDefault();
    }
    evt.returnValue = false; //ie event model
}
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// _END CLASS DEF'N FOR WRSEventUtil
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
