	//create the fuzzAsync object
	fuzzAsync = new Object();
	
	// create the que for calling async actions
	fuzzAsync.actionQueue = new Array();
	
	// load the transparent bg image
	fuzzAsync.fuzzAsyncTransBgImg = new Image(20,20);
	fuzzAsync.fuzzAsyncTransBgImg.src = fuzzFrameworkBaseURL + "/images/white-trans-bg.png";

	
	// Async wrapper functions
	// development should use these functions
	
	// use this function to call an action as defined in cbAsyncMappings.
	// return value is based on the mapping
	// methodArgsStruct must contain all the args that are required in the method mapped to the called action
	/////////////
	// RETURN
	// If the return type, as defined in the action is html
	// Then html will be returned
	// If the return type is "OBJECT"
	// then the return type will be some kind of javascript struct
	// for objects the return struct is isError, errorDescription, and data
	// the data field is the data returned from the function, and can be any standard
	// object... struct, array, or whatever
	////////////
	// NOTE Component Functions must be defined, this will not work for dynamic stuff like dbo's
	// but this is ok, because you would have to instantiate a dbo to do this anyway
	// just do it in the method that is being called, and it is fine
	fuzzAsync.callAction = function callAction(control, action, methodArgsArray, callbackFunction, callbackDataStruct){
		var tStruct = new Object();
		var jsonStruct = "";
		var asyncHandlerURL = "fuzzAsyncHandler.php?method=doAsyncFetch";
		
		tStruct.control = control;
		tStruct.action = action;
		tStruct.methodArgs = methodArgsArray;	
		
		// callbackDataStruct is the data that will be returned to the callback incase it is needed
		if(callbackDataStruct) tStruct.callbackData = callbackDataStruct;
		else tStruct.callbackData = "";
		
		// create json
		jsonStruct = JSON.stringify(tStruct);
		
		tAction = new Object();
		tAction.jsonStruct = jsonStruct;
		tAction.asyncHandlerURL = asyncHandlerURL;
		tAction.callbackFunction = callbackFunction;

		// add current action to the queue
		fuzzAsync.actionQueue.push(tAction);
		
		// see if we should call the action, or wait for a response from another action in the que
		if(fuzzAsync.actionQueue.length == 1){
			// Send it, cross your fingers...
			fuzzAsync.sendAsyncJSRequest(jsonStruct,asyncHandlerURL,callbackFunction);
		}
	}
	
	fuzzAsync.handleActionResponse = function handleActionResponse(res, callbackFunction){
		var ret = "";
		
		try{
			// attempt to continue the action queue
			fuzzAsync.handleActionQueue();
			ret = JSON.parse(res);
		}
		catch(e){
			ret = new Array()
			ret["isError"] = true;
			ret["errorDescription"] = e.message;
			ret["errorFile"] = e.fileName;
			ret["errorTrace"] = e.lineNumber;
			ret["response"] = res;
		}
		
		callbackFunction(ret);
	}
	
	fuzzAsync.handleActionQueue = function handleActionResponse(){
		// if this is being called, we know that the first index was just completed
		// remove it and move on
		fuzzAsync.actionQueue.shift();
		if(fuzzAsync.actionQueue.length > 0){
			// call the next action
			fuzzAsync.sendAsyncJSRequest(fuzzAsync.actionQueue[0].jsonStruct,fuzzAsync.actionQueue[0].asyncHandlerURL,fuzzAsync.actionQueue[0].callbackFunction);
		}
	}
	
	
	fuzzAsync.toggleErrorDetails = function toggleErrorDetails(cbAsyncContainerName){
		var detailsDiv = document.getElementById("cbAsyncContainer_"+cbAsyncContainerName+"_error_details_div_id");
		var errorDiv = document.getElementById("cbAsyncContainer_"+cbAsyncContainerName+"_error_div_id");
		
		if(isHiddenElement(detailsDiv)) errorDiv.style.height = "";
		else errorDiv.style.height = "200px";
		toggleElement(detailsDiv);
	}
	

	
	fuzzAsync.displayErrorDiv = function displayErrorDiv(cbAsyncContainerName, errorDescription, trace, errorDetails){
		var errorDiv = document.getElementById("cbAsyncContainer_"+cbAsyncContainerName+"_error_div_id");
		var containerDiv = document.getElementById("cbAsyncContainer_"+cbAsyncContainerName+"_div_id");
		var waitDiv = document.getElementById("cbAsyncContainer_"+cbAsyncContainerName+"_wait_div_id");
		var textDiv = document.getElementById("cbAsyncContainer_"+cbAsyncContainerName+"_error_text_div_id");
		var descriptionDiv = document.getElementById("cbAsyncContainer_"+cbAsyncContainerName+"_error_description_div_id");
		var detailsDiv = document.getElementById("cbAsyncContainer_"+cbAsyncContainerName+"_error_details_div_id");
		
		var text = 'Sorry, a system error has occured while trying to process your request.';
		// set the text
		textDiv.innerHTML = text;
		descriptionDiv.innerHTML = errorDescription;
		if(isHiddenElement(containerDiv)) fuzzAsync.util.resizeProgressDiv(errorDiv,waitDiv);
		else fuzzAsync.util.resizeProgressDiv(errorDiv,containerDiv);
		
		// create the error details
		var edTbl = fuzzAsync.util.createErrorDetailsTable(errorDescription, trace, errorDetails);
		// Put it in the div
		detailsDiv.innerHTML = edTbl;
		
		hideElement(waitDiv);
		hideElement(containerDiv);
		showElement(errorDiv);
	}
	
		
	// declare standard ajax methods
	// these objects will be put in a wrapper
	// do not use these for development
	fuzzAsync.getAsyncJSObject = function getAsyncJSObject(){
		var xmlHttp;
		if (typeof netscape != 'undefined' && typeof netscape.security != 'undefined') {
			try {
				netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');
			}
			catch (e){}
		}
		try{
			// Firefox, Opera 8.0+, Safari
			xmlHttp=new XMLHttpRequest();
		}
		catch (e){
			// Internet Explorer
			try{
				xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
			}
			catch (e){
				try{
					xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
				}
				catch (e){
					alert("Your browser does not support AJAX!");
				return false;
				}
			}
		}
		return xmlHttp;
	}
					
	fuzzAsync.sendAsyncJSRequest = function sendAsyncJSRequest(jsonObject, URL, callbackFunction){					
		xmlHttp = fuzzAsync.getAsyncJSObject();
		var handlerFn = fuzzAsync.handleActionResponse;

		if(xmlHttp){
			xmlHttp.onreadystatechange=function(){
				if(xmlHttp.readyState==4){
				//	dump(xmlHttp.responseText);
					handlerFn(xmlHttp.responseText,callbackFunction);
				}
		  	}
		  				
		  	// Open a connection to the server
			xmlHttp.open("POST", URL, true);
			// Tell the server you're sending it XML
			xmlHttp.setRequestHeader("Content-Type", "text/html");
		    xmlHttp.send(jsonObject);
	    }
	}
	
	// General Async Related Utils
	fuzzAsync.util =  new Object();
	fuzzAsync.util.replaceDiv = function replaceDiv(divElement, nHTML){
		//var tDiv = document.createElement('div');
		//tDiv.innerHtml = nHTML;
		divElement.innerHTML = nHTML;
		//divElement.appendChild(tDiv);
	}
	

	
	// Util functions
	
	fuzzAsync.util.createErrorDetailsTable = function createErrorDetailsTable(errorDescription, trace, errorDetails){
		var tableStr = "";
		tableStr += "<table cellpadding=2 cellspacing=0 width='80%'>";
		
		// Description
		tableStr += "<tr>";tableStr += "<td valign='top' align='left'>";
		tableStr += "ERROR: " + errorDescription;
		if(errorDetails != '') tableStr += "<br />Details: " + errorDetails;
		tableStr += "</td>";tableStr += "</tr>";
		
		// Other stuff
		for(i = 0; i < trace.length; i++){
			//showLine = true;
			//if(i == 0 && errorDetails.length > 1){
			//	if(errorDetails[0].ID == 'CFTHROW') showLine = false;
			//}
			//if(showLine){
				tableStr += "<tr>";tableStr += "<td valign='top' align='left'>";
				tableStr += trace[i].TEMPLATE + "; Line: "+trace[i].LINE;
				tableStr += "</td>";tableStr += "</tr>";
			//}
		}
		
		tableStr += "</table>";
		
		return(tableStr)
	}
	
	fuzzAsync.util.replaceDiv = function replaceDiv(divElement, nHTML){
		//var tDiv = document.createElement('div');
		//tDiv.innerHtml = nHTML;
		divElement.innerHTML = nHTML;
		//divElement.appendChild(tDiv);
	}
	
	fuzzAsync.util.hideProgressDiv = function hideProgressDiv(prefix){
		var progressDiv = document.getElementById(prefix+"_wait_div_id");

		hideElement(progressDiv);
	}

	fuzzAsync.util.displayProgressDiv = function displayProgressDiv(prefix, text, gridName, startRow, endRow,waitDivContainerId){
		var progressDiv = document.getElementById(prefix+"_wait_div_id");
		var textContainerDiv = document.getElementById(prefix+"_wait_text_container_div_id");
		var smallContainerDiv = document.getElementById(prefix+"_wait_text_container_small_div_id");
		var textDiv = document.getElementById(prefix+"_wait_text_div_id");
		var imgDiv = document.getElementById(prefix+"_wait_image_div_id");

		isRows = false;
		if(gridName && startRow){

			isRows = true;
			// this is a grid
			if(! endRow) endRow = startRow;
			else if(endRow < startRow){
				tRow = startRow;
				startRow = endRow;
				endRow = tRow;
			}

			// get the table
			var tGridTable = document.getElementById("fuzzDataGrid_"+gridName+"_table");
			if(! tGridTable) throw("displayProgressDiv: Unable to find table: "+gridName);
			if(! tGridTable.rows[startRow]) throw("displayProgressDiv: Unable to find startRow: "+gridName);
			if(startRow != endRow && ! tGridTable.rows[endRow]) throw("displayProgressDiv: Unable to find endRow: "+gridName);

			sRowElement = tGridTable.rows[startRow];
			xyPos = YAHOO.util.Dom.getXY(sRowElement);
			cWidth = sRowElement.offsetWidth;
			cHeight = sRowElement.offsetHeight;

			if(startRow != endRow){
				eRowElement = tGridTable.rows[endRow];
				eXY =  YAHOO.util.Dom.getXY(eRowElement);
				cHeight = eXY[1] - xyPos[1] + eRowElement.offsetHeight;
			}

			//fuzzUtils.setOpacity(progressDiv,40);
		}
		else{
			
			containerDiv = "";
			if(waitDivContainerId){
				containerDiv =  document.getElementById(waitDivContainerId);
			}
			else containerDiv = document.getElementById(prefix+"_div_id");

			xyPos = YAHOO.util.Dom.getXY(containerDiv);
			cHeight = containerDiv.offsetHeight;
			cWidth = containerDiv.offsetWidth;
		}

		// hide the layer but display it to get widths
		progressDiv.style.visibility = "hidden";
		progressDiv.style.display = "block";
		
		// show incase it was hidden
		showElement(textContainerDiv);
		hideElement(smallContainerDiv);
		
	

		// set the text
		textDiv.innerHTML = text;

		YAHOO.util.Dom.setXY(progressDiv,xyPos);
		progressDiv.style.width = cWidth+"px";
		progressDiv.style.height = cHeight+"px";

		tHeight = textContainerDiv.offsetHeight;

		// make sure it fits on the page
		if(tHeight > cHeight){
			// use the small progress div
			hideElement(textContainerDiv);
			
			textContainerDiv = smallContainerDiv;
			textDiv = document.getElementById(prefix+"_wait_text_small_div_id");
			imgDiv = document.getElementById(prefix+"_wait_image_small_div_id");
			
			showElement(smallContainerDiv);
			tHeight = smallContainerDiv.offsetHeight;
			
		}

		
		pHeight = cHeight;

		// figure out the text postion
		if(textContainerDiv){
			
			// set margin to 0;
			textContainerDiv.style.marginTop = "0px";
			//tHeight = textContainerDiv.offsetHeight;
			
			//pHeight = progressDiv.offsetHeight;
			
			//alert("tHeight="+tHeight+" cHeight="+cHeight);
			// Make sure it fits on the screen
			viewportXY = getViewportXY();
			scrollXY = getScrollXY();
			
			pPos = YAHOO.util.Dom.getXY(progressDiv);
			
			//if(tHeight > pHeight){
				//progressDiv.style.height = tHeight+"px";
				//containerDiv.style.height = tHeight+"px";
				//pHeight = progressDiv.offsetHeight;
				
			//}
			
			//viewportXY[1] is available viewport space
			//scrollXY[1] is the current scroll position
			//tPos[1] is the top height of the div
			
			// figure out where the progress div is
			pxTop = scrollXY[1];
			pxBottom = scrollXY[1] + viewportXY[1];
			
			pTop = pPos[1];
			pBottom = pPos[1] + pHeight;
			
			fitsTop = false;
			fitsBottom = false;
			if(pxTop <= pTop) fitsTop = true;
			if(pBottom <= pxBottom) fitsBottom = true;
			
		
			if((fitsBottom && fitsTop) || isRows){
				tTop = Math.round((pHeight - tHeight) / 2);
			}
			else{
				pCropTop = ((pxTop - pTop) < 0) ? 0 : (pxTop - pTop);
				pCropBottom = ((pBottom - pxBottom) < 0) ? 0 : (pBottom - pxBottom);
				spaceAvail = pHeight - (pCropTop + pCropBottom);
				if(spaceAvail >= tHeight){
					tTop = Math.round((spaceAvail - tHeight) / 2) + pCropTop ;
				}
				else{
					if(! fitsTop){
						tTop = pHeight - tHeight;
					}
					else if(! fitsBottom){
						tBottom = tTop = 0;
					}
					else tTop = Math.round((pHeight - tHeight) / 2);
				}
			}
					
			textContainerDiv.style.marginTop = tTop+"px";
		}
		
		progressDiv.style.visibility = "visible";

		// fix the position
		if(isRows){
			var viewportXY = getViewportXY();
			var scrollXY = getScrollXY();

			tPos = YAHOO.util.Dom.getXY(progressDiv);
			cHeight = tPos[1] + pHeight;

			if(tPos[1] < scrollXY[1] || cHeight > (scrollXY[1] + viewportXY[1])){
				window.scrollBy(0,tPos[1]-scrollXY[1]-30);
			}
		}
		
		
		return;
	}
	
	fuzzAsync.util.getZIndex = function getZIndex(obj){
		if (obj.currentStyle){  
			currentIndex = parseFloat(obj.currentStyle['zIndex']);  
		}
		else if(window.getComputedStyle){  
			currentIndex = parseFloat(document.defaultView.getComputedStyle(obj,null).getPropertyValue('z-index'));  
		}
		return currentIndex;
	}
