/**
 * Direct Web Remoting (DWR)
 * engine.js
 */

 
/**
 * Declare a constructor function to which we can add real functions.
 * @constructor
 */
function DWREngine()
{
}
DWREngine._httpMethod = "GET"; //default is post

/*
 * The constants to pick the remoting method
 */
DWREngine.XMLHttpRequest = 1;
DWREngine.IFrame = 2;
DWREngine.showDebug = false; //default is false
DWREngine.showDump = false; //default is false

/*
 * A function to be called before requests are marshalled. Can be null.
 * @private
 */
DWREngine._preHook = null;

/**
 * A function to be called after replies are received. Can be null.
 * @private
 */
DWREngine._postHook = null;

/**
 * A function to be called if the server doesn't respond, the status code is different than 200 or an error while trying to evaluate the returned object. Can be null.
 * @private
 */
DWREngine._failOver = null;


/**
 * A map of all the known current calls
 * @private
 */
DWREngine._calls = new Object();


DWREngine.callStack = new Array();
DWREngine.reloadCalls = new Array();
DWREngine.reloadCallBack = new Array();

/**
 * What is the default remoting method
 * @private
 */
DWREngine._method = DWREngine.XMLHttpRequest;

/**
 * When we are dreaming up variable names, this is a number that we use
 * as a basis to ensure uniqueness.
 * WARNING: if it is possible for execute to be called again before
 * marshalling has finished on the first then this will break, but I
 * think that is not possible.
 * @private
 */
DWREngine._paramCount = 0;

/**
 * The base URL that we use to contact the server. This is dependent on the
 * server on which DWR is deployed so it is dynamic.
 * Warning: if you can see EL-like (${...}) expressions on the next line, don't
 * be fooled into thinking it is EL. Check the DWRServlet source for the hack.
 * Maybe we will do something more EL like in the future.
 * If you can't see the ${...} then you are probably looking at the processed
 * source
 * @private
 *
 * Jalpino - This was set to blank, the specfied URL will be supplied when the ajax request
 *		   - is executed (via .Execute() )
 */
DWREngine._urlBase = '';

/**
 * The error handler function
 * @param handler A function to call with single an error parameter on failure
 */
DWREngine.setErrorHandler = function(handler)
{
    DWREngine._errorHandler = handler;
}

/**
 * The Pre-Hook is called before any DWR remoting is done.
 * Pre hooks can be useful for displaying "please wait" messages.
 * @param handler A function to call with no params before remoting
 * @see DWREngine.setPostHook
 */
DWREngine.setPreHook = function(handler)
{
    DWREngine._preHook = handler;
}

/**
 * The Post-Hook is called after any DWR remoting is done.
 * Pre hooks can be useful for removing "please wait" messages.
 * @param handler A function to call with no params after remoting
 * @see DWREngine.setPreHook
 */
DWREngine.setPostHook = function(handler)
{
    DWREngine._postHook = handler;
}

/**
 * The FailOver-Hook is called if a failed request was made to the server
 * and when a status code different than 200 is returned.
 * @param handler A function to call with no params if failure from remoting
 * @see DWREngine.setFailOver
 */
DWREngine.setFailOver = function(handler)
{
    DWREngine._failOver = handler;
}

/**
 * Set the preferred remoting method.
 * setMethod does not guarantee that the selected method will be used, just that
 * we will try that method first.
 * @param newmethod One of DWREngine.XMLHttpRequest or DWREngine.IFrame
 */
DWREngine.setMethod = function(newmethod)
{
    if (newmethod != DWREngine.XMLHttpRequest && newmethod != DWREngine.IFrame)
    {
        alert("Remoting method must be one of DWREngine.XMLHttpRequest or DWREngine.IFrame");
        throw newmethod;
    }

    DWREngine._method = newmethod;
}

/**
 * A function to call if something fails.
 * @private
 */
DWREngine._errorHandler = function(data)
{
    if (typeof data == "object" && data.name == "Error" && data.description)
    {
        alert("Error: " + data.description);
		alert(data);
		for(k in data)
			alert(k);
		
    }
    else
    {
        alert("Error Occured: " + data);
    }
}

/**
 * Called when the replies are received.
 * This method is called by Javascript that is emitted by server
 * @private
 */
DWREngine.handleResponse = function(id, reply)
{
    var call = DWREngine._calls[id];
    if (call == null)
    {
        var known = "";
        for (call in DWREngine._calls)
        {
            known += call + "\n";
        }
        if( DWREngine.showDebug ){
			alert("Internal Error: Call with id='"+id+"' unknown.\nI do know about the following:\n"+known);
        }
		return;
    }
    
	// C.Layton, this is commented out so we can check current 
	// calls and kill them if we need to clear the connection.
	//DWREngine._calls[id] = undefined;

    if (call.iframe != null)
    {
        call.iframe.parentNode.removeChild(call.iframe);
    }

    if (DWREngine._postHook != null)
    {
        DWREngine._postHook();
    }

    if (call.callback == null)
    {
        if (reply != null)
        {
            alert("Missing callback for reply "+reply);
        }
    }
    else
    {
        // Error handlers inside here indicate an error that is nothing to do
        // with DWR so we handle them differently.
        try
        {
            call.callback(reply);
        }
        catch (ex)
        {
            DWREngine._errorHandler(ex);
        }
    }
}

/**
 * Called when errors are received.
 * This method is called by Javascript that is emitted by server
 * @private
 */
DWREngine.handleError = function(id, reason)
{
    var call = DWREngine._calls[id];
    if (call != null)
    {
        DWREngine._calls[id] = undefined;

        if (call.iframe != null)
        {
            call.iframe.parentNode.removeChild(call.iframe);
        }
    }
    else
    {
        // Things are going wrong so alerting probably does not make sense
        // alert("Internal Error: Call with id="+id+" unknown.");
    }

    if (DWREngine._postHook != null)
    {
        DWREngine._postHook();
    }

    DWREngine._errorHandler(reason);
}

// This function was created to try and clear
// ajax calls on the CHP as users try to navigate away
// prior to the CHP finishing loading.  This problem
// was evident in IE browsers.
DWREngine.clearConnections = function (location) {
		// loop through the ajax calls for this request
		for(var i=0;i<DWREngine.callStack.length;i++){
     		// if the request still has a ready state of loading
			// kill the request and capture information about it in case
			// we need to try and reload it later.
			if(DWREngine._calls[DWREngine.callStack[i]].req.readyState == 1){
				DWREngine._calls[DWREngine.callStack[i]].req.abort();
				DWREngine.reloadCalls.push(DWREngine._calls[DWREngine.callStack[i]].url);
				DWREngine.reloadCallBack.push(DWREngine._calls[DWREngine.callStack[i]].callback);
			}
     	}
		
		if (DWREngine.reloadCalls.length) { 
			// here we want to try and reload the ajax calls for certain windows
			// that we may close but still remain on the home page.
			// examples are opening help, opening c/b explorer
			// mail links from the contacts modules, etc.
			if (location.target != "" || location.href.indexOf("modalShowWindow",0) > 0 || location.href.indexOf("openHelp()",0) > 0 || location.protocol == "mailto:" || location.className == "moduleDelete") {
				
				// pause was placed here to allow modal window to popup
				window.setTimeout('reloadCalls()', 500);
			}
		}
}

// Loop through ajax calls and recall them
function reloadCalls(){
	for(var i=0;i<DWREngine.reloadCalls.length;i++){
		DWREngine.execute(DWREngine.reloadCallBack[i], DWREngine.reloadCalls[i]);
	}
	
	DWREngine.reloadCalls = new Array();
	DWREngine.reloadCallBack = new Array();
}
     
/**
 * Send a request to the server
 * This method is called by Javascript that is emitted by server
 * @private
 * This method was modified by jalpino to work inconjuction with FuseBox3 for CF
 */
DWREngine.execute = function(func, requestTarget )
{
	//Validate that the callback function is infact a function
    if (func != null && typeof func != "function" && typeof func != "object")
    {
        alert("Supplied callback function is neither null nor a function: "+func);
        throw func;
    }

    var call = new Object();

    // Get a unique ID for this call
    call.id = getUUID();

	//Keep track of the this request
    DWREngine._calls[call.id] = call;
	DWREngine.callStack.push(call.id);
	
	//Set the prehook if there is one
    if (DWREngine._preHook != null)
    {
        DWREngine._preHook();
    }

    call.callback = func;

    // Build a map containing all the values to pass to the server.
    DWREngine._paramCount = 0;

    call.map = new Object();
	call.url = requestTarget +'&ajaxRequestID='+ call.id;
	
    // Get setup for XMLHttpRequest if possible
    if (DWREngine._method == DWREngine.XMLHttpRequest)
    {
        if (window.XMLHttpRequest)
        {
            call.req = new XMLHttpRequest();
        }
        else if (window.ActiveXObject)
        {
			// - jalpino 05/26/2005
			try{
				call.req = new ActiveXObject("Msxml2.XMLHTTP");
			}catch( e ){
				try{
					call.req = new ActiveXObject("Microsoft.XMLHTTP");
				}catch( E ){
					//do nothing
				}
			}							
            if (!call.req)
            {
                alert("Creation of Microsoft.XMLHTTP failed. Reverting to iframe method.");
			}
        }
    }

    if (call.req)
    {
        call.map.xml = true;
        
        // Proceed using XMLHttpRequest
        call.req.onreadystatechange = function() { DWREngine._stateChange(call); };
		if (DWREngine._httpMethod == "GET") 
		{
	        call.req.open("GET", call.url, true);
			call.req.send(null);
		} 
		else 
		{
			var query = 'ajaxRequestID='+ call.id +'&'+ (arguments[2] ? arguments[2] : '') ;
	        call.req.open("POST", call.url, true);
			call.req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
			call.req.setRequestHeader("Content-Length",query.length);
			call.req.send(query);
			
		}
    }
    else
    {
        call.map.xml = false;

        // Proceed using iframe
        call.iframe = document.createElement('iframe');
        call.iframe.setAttribute('id', 'dwr-iframe');
        call.iframe.setAttribute('style', 'width:0px; height:0px; border:0px;');
        call.iframe.setAttribute('src', call.url);
        document.body.appendChild(call.iframe);
    }
}


/**
 * Called by XMLHttpRequest to indicate that something has happened
 * @private
 */
DWREngine._stateChange = function( call ){

    if (call.req.readyState == 4){
		var stat;
		var ex;
		
		try {
			stat = call.req.status
		}
		catch (ex) {
			stat = -99999;
		}
        if (stat){

            try{

                // If we received an OK status from the server, evaluate our returned object
                if (stat == 200){

					//Execute the returned content from the server
					eval(trim(call.req.responseText));

                } else {

                        //A status code of something other than '200' was returned.
                        //This would be a good place to perform additional tasks for specific status codes 
						//Display debug output
						if( DWREngine.showDebug ){
							alert('The Ajax request returned a status code of:\n\t"'+ stat +'"');
	                        DWREngine.handleError(call.id,ex);
		               	}
	
					//Execute the failover function if specified
					if (DWREngine._failOver != null){
				        DWREngine._failOver();
    				}
               }

				//Dump the returned content to the browser
				if( DWREngine.showDebug || DWREngine.showDump){
					displayDebugConsole(trim(call.req.responseText));
				}
          }
          catch (ex){

                //An Error has occured while trying to evaulate our returned object
				//Display debug output
				if( DWREngine.showDebug ){
					alert('The Ajax request returned a status code of:\n\t"'+ stat +'"');
		            DWREngine.handleError(call.id, "The server did not return an object or your call back method is broken.\n"+ex);
		            displayDebugConsole(trim(call.req.responseText));				
				}
				
				//Execute the failover function if specified
				if (DWREngine._failOver != null){
			        DWREngine._failOver();
   				}	
		                      
           }
        }
        else{

	        //We did not receive a response from the server
			//Display debug output
			if( DWREngine.showDebug ){
				alert('The Ajax request returned a status code of:\n\t"'+ stat +'"');
	            DWREngine.handleError(call.id, "No response from remote server.");
	            displayDebugConsole(trim(call.req.responseText));
			}
	
			//Execute the failover function if specified
			if (DWREngine._failOver != null){
		        DWREngine._failOver();
  			}	
            
        }
    }
}


/**
 * displayDebugConsole( debugText )
 * This function builds a display console for debug and dumping of text to the screen
 * This function should be part of a helper class
 * @public
 */ 
function displayDebugConsole( debugText ){
	//Build the debug window
	var debugID = getUUID();
	var debugHTML = "<span style='font:10pt verdana'><b>Ajax Response Dump</b><br><span style='font:8pt verdana'>Remove '<i>DWREngine.showDebug = true</i>' and/or '<i>DWREngine.showDump = true</i>' from your code to hide this dump.</span><br><hr><div style='width:100%;height:350px;overflow:auto;background-color:white;border:1px inset silver'>"+ trim(debugText).replace(/;/g,';br>') +"</div>";
		debugHTML += "<div align='right'><a href='#' onClick='document.getElementById(\""+ debugID +"\").style.display=\"none\";'>Close (X)</a></div>";
	var debugDiv = document.createElement('div');
	    debugDiv.id = debugID;
		debugDiv.style.backgroundColor='aliceblue';
		debugDiv.style.border = '2px outset midnightblue';
		debugDiv.style.position = 'absolute';
		debugDiv.style.top = '80px';
		debugDiv.style.left = '10px';
		debugDiv.style.width = '650px';
		debugDiv.style.padding = '5px';
		debugDiv.style.zIndex='100';
		debugDiv.innerHTML = debugHTML;
	//Write the console to the screen and assume focus
	document.body.appendChild(debugDiv);
}

/**
 * getUUID()
 * This function returns a unique identifier
 * This function should be part of a helper class
 * @public
 */
function getUUID(){
	var random = Math.floor(Math.random() * 10001);
    return (random + "_" + new Date().getTime()).toString();
}

/**
 * trim()
 * This function trims whitespace from the begining and ending of a string
 * This function should be part of a helper class
 * @public 
 */
function trim( str ){
	if(arguments[0] && str.length){
		var wsb = /^(\s)*/;
		var wse = /(\s)*$/;
		str = str.replace(wsb,'');
		return str.length ? str.replace(wse,'') : str;			
	}else
		return '';
}


