//// ------------------------------------------------------------------
////   ENVISION SYSTEMS AJAX LIBRARY Written by William Jamieson
//// ------------------------------------------------------------------
// built based on Matt Waldens Tutorial @ http://www.devx.com/webdev/Article/28695/0/page/1

// constants
var HTTP_STATUS_OK = 200;
var HTTP_ERROR = 404;
var READYSTATE_UNINITIALIZED = 0;
var READYSTATE_LOADING = 1;
var READYSTATE_LOADED = 2;
var READYSTATE_INTERACTIVE = 3;
var READYSTATE_COMPLETED = 4;
var NODE_TYPE_ELEMENT = 1;

function getContainerFunctionName(){
	rawString = getContainerFunctionName.caller.toString();
	openingBracket = rawString.indexOf("(");
	prefix = rawString.toLowerCase().indexOf("function ")+9;
	return rawString.substring(prefix, openingBracket);
}

function findValueInArray(needle, haystack){
	for(i=0;i<haystack.length;i++){
		if(haystack[i]==needle){
			return true;
		}
	}
	return false;
}

function getNextAvailableArrayIndex(theArray){
	// look for an empty spot in requests 
	// array due to a deleted request.
	// default spot is at end
	var nextAvailableArrayIndex = theArray.length;
	// look for closer spot
	for (i=0;i<theArray.length;i++){
		if (theArray[i]==null){
			nextAvailableArrayIndex = i;
			break;
		}
	}
	return nextAvailableArrayIndex;
}

function AJAXHandler(){
	//debug message
	this.debugMessage = new Array();
	this.debugEnabled = false;

	// request objects queue Array
	this.requestQueue = new Array();
	//this.requestQueue[0] = null;
	// our assoc array to map elements to handling functions
	this.responseHandlerArray = new Array();    
 	//this.responseHandlerArray[0] = null;
   
	this.addRequest = function(requestURL, method, requestXML, responseHandlerFunctionName){
         // set defaults omitted optional arguments
		// SITE PROTOCOL FIX - IF THE REQUEST URL CONTAINS THE PROTOCOL (HTTP/HTTPS)
		// REPLACE IT WITH THAT OF THE CURRENT SITE
		var protocolFormat = /[hH]{1}[Tt]{2}[Pp]{1}[Ss]?:\/\//;
		if(protocolFormat.test(requestURL)) {
			requestURL = requestURL.replace(requestURL.substring(0, requestURL.indexOf(':') + 1), location.protocol);
			this.addDebugMessage('protocol specified. overwriting');
		}
		if (!method) method = "GET";
		if (!requestXML) requestXML = null;
		// request object
		var theRequest = null;
		var availableRequestQueueIndex = getNextAvailableArrayIndex(this.requestQueue);         
		// now attempt to make the request
		if (window.XMLHttpRequest){
			// this might look odd, but it is necessary because 'this' in event handlers refers to the owner of the 
			// fired event. See: www.quirksmode.org/js/this.html
			var self = this;
			theRequest = new XMLHttpRequest();
			theRequest.onreadystatechange = function() {self.handle()};
			// add to requestQueue before starting request
			this.requestQueue[availableRequestQueueIndex] = theRequest;
			this.responseHandlerArray[availableRequestQueueIndex] = responseHandlerFunctionName;
			theRequest.open(method, requestURL, true);
			theRequest.send(requestXML);
			this.addDebugMessage(requestURL);
			return theRequest;
		}else if (window.ActiveXObject){
			theRequest = new ActiveXObject("Microsoft.XMLHTTP");
			if (theRequest){
				var self = this;
				theRequest.onreadystatechange = function() {self.handle()};
				// add to requestQueue before starting request
				this.requestQueue[availableRequestQueueIndex] = theRequest;
				this.responseHandlerArray[availableRequestQueueIndex] = responseHandlerFunctionName;
				theRequest.open(method, requestURL, true);
				theRequest.send(requestXML);
				return theRequest;
			}else{
				return false; // request failed
			}
		}else{
			// no ajax support
			return false; // request failed
		}
		return false; // request failed
	};
    
    
	this.handle = function(){
		// loop through request queue to see 
		// if any are ready with a response. we 
		// keep looping even after we find one, 
		// because it might not be the one that 
		// fired the event (there could be 
		// multiple that are ready.
		for (var i=0; i<this.requestQueue.length; i++){
			// if state is "complete" (skipping empty queue entries)
			if(this.requestQueue[i]!=null && this.requestQueue[i].readyState==READYSTATE_COMPLETED){
				if(this.requestQueue[i].status==HTTP_STATUS_OK){
					this.addDebugMessage('---------------- PROCESSING STARTED ----------------');
					this.addDebugMessage('1: this.requestQueue.length: ' + this.requestQueue.length);
					this.addDebugMessage('2: this.responseHandlerArray.length: ' + this.responseHandlerArray.length);
					// pass this off to the xml parser
					//this.parseResponse(this.requestQueue[i].responseXML);
					
					// remove request from queue and handler array before the response handler gets called
					// as this can cause problems where response handler triggers additional ajax functions
					this.theResponseHandler = this.responseHandlerArray[i];
					this.theRequestQueueItem = this.requestQueue[i];
					this.addDebugMessage('3: this.theResponseHandler: '+this.theResponseHandler);
					this.addDebugMessage('4: this.theRequestQueueItem: '+this.theRequestQueueItem);
					this.requestQueue[i] = null;
					this.responseHandlerArray[i] = null;
					//alert('array: '+this.requestQueue.join('###')+' i: ' + i + ' this.requestQueue.length: ' + this.requestQueue.length + ' handlerFunction:   ' + this.theResponseHandler);
					
					// send to handler function
					if (this.theResponseHandler!=null && this.theResponseHandler!='' && 'function' == typeof eval('window.'+this.theResponseHandler)){
						this.addDebugMessage('5: this.theRequestQueueItem.responseText: ' + this.theRequestQueueItem.responseText.htmlEncode());
						eval(this.theResponseHandler+'(this.theRequestQueueItem);')
						this.addDebugMessage('---------------- "' + this.theResponseHandler + '" EXECUTED SUCCESSFULLY COMPETED ----------------\n');
					}else{
						alert('AJAX response could not be handled because \'' + this.theResponseHandler + '\' function could not be found.');
						this.addDebugMessage('\n---------------- FUNCTION NOT FOUND ----------------\n');
					}
					
				}else{
					alert('An AJAX HTTP error status occurred ('+this.requestQueue[i].status+')');
					this.addDebugMessage('---------------- AJAX ERROR OCCURRED ----------------\n');
				}
			}
			this.printDebug();
		}
	};

    
    
   this.parseResponse = function(oNode){
		if (!oNode) return;
		// base case (oNode is a leaf element)
		if (!oNode.hasChildNodes()) return;
		// else... recurse through children
		var children = oNode.childNodes;
		for (var i=0; i<children.length; i++){
			// all nodes (element, attribute, text, 
			// etc.) are returned as children, but 
			// we only want to act on children that 
			// are element nodes
			if (children[i].nodeType==NODE_TYPE_ELEMENT){
			// check to see if a handler exists 
			// specifically for this element
			var elementName = children[i].nodeName;
			if(this.responseHandlerArray.containsKey(elementName)){
				// if so, fire handler, and pass 
				// subtree starting with the node 
				// of interest
				// retreive the handler
				var funcHandler = this.responseHandlerArray.get(elementName);
				// fire the handler and pass the 
				// subtree as its argument
				funcHandler(children[i]);
				// a match was found. conditionally 
				// recurse on the subtree. 
				// conditionally, because this subtree
				// has already been handed off to one 
				// function.
				if (this.recurseOnChildren){
					this.parseResponse(children[i]);
				}
			}else if (this.isWildcardSet){
				// retreive the handler
				var funcHandler = this.responseHandlerArray.get('*');
				// fire the handler and pass the 
				// subtree as its argument
				funcHandler(children[i]);
				
				// a match was found. conditionally 
				// recurse on the subtree. 
				// conditionally, because this subtree
				// has already been handed off to one 
				// function.
				if (this.recurseOnChildren){
					this.parseResponse(children[i]);
				}
			}else{
				// not match yet found on this 
				// subtree. keep digging.
				this.parseResponse(children[i]);
			}
			
			} // end if element node type
		} // end for loop of children
	}; // end method parseResponse

    this.addDebugMessage = function(message) {
    	var temp = new Array(message);
    	this.debugMessage = this.debugMessage.concat(temp);
    };

	this.printDebug = function() {
		// THIS FUNCTION WILL ONLY PRINT THE AJAX DEBUG INFORMATION IF 
		// DEVELOPER INFORMATION IS SET TO ON IN THE SETTING MENU
		var ajaxDebugInformation = returnDOMObject('ajaxDebugInformation');
		if(ajaxDebugInformation != null) {
			ajaxDebugInformation.innerHTML = this.debugMessage.join('\r\n');
		}
    };

}

var ajaxHandler = new AJAXHandler();

function setObjectInnerHTMLToAJAXResponseText(containerID,ajaxRequest){
	// INSERT AJAX RESPONSE INTO REQUESTED CONTAINER
	returnDOMObject(containerID).innerHTML = ajaxRequest.responseText;
	
	// EXTRACT JAVASCRIPT CODE FROM AJAX RESPONSE
	scriptObjects = returnDOMObject(containerID).getElementsByTagName('script');
	var ajaxJSSourceCode = '';
	
	// AN ARRAY CONTAINING URLS TO JAVASCRIPT INCLUDES
	var ajaxJSInludesSourceList = new Array();
	// ajaxJSInluceSourceTemp IS A ONE ELEMENT ARRAY. AN ARRAY IS USED FOR TEMPORARY STORAGE
	// TO MAKE IT EASY TO ADD IT TO THE FINAL ARRAY USING CONCAT
	var ajaxJSInluceSourceTemp = new Array(1);
	for (var i = 0;i < scriptObjects.length; i++) {
		// PROCESS THE JAVASCRIPT INCLUDES FIRST. THIS IS REQUIRED IN CASE THE TAG IS IN THE FORMAT OF
		// <SCRIPT SRC="SOURCE.JS" ...>NO SCRIPT CODE< / SCRIPT>
		if(scriptObjects[i].src!='') {
			// ADD THE SOURCE URL OF THE JS INLCUDE TO ajaxJSInludesSourceList ARRAY
			ajaxJSInluceSourceTemp[0] = scriptObjects[i].src;
			ajaxJSInludesSourceList = ajaxJSInludesSourceList.concat(ajaxJSInluceSourceTemp);
		} else if(scriptObjects[i].innerHTML!='') {
			// THE TAG IS NOT AN INCLUDE TAG - ADD ITS SOURCE TO ajaxJSSourceCode STRING
			ajaxJSSourceCode = ajaxJSSourceCode + scriptObjects[i].innerHTML;
		}
	}
	// REMOVE INLINE SCRIPT TAGS AND CONTENTS FROM MSIE BECAUSE WE ARE MOVING IT TO THE HEAD OF THE DOCUMENT
	if (navigator.userAgent.indexOf('MSIE')!=-1){
		while (scriptObjects.length>0) {
			// REMOVE SCRIPT OBJECT FROM DOM AS WE WILL ADD IT TO THE DOCUMENT HEAD LATER
			scriptObjects[0].parentNode.removeChild(scriptObjects[0]);
		}
	}
	// REMOVE HTML COMMENTS FROM JAVASCRIPT CODE
	//ajaxJSSourceCode.replace('<!--','');
	//ajaxJSSourceCode.replace('//-->','');
	//ajaxJSSourceCode.replace('-->','');
	//prompt('ajaxJSSourceCode',ajaxJSSourceCode);
	// ADD JAVASCRIPT CODE TO DOM SO THAT IT CAN BE EXECUTED		
	if(ajaxJSSourceCode!=''){
		// WORKS FOR MSIE AND FIREFOX
		// HOWEVER FIREFOX DOES NOT HANDLES ERRORS PROPERLY IF USING THIS METHOD
		if (navigator.userAgent.indexOf('MSIE')!=-1){
			// DELETE AND RE-REGISTER JAVASCRIPT IF IT ALREADY EXISTS 
			if(returnDOMObject(containerID + 'JS') != null){
				returnDOMObject(containerID + 'JS').parentNode.removeChild(returnDOMObject(containerID + 'JS'));
			}
			// REGISTER JAVASCRIPT IN DOM TO MAKE IT EXECUTABLE
			scriptElementForContainer = document.createElement('script');
			scriptElementForContainer.id = containerID + 'JS';
			scriptElementForContainer.type = 'text/javascript';
			document.getElementsByTagName('head').item(0).appendChild(scriptElementForContainer);
			scriptElementForContainer.text = ajaxJSSourceCode;
		
		// WORKS FOR SAFARI AND FIREFOX
		}else{
			window.setTimeout(ajaxJSSourceCode,0);
		}
	}
	
	// ADD JAVASCRIPT INCLUDES TO DOM SO THAT IT CAN BE EXECUTED		
	if(ajaxJSInludesSourceList.length != 0) {
		for(JSIncludeSourceURL = 0; JSIncludeSourceURL < ajaxJSInludesSourceList.length; JSIncludeSourceURL++) {
			// CHECK IF THIS INCLUDE WAS ALREADY PROCESSED AND REMOVE IT IF SO
			if(returnDOMObject(containerID + 'JSInclude') != null) {
				returnDOMObject(containerID + 'JSInclude').parentNode.removeChild(returnDOMObject(containerID + 'JSInclude'));
			}//*/
			scriptElementForContainer = document.createElement('script');
			scriptElementForContainer.id = containerID + 'JSInclude';
			scriptElementForContainer.type = 'text/javascript';
			scriptElementForContainer.src = ajaxJSInludesSourceList[JSIncludeSourceURL];
			document.getElementsByTagName('head').item(0).appendChild(scriptElementForContainer);
		}
	}
}
	
function urlEncode(text) {
	// The Javascript escape and unescape functions do not correspond
	// with what browsers actually do...
	var SAFECHARS = "0123456789" +                  // Numeric
					"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +  // Alphabetic
					"abcdefghijklmnopqrstuvwxyz" +
					"-_.!~*'()";                    // RFC2396 Mark characters
	var HEX = "0123456789ABCDEF";
	var plaintext = text;
	var encoded = "";
	for(var i=0;i<plaintext.length;i++){
		var ch = plaintext.charAt(i);
		if(ch==" "){
			encoded += "+";                             // x-www-urlencoded, rather than %20
		}else if(SAFECHARS.indexOf(ch) != -1){
			encoded += ch;
		}else{
			var charCode = ch.charCodeAt(0);
			if(charCode>255){
				alert("Unicode Character '"+ch+"' cannot be encoded using standard URL encoding.\n" +
					  "(URL encoding only supports 8-bit characters.)\n" +
					  "A space (+) will be substituted." );
				encoded+="+";
			}else{
				encoded += "%";
				encoded += HEX.charAt((charCode >> 4) & 0xF);
				encoded += HEX.charAt(charCode & 0xF);
			}
		}
	}
	return encoded;
}


// ADD HTML ENCODE FUNCTION TO ALL STRINGS
String.prototype.htmlEncode =  function () {
	return this.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

String.prototype.htmlDecode =  function () {
	return this.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
}

function div2textarea(divID) {
	var div = returnDOMObject(divID);
	var textarea = document.createElement('textarea');
//	textarea.setAttribute('cols', '200');
//	textarea.setAttribute('rows', '25');
	textarea.style.height = '350px';
	textarea.style.width = '100%';
	textarea.style.overflow = 'auto';
	textarea.value = div.innerHTML.htmlDecode();
	div.parentNode.appendChild(textarea);
	div.style.display = 'none';
}

