/**
* Handle general tasks dealing with Ajax transactions
* @package Referal_Tracker
* @subpackage JavaScript
* @author Paul Hulett, Solar Flare Productions <paul@bestofsantarosa.net>
* @copyright Mark Wrafter (www.onemorego.com)
* @version 1.0
*/

var req;

/**
* Facilitate an Ajax request.
* @version 1.0
* @param string $myAction Value of the POSTed variable action
* @param string $destAddr URL to POST information to (full or relative)
*/
function AjaxRequest(myAction, destAddr) {
	try {
		if (window.XMLHttpRequest) {
			req = new XMLHttpRequest();
		}
		else if (window.ActiveXObject) {
			req = new ActiveXObject("Microsoft.XMLHTTP");
		}
		else {
			window.alert('Your browser does not support AJAX');
			return false;
		}
		var posted = 'action=' + escape(myAction);
		//get all inputs and selects, send them to the server
		posted = AjaxBuildRequest(document.getElementsByTagName('input'),posted);
		posted = AjaxBuildRequest(document.getElementsByTagName('select'),posted);
		posted = AjaxBuildRequest(document.getElementsByTagName('textarea'),posted);
		req.open('POST', destAddr, true);
		req.onreadystatechange = AjaxUpdate;
		req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		req.send(posted);
	} catch (error) {AjaxError('Error: AjaxRequest, Ajax.js\n' + error);}
}
/**
* For a given type of document element, associate tag id with value.
* @version 1.0
* @param object $elements Collection of input elements, like document.getElementsByTagName('input'))
* @param string $postString New elements will be added to this string
* @return string
*/
function AjaxBuildRequest(elements,postString) {
	try {
		for (var i = 0; i < elements.length; i++) {
			if (elements[i].id && elements[i].value && elements[i].id != '' && elements[i].value != '' && elements[i].value != '-') {
				postString += '&' + escape(elements[i].id) + '=' + escape(elements[i].value);
			}
		}
		return postString;
	}
	catch(error) {
		AjaxError(error);
	}
}
/**
 * Convert the special characters < & > so that they can be displayed.
 *
 * The main use of this function is to ensure that the entire XML response can be
 * dumped into a debug area for inspection.
 *
 * @version 1.0
 * @param string $rawText The text to be modified
 * @return string
 */
function html2entities(rawText) {
	try {
		while (rawText.indexOf("<") > -1) {
			rawText = rawText.replace("<","&lt;");
			rawText = rawText.replace(">","&gt;");
		}
		return rawText;
	} catch (error) {AjaxError('Error: html2entities');}
}
/**
 * Process the XML return of an Ajax request.
 *
 * <p>The XML return is checked for its state. When it has finished, it is processed
 * to update page elements.</p><p><div> elements are processed first. If a table can be delivered
 * by the XML response, it should be targeted at a <div> to ensure proper processing. <td> elements
 * are processed after <div> elements. Select boxes
 * are built next, that way if the value of the select box is also affected by the XML response it
 * will be properly reflected. Finally, input, select and textareas are updated (in that order).</p>
 * @version 1.0
 */
function AjaxUpdate() {
	try {
		if (req.readyState == 4) {
			AjaxStatus('Updating');
			if(docElement('statusBar')) docElement('statusBar').bgColor = "Green";
			if (req.status == 200) {
				setInner('debug',html2entities(req.responseText));
				AjaxStatus("Reading XML Data");
				SetInnerByType(document.getElementsByTagName('div'));
				SetInnerByType(document.getElementsByTagName('td'));
				FillSelects();
				FillById();
				if (xmlHasVal('UserAlert') != false) {
					window.alert(xmlHasVal('UserAlert'));
				}
				if(docElement('statusBar')) docElement('statusBar').bgColor = "White";
				AjaxStatus("Ready");
				doCookies();
				doPrompts();
				doTrigger();
			}
			else setInner('debug',req.status);
		}
		else if (req.readyState == 1) {
			AjaxStatus('Uninitialized');
			if(docElement('statusBar')) docElement('statusBar').bgColor = "Red";
		}
		else if (req.readyState == 2) {
			AjaxStatus('Loading');
			if(docElement('statusBar')) docElement('statusBar').bgColor = "Yellow";
		}
		else if (req.readyState == 3) {
			AjaxStatus('Loaded');
			if(docElement('statusBar')) docElement('statusBar').bgColor = "Yellow";
		}
		else {
			AjaxStatus('Unrecognized Ready State');
			if(docElement('statusBar')) docElement('statusBar').bgColor = "Red";
		}
	}
	catch(error) {
		AjaxError('Error: AjaxStatus\n' + error);
	}
}
/**
 * Fill select elements with data from the XML response.
 *
 * The id of each select element is checked against the XML response. If an element
 * of the XML response matches the select, the select is emptied and filled with the XML data.
 * @version 1.0
 */
function FillSelects() {
	try {
		var xml = req.responseXML;
		var Ids, Outs, j;
		var iSel = document.getElementsByTagName('select');
		for (var i=0; i < iSel.length; i++) {
			if (iSel[i].id && xml.getElementsByTagName('ID'+iSel[i].id).length > 0) {
				EmptySelect(iSel[i]);
				Ids = xml.getElementsByTagName('ID'+iSel[i].id);
				Outs = xml.getElementsByTagName('OUT'+iSel[i].id);
				for (j=0; j < Ids.length; j++) {
					iSel[i].options[j] = new Option(Outs[j].firstChild.nodeValue,Ids[j].firstChild.nodeValue);
				}
			}
		}
	} catch(error){AjaxError('Error: FillSelects:\n' + error);}
}
/**
 * Empty a select element.
 * @version 1.0
 */
function EmptySelect(sel){
	try {
		while (sel.options.length > 0) {
			sel.options[0] = null;
		}
	} catch(error){AjaxError('Error Emptying Select:\n'+error);}
}
/**
 * Part of the process of filling the document with the XML data.
 *
 * This function is a gateway. It makes calls to FillByType with the types needed.
 * @version 1.0
 */
function FillById() {
	try {
		FillByType(document.getElementsByTagName('input'));
		FillByType(document.getElementsByTagName('select'));
		FillByType(document.getElementsByTagName('textarea'));
	}catch(error){AjaxError('Error Filling By Id:\n' + error);}
}
/**
 * Update document elements with data from the XML response.
 *
 * For the given elements, check to see if data was delivered in the XML response.
 * If so, update the document.
 * @version 1.0
 * @param object $element A collection of input elements to be checked.
 */
function FillByType(element) {
	try {
		var val;
		var xml = req.responseXML;
		for (var i = 0; i < element.length; i++) {
			if (element[i].id && xmlHasVal(element[i].id)) {
				val = xmlHasVal(element[i].id);
				if (val == ' ') val = '';
				element[i].value = val;
			}
		}
	}catch(error){AjaxError('Error Filling by Type:\n' + error);}
}
/**
 * Change the value of an element of the document to match a
 * corresponding element in the XML response.
 *
 * @version 1.0
 * @param string $element The id of the element to be updated.
 */
function FillXML(element) {
	try {
		var val;
		var xml = req.responseXML;
		for (var i = 0; i < element.length; i++) {
			element[i].value = xml.getElementsByTagName(element[i].id);
		}
	}catch(error){AjaxError('Error Filling by Type:\n' + error);}
}
/**
 * Update the value of a collection of document elements to corresponding values
 * in the XML response.
 *
 * This function got its name by the fact that collections are usually obtained by
 * document.getElementsByTagName -- the type of element they are.
 *
 * @version 1.0
 * @param object $element The elements to be checked.
 */
function SetInnerByType(element) {
	try {
		var val;
		var xml = req.responseXML;
		for (var i = 0; i < element.length; i++) {
			if (element[i].id && xmlHasVal(element[i].id)) {
				val = xmlHasVal(element[i].id);
				if (val == ' ') val = '';
				element[i].innerHTML = val;
			}
		}
	}catch(error){AjaxError('Error Setting Inner by Type:\n' + error);}
}
/**
 * Handles updating Status messages.
 *
 * @version 1.0
 * @param string $status The text to display in the status area.
 */
function AjaxStatus(status) {
	try {
		setInner('status',status);
	}
	catch(error) {
		AjaxError('Error: AjaxStatus\n' + error);
	}
}
/**
 * Stub fuction to reduce typing.
 *
 * @version 1.0
 * @param string $myElement The id of the element.
 * @return object A reference to a document object.
 */
function docElement(myElement) {
	try {
		return document.getElementById(myElement);
	}
	catch(error) {
		AjaxError('Error: docElement\n' + error);
	}
}
/**
 * Error handler.
 *
 * @version 1.0
 * @param string $error The text of the error message
 * @todo Before going live, the errors should be redirected or silenced.
 */
function AjaxError(error) {
	//window.alert(error);
}
/**
 * Stub function to reduce typing.
 *
 * Updates an element via innerHTML, as opposed to setting its value.
 *
 * @version 1.0
 * @param string $element The id of the element to be updated.
 * @param string $value The innerHTML of the element will be set to this value.
 */
function setInner(element,value) {
	try {
		var myElement = docElement(element);
		if (myElement) myElement.innerHTML = value;
	}
	catch(error) {
		AjaxError('Error: setInner\n' + error);
	}
}
/**
 * Fetch the value of an XML element.
 *
 * If the XML element exists, its value is returned. Otherwise, return boolean false.
 * By default, the Ajax XML response is checked. To check a separate XML document or collection,
 * pass it as the second argument.
 *
 * @version 1.0
 * @param string The id of the tag to be checked
 * @param object (optional) An XML document or collection to check. (When not supplied, the Ajax response is used)
 * @return string
 */
function xmlHasVal() {
	// if tag has a value, return it
	// otherwise, return false
	try {
		var tag = arguments[0];
		if (arguments.length > 1) {
			var xml = arguments[1];
		} else {
			var xml = req.responseXML;
		}
		if (xml.getElementsByTagName(tag).length > 0) {
			return xml.getElementsByTagName(tag)[0].firstChild.nodeValue;
		}
		return false;
	}catch(error){AjaxError('Error in xmlHasVal: ' + tag + '\n' + error);}
}
/**
 * Create an array of values based on a tag id.
 *
 * When more than one value for a tag id may be returned by the Ajax response,
 * use this function to get the values as an array.
 *
 * @version 1.0
 * @param string $tag The id of tag to be checked.
 * @return array An array of strings.
 */
function xmlArray(tag) {
	try {
		var xml = req.responseXML;
		var vals = xml.getElementsByTagName(tag);
		var iXml = new Array();
		for (var i=0; i<vals.length; i++) {
			iXml[i] = vals[i].firstChild.nodeValue;
		}
		return iXml;
	}catch(error){AjaxError('Error creating array from XML for ' + tag + '\n' + error);}
}
/**
 * Set a cookie on the users computer
 *
 * @version 1.0.1
 * @param string Cookie name
 * @param string Cookie value
 * @param int (optional) Hours until cookie will expire
 * @since 1.0.1 - convert '_empty_' to '', IE balked at using a space for an array element
 */
function setCookie() {
	try {
		var c_name = arguments[0];
		var value = arguments[1];
		if (value == ' ' || value == '_empty_') value = '';
		var hours;
		if (arguments.length > 2) hours = arguments[2];
		else hours = 36; //default cookie length
		var extime=new Date();
		//get the current time as miliseconds since 1-1-70
		var t_msecs = extime.getTime();
		//converts hours to miliseconds
		hours = t_msecs + (hours * 60 * 60 * 1000);
		//set the expiration for the cookie
		extime.setTime(hours);
		if (navigator.userAgent.indexOf('Opera') > -1) {
			//browser is Opera, I can't find a way to set the cookie expire time for Opera browsers
			//If you know a fix, please email it to paul at bestofsantarosa dot net
			document.cookie = c_name + '=' + escape(value);
		}
		else document.cookie=c_name + "=" + escape(value) + ((extime==null) ? "" : ";expires=" + extime);
	} catch(error) {AjaxError('Error setting cookie:\n' + error);}
}
/**
 * Use input from the user to set a cookie.
 *
 * @version 1.0
 * @param string Cookie name
 * @param string Question
 * @param int (optional) Hours until cookie will expire
 */
function askUser() {
	try {
		var c_answ = prompt(arguments[1], '');
		if (arguments.length > 2) {
			setCookie(arguments[0], c_answ, arguments[2]);
		}
		else setCookie(arguments[0], c_answ);
	} catch(error) {AjaxError('Error asking user:\n' + error);}
}
/**
 * Check to see if we need to prompt the user for information, which will be stored in a cookie.
 *
 * @version 1.0
 */
function doPrompts(){
	try {
		var myCookies = xmlArray('__COOKIE__');
		var myPrompts = xmlArray('__PROMPT__');
		for (var i=0; i<myPrompts.length; i++) {
			askUser(myCookies[i], myPrompts[i]);
		}
	} catch(error) {AjaxError('Error in doPrompts:\n' + error);}
}

/**
 * If asked for in the XML response, send off a new Ajax request.
 *
 * This functionality was added for those instances when resubmission of data
 * is required, especially for logins. For example: if the user has not logged in
 * send <__COOKIE__> and <__PROMPT__> elements to store the name and passwork, then
 * add  <__TRIGGER__> and <__TDEST__> elements to resubmit the data.
 *
 * @version 1.0
 */
function doTrigger(){
	try {
		var myAction = xmlHasVal('__TRIGGER__');
		var myDest = xmlHasVal('__TDEST__');
		if (myAction && myDest) {
			AjaxRequest(myAction, myDest);
		}
	}catch(error){AjaxError('Error in doTrigger:\n' + error);}
}

/**
 * Set cookies from values in the XML response.
 *
 * @version 1.0
 */
function doCookies(){
	try {
		var cNames = xmlArray('__cName__');
		var cValus = xmlArray('__cValu__');
		for (var i=0; i<cNames.length; i++) {
			setCookie(cNames[i], cValus[i]);
		}
	}catch(error) {AjaxError('Error in doCookies:\n' + error);}
}