// ----------------------------------------------------------- //
//                        clientBase                           //
// ----------------------------------------------------------- //
function debugLog() { this.clear(); }
debugLog.prototype.clear = function()
{
	this.element = null;
}
debugLog.prototype.log = function(theMsg)
{
	if (this.element)
	{
        newDiv = document.createElement('div');
        this.element.appendChild(newDiv);
       	newDiv.appendChild(document.createTextNode(theMsg));
    }
}



// ----------------------------------------------------------- //
//                       ajaxRequestor                         //
// ----------------------------------------------------------- //
function ajaxRequestor() { this.clearAll(); }

ajaxRequestor.prototype.__defaultError = function(sender)
{
	var tempStr = "ajaxRequestor Error:\n" +
                  "status: " + this.requestor.status + "\n" +
	 	          "headers: " + this.requestor.getAllResponseHeaders();
	alert(tempStr);
}
ajaxRequestor.prototype.__defaultSuccess = function(sender)
{
	alert("ajaxRequestor successfully returned from a request - but there is no handler assigned to receive it");
}
ajaxRequestor.prototype.__decodeString = function(inputStr)
{
	var decoded = unescape(inputStr);
	decoded = decoded.replace(/\%2F/g, "/"); 
	decoded = decoded.replace(/\%3F/g, "?");
	decoded = decoded.replace(/\%3D/g, "=");
	decoded = decoded.replace(/\%26/g, "&");
	decoded = decoded.replace(/\%40/g, "@");
	return decoded;
}
ajaxRequestor.prototype.__encodeString = function(inputStr)
{
	var encoded = escape(inputStr);
	encoded = encoded.replace(/\//g,"%2F");
	encoded = encoded.replace(/\?/g,"%3F");
	encoded = encoded.replace(/=/g,"%3D");
	encoded = encoded.replace(/&/g,"%26");
	encoded = encoded.replace(/@/g,"%40");
	return encoded;
}
ajaxRequestor.prototype.__getParams = function()
{
	if (this.getNames.length == 0) { return ""; }
	var out = (this.url.indexOf('?') == -1) ? '?' : '&';
	for (var i=0; i<this.getNames.length; i++)
	{
		out += this.getNames[i] + '=' + this.getValues[i];
		if (i < (this.getNames.length - 1)) { out += '&'; }
	}
	return out;
}
ajaxRequestor.prototype.__getRequestor = function()
{
	if ((this.requestor != null) && (!this.reqIsIE)) { return true; }
	
	try {
		this.requestor = new XMLHttpRequest();
		this.reqIsIE = false;
		return; true;
	} catch(e) {}
	
	try {
		this.requestor = new ActiveXObject("Msxml2.XMLHTTP.6.0");
		this.reqIsIE = true;
		return; true;
	} catch(e) {}

	try {
		this.requestor = new ActiveXObject("Msxml2.XMLHTTP.3.0");
		this.reqIsIE = true;
		return; true;
	} catch(e) {}

	try {
		this.requestor = new ActiveXObject("Msxml2.XMLHTTP");
		this.reqIsIE = true;
		return; true;
	} catch(e) {}

	try {
		this.requestor = new ActiveXObject("Microsoft.XMLHTTP");
		this.reqIsIE = true;
		return; true;
	} catch(e) {}
	
	alert('ajaxRequestor Fatal Error: Cannot instantiate an XMLHTTP Object');
}
ajaxRequestor.prototype.__xmitLog = function(theMsg)
{
	var bodyArr = document.getElementsByTagName('body');
	var theBody = bodyArr[0];
	theBody.appendChild(document.createTextNode(theMsg));
	theBody.appendChild(document.createElement('br'));
}
ajaxRequestor.prototype.__onRTS = function()
{
	if ((this.requestor.readyState >= 2) && (this.timeoutHandle))
	{
		clearTimeout(this.timeoutHandle);
		this.timeoutHandle = false;
	}
	
    if (this.requestor.readyState == 4)
	{
		if (this.masterStatus) { this.masterStatus.handleChange(false); }
		if ((this.requestor.status==200) || (this.requestor.status==0))
		{
			this.lastResponse = this.__decodeString(this.requestor.responseText);
			if (!this.lastResponse) 
			{
				return false;
			}
			if (this.xmlHandler) 
			{
				this.xmlHandler.importXML(this.lastResponse); 
			}
			this.onSuccess(this);
		} else {
			switch(this.requestor.status)
			{
				case 12029:
				case 12030:
				case 12031:
				case 12152:
				case 12159:
					alert('Untrapped error: ' + this.request.status);
/*
					var loader = this;
					setTimeout( function() { loader.execute.call(loader); }, 10);
*/
					break;
					
				default:
					this.onError(this);
			}
		}
		this.busy = false;
	}
}
ajaxRequestor.prototype.__postParams = function()
{
	var out = "";
	var varNames = '';
	for (var i=0; i<this.postNames.length; i++)
	{
		if (i > 0) { varNames += '|'; }
		varNames += this.postNames[i];
		if (i > 0) { out += '&'; }
		out += this.postNames[i] + '=' + this.__encodeString(this.postValues[i]);
	}
	if (out) { out += '&' + 'ajax_var_names=' + varNames; }
	return out;
}
ajaxRequestor.prototype.abort = function()
{
	if (this.busy)
	{
		// clear timeout as well
		this.requestor.abort();
		clearTimeout(this.timeoutHandle);
		this.timeoutHandle = false;
		this.busy = false;
	}
}

ajaxRequestor.prototype.clear = function()
{
	this.methodPost = true;
	this.__transStatus = 0;
	this.__transBusy = false;
    this.lastResponse = new String();
	this.selfReference = null;
    this.newRequest();
	this.timeoutHandle = false;
	this.timeoutMS = 5000;
}
ajaxRequestor.prototype.clearAll = function()
{
    this.xmlHandler = null;
    this.masterStatus = null;
    this.onUnrecognized = new String();

    this.onError = this.__defaultError;
    this.onSuccess = this.__defaultSuccess;
    
    this.clear();
}
ajaxRequestor.prototype.execute = function(timeoutVal)
{
	if (this.busy)
	{
		// clear timeout as well
		this.requestor.abort();
		this.busy = false;
	}
	
	var thisTimeoutVal = this.timeoutMS;
	if (timeoutVal != undefined) { thisTimeoutVal = timeoutVal; }
	
	this.__getRequestor();

	if (!this.requestor) {
		alert("You cannot dispatch a request on this machine (no viable XMLHTTPRequestor)");
		return "";
	}
	if (!this.url) {
		alert("You must supply a URL to ajaxRequestor to process a request");
		return "";
	}

	this.busy = true;
	var httpMethod = (this.methodPost) ? 'POST' : 'GET';

	var theURL = this.url;
	theURL += this.__getParams();
	this.lastRequest = theURL;
	
	var loader = this;
	this.requestor.onreadystatechange = function() { loader.__onRTS.call(loader); }
	if (this.masterStatus) { this.masterStatus.handleChange(true); }

	// Set a callback to <me> in case the request takes to long...
	this.timeoutHandle = setTimeout( function() { loader.__handleTimeout.call(loader); }, this.timeoutMS);
	
	this.requestor.open('POST', theURL, true);
	this.requestor.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	if ((document.all) && (document.getElementById))
	{
		// IE
		setTimeout( function() { loader.__executeSend.call(loader) }, 10);
		} else {
		this.requestor.send(this.__postParams());
	}
}
ajaxRequestor.prototype.__executeSend = function() { this.requestor.send(this.__postParams()); }

ajaxRequestor.prototype.__handleAbort = function()
{
	if (this.masterStatus) { this.masterStatus.handleChange(false); }
	this.requestor.abort();
}
ajaxRequestor.prototype.__handleTimeout = function()
{
	this.__handleAbort();
	var loader = this;
	setTimeout(function() { loader.execute.call(loader); }, 100);
}
ajaxRequestor.prototype.getParam = function(key, value)
{
	var ptr = this.getNames.length;
	for (var i=0; i<this.getNames.length; i++)
	{
		if (this.getNames[i] == key) { ptr = i; }
	}
	this.getNames[ptr] = key;
	this.getValues[ptr] = value;
}
ajaxRequestor.prototype.method = function(doPost)
{
	this.methodPost = (doPost);
}
ajaxRequestor.prototype.newRequest = function()
{
	this.getNames = new Array();
	this.getValues = new Array();
	this.postNames = new Array();
	this.postValues = new Array();
	this.url = '';
}
ajaxRequestor.prototype.postParam = function(key, value)
{
	var ptr = this.postNames.length;
	for (var i=0; i<this.postNames.length; i++)
	{
		if (this.postNames[i] == key) { ptr = i; }
	}
	this.postNames[ptr] = key;
	this.postValues[ptr] = value;
}



// ----------------------------------------------------------- //
//                       transmitState                         //
// ----------------------------------------------------------- //
function transmitState() { this.clear(); }

transmitState.prototype.clear = function()
{
	this.busyCount = 0;
	this.onChange = null;
	this.busy = false;
}
transmitState.prototype.handleChange = function(busy)
{
        if (busy) { this.busyCount++; }
        if (!busy) { this.busyCount--; }
        if (this.busyCount < 0) { this.busyCount = 0; }
        if (this.busyCount)
        {
                if (!this.busy) { if (this.onChange) { this.onChange(true); } }
                this.busy = true;
        } else {
                if (this.busy) { if (this.onChange) { this.onChange(false); } }
                this.busy = false;
        }
}





// ----------------------------------------------------------- //
//                       dirtyElement                          //
// ----------------------------------------------------------- //
function dirtyElement() { this.clear(); }
dirtyElement.prototype.clear = function()
{
	this.dirtyList = new Array();
	this.dirtyIDX = new Array();
	this.onChange = null;
	this.isDirtyAny = false;
}
dirtyElement.prototype.clearDirty = function(theName) 
{ 
	this.dirtyList[theName] = false;
	this.isDirtyAny = false;
	for (var i=0; i<this.dirtyIDX.length; i++)
	{
		var thisName = this.dirtyIDX[i];
		if (this.dirtyList[thisName]) { this.isDirtyAny = true; }
	}
	if ( (!this.isDirtyAny) && (this.onChange) ) { this.onChange(false, theName); }
}
dirtyElement.prototype.clearDirtyAll = function()
{
	for (var i=0; i<this.dirtyIDX.length; i++)
	{
		var thisName = this.dirtyIDX[i];
		this.dirtyList[thisName] = false;
	}
	if (this.onChange) { this.onChange(false); }
	this.isDirtyAny = false;
}
dirtyElement.prototype.dirty = function(theName) 
{
	if ( (!this.isDirty(theName)) && (this.onChange)) { this.onChange(true, theName); }
	this.dirtyList[theName] = true;
	this.isDirtyAny = true; 
}
dirtyElement.prototype.isDirty = function(theName) 
{ 
	return this.dirtyList[theName]; 
}
dirtyElement.prototype.proceed = function(theName, theMsg)
{
	if (this.isDirty(theName)) { return confirm(theMsg); }
	else return true;
}
dirtyElement.prototype.proceedAny = function(theMsg)
{
	if (this.isDirtyAny) 
	{ 
		ok2Go = confirm(theMsg);
		if (ok2Go) { this.clearDirtyAll(); }
		return ok2Go; 
	} else return true;
}
dirtyElement.prototype.watch = function(theName) { 
	this.dirtyList[theName] = false; 
	var ptr = this.dirtyIDX.length;
	this.dirtyIDX[ptr] = theName;	
}



// ----------------------------------------------------------- //
//                       localStorage                          //
// ----------------------------------------------------------- //
function localStorage() { this.clear(); }
localStorage.prototype.clear = function()
{
	this.fileName = new String();
}
localStorage.prototype._getRaw = function()
{
	var rawBuff = document.cookie;
	var cookieRegExp = new RegExp("\\b" + this.fileName + "=([^;]*)");
	theValue = cookieRegExp.exec(rawBuff);
	if (theValue != null) { theValue = theValue[1]; }
	return theValue;
}
localStorage.prototype.asArray = function()
{
	var outArr = new Object();
	var rawBuff = this._getRaw(this.fileName);
	if (rawBuff == undefined) { return false; }
	var tempArr = rawBuff.match(/([^&]+)/g);
	for (var i=0; i<tempArr.length; i++)
	{
		var parts = tempArr[i].match(/([^=]+)=(.*$)/);
		var varName = parts[1];
		var varValue = parts[2];
		outArr[varName] = unescape(varValue);
	}
	return outArr;
}
localStorage.prototype.dropFile = function()
{
	if (this.fileName)
	{
		var expiredDate = new Date();
		expiredDate.SetMonth(-1);
		var writeBuff = this.fileName + "=";
		writeBuff += "expires=" + expiredDate.toGMTString();
		document.cookie = writeBuff;
	}
}
localStorage.prototype.dropItem = function(theName)
{
	var rawBuff = readUnEscapedCookie(this.fileName);
	if (rawBuff)
	{
		var stripAttributeRegExp = new RegExp("(^|/&)" + theName + "=[^&]*&?");
		rawBuff = rawBuff.replace(stripAttributeRegExp, "$1");
		if (rawBuff.length != 0)
		{
			var newBuff = this.fileName + "=" + rawBuff;
			document.cookie = newBuff
		} else { this.dropFile(); }
	}
}
localStorage.prototype.enabled = function()
{
	var cookiesEnabled = window.navigator.cookieEnabled;
	if (!cookiesEnabled)
	{
		document.cookie = "cookiesEnabled=True";
		cookiesEnabled = new Boolean(document.cookie).valueOf();
	}
	return cookiesEnabled;
}
localStorage.prototype.retrieveItem = function(theName)
{
	var rawBuff = this._getRaw(this.fileName);
	var extractMultiValueCookieRegExp = new RegExp("\\b" + theName + "=([^;&]*)");
	resValue = extractMultiValueCookieRegExp.exec(rawBuff);
	if (resValue != null) { resValue = unescape(resValue[1]); }
	return resValue;
}
localStorage.prototype.storeItem = function(theName, theValue)
{
	var rawBuff = this._getRaw(this.fileName);
	if (rawBuff)
	{
		var stripAttributeRegExp = new RegExp("(^|&)" + theName + "=[^&]*&?");
		rawBuff = rawBuff.replace(stripAttributeRegExp, "$1");
		if (rawBuff.length != 0) { rawBuff += "&"; }
	} else rawBuff = "";
	
	rawBuff += theName + "=" + escape(theValue);
	document.cookie = this.fileName + "=" + rawBuff;
}



// ----------------------------------------------------------- //
//                          xmlNode                            //
// ----------------------------------------------------------- //
function xmlNode(theHandler) 
{ 
	this.tag = new String("unassigned");
	this.data = new String();
	this.updateData = new String();
	this.children = new Array();
	this.attributeKeys = new Array();
	this.attributeValues = new Array();
	this.parent = new Number();
	this.mID = new Number();
	this.handler = theHandler;
}
xmlNode.prototype.addChildPtr = function(thePtr)
{
	var newPtr = this.children.length;
	this.children[newPtr] = thePtr;
}
xmlNode.prototype.attribStr = function()
{
	var out = '';
	for (var i=0; i<this.attributeKeys.length; i++)
	{
		if (this.attributeValues[i])
		{
			out += ' ' + this.attributeKeys[i] + '="' + this.attributeValues[i] + '"';
		}
	}
	return out;
}
xmlNode.prototype.attributeValue = function(attrName)
{
	attrName = attrName.toLowerCase();
	for (var i=0; i<this.attributeKeys.length; i++)
	{
		if (attrName == this.attributeKeys[i])
		{
			return this.attributeValues[i];
		}
	}
	return "";
}
xmlNode.prototype.buildXML = function(outArr, indent)
{
	var ptr = outArr.length;
	var myIndent = '';
	if (!this.handler.forAjaxMS) { myIndent = this.handler.indentTemplate.substr(0, indent * 4); }
	var myTag = myIndent + '<' + this.tag;
	var myAttribs = this.attribStr();
	if (myAttribs) { myTag += ' ' + myAttribs; }
	if ( (this.data.length==0) && (!this.hasChildren()) )
	{
		outArr[ptr] = myTag + '/>';
		return;
	}
	
	// If no children, then I either have data or am empty...
	if (!this.hasChildren()) 
	{
		var myData = this.data;
		myData = myData.replace(/\</g, '[$felt]');
		myData = myData.replace(/\>/g, '[$fegt]');
		outArr[ptr] = myTag + '>' + myData + '</' + this.tag + '>';
		return;
	}
	
	// OK: I have child nodes...
	outArr[ptr] = myTag + '>';
	for (var i=0; i<this.children.length; i++)
	{
		var thisNode = this.handler.__xmlNodeArray[this.children[i]];
		thisNode.buildXML(outArr, indent+1);
	}
	var ptr = outArr.length;
	outArr[ptr] = myIndent + '</' + this.tag + '>';	
}
xmlNode.prototype.childData = function(childIDX)
{
	var tempNode = this.childNode(childIDX);
	return tempNode.data;
}
xmlNode.prototype.childDataNamed = function(tagName)
{
	tagName = tagName.toLowerCase();
	for (var i=0; i<this.children.length; i++)
	{
		var tempNode = this.childNode(i);
		if (tempNode.tag == tagName) { return tempNode.data; }
	}
	return '';
}
xmlNode.prototype.childNode = function(childIDX)
{
	var childPtr = this.children[childIDX];
	return this.handler.__xmlNodeArray[childPtr];
}
xmlNode.prototype.dump = function()
{
	var out = '[' + this.mID + '] ';
	out += 'tag=' + this.tag + ', ';
	out += 'data length is ' + this.data.length + ', ';
	var childCount = this.children.length;
	out += 'children[' + childCount + '](';
	for (var j=0; j<childCount; j++) { out += this.children[j] + ','; }
	out += '), ';
	out += 'parent=' + this.parent + ', ';
	out += 'data=' + this.data;
	return out;
}
xmlNode.prototype.hasChildren = function() { return (this.children.length > 0); }
xmlNode.prototype.importNode = function() 
{
	// Find the end of <this> tag and ditch all of it from the input string...
	endOfTag = this.handler.work.indexOf('>', this.handler.searchPtr);
	workStr = this.handler.work.substring(this.handler.searchPtr + 1, endOfTag); // doesnt grab the '>'
	this.handler.searchPtr = endOfTag + 1;

	// now look for the first space in the tag - it denotes the end of the tag name...
	emptyNode = false;
	if (workStr.indexOf('/') >= 0)
	{
		emptyNode = true;
		workStr = workStr.substr(0, workStr.length - 1);
	}
	space = workStr.indexOf(' ');
	if (space == -1) { space = 65535; } // arbitrary large number
	ptr = workStr.length;
	if (space < ptr) { ptr = space };

	this.tag = workStr.substring(0, ptr);
	this.tag = this.tag.toLowerCase();
	workStr = workStr.substring(ptr, 65535); // artibrarily large number...

	// Now time for the attributes...
    workStr = workStr.replace(/=[\s]*"[\s]*/g, '=\"') + ' '; // Kill all whitespace between = and "
	arr1 = workStr.split(/" /g); // now split on the remaining "(space)
	this.attributeKeys = new Array();
	this.attributeValues = new Array();
	for (i=0; i<arr1.length; i++) 
	{
		if (arr1[i] <= ' ') { continue; }
		arr2 = arr1[i].split(/=\"/g);
		var thisKey = arr2[0];
		var thisValue = arr2[1];
		this.attributeKeys[i] = thisKey.replace(/^\s+|\s+$/, '');
		this.attributeValues[i] = thisValue.replace(/^\s+|\s+$/, '');
	}
	
	// If <I> am an empty tag, then exit now...
	if (emptyNode) { return ""; }

	// If the next character in the work buffer is anything other than a '<' then there
	// is text for me to collect, and the tag must, by XML rules, be all done...
	thisChar = this.handler.work.charAt(this.handler.searchPtr);
	if (thisChar != '<') 
	{
		textEnd = this.handler.work.indexOf('<', this.handler.searchPtr);
		var tempStr = this.handler.work.substring(this.handler.searchPtr, textEnd);
		tempStr = tempStr.replace(/\[\$fegt\]/g, '>');
		tempStr = tempStr.replace(/\[\$felt\]/g, '<');
		this.data = tempStr;
		this.handler.__killNextTag();
		return "";
	}


	// OK: If I am here, then either there are children OR the very next tag is <me> closing
	// and I was empty after all. 
	while ((!this.handler.__nextIsClose()) && (this.handler.work.length > 0)) 
	{
		myPtr = this.children.length;
		ptr = this.handler.__newXMLNode(this.handler);
		this.children[myPtr] = ptr;
		tempObj = this.handler.__xmlNodeArray[ptr];
		tempObj.parent = this.mID;
		tempObj.importNode();
	}


	// I am at <me> closing. Eat me off the master string end exit...
	this.handler.__killNextTag();
}
xmlNode.prototype.updateAttribute = function(key, value)
{
	var key = key.toLowerCase();
	for (var i=0; i<this.attributeKeys.length; i++)
	{
		if (this.attributeKeys[i] == key)
		{
			this.attributeValues[i] = value;
			return true;
		}
	}
	var ptr = this.attributeKeys.length;
	this.attributeKeys[ptr] = key;
	this.attributeValues[ptr] = value;
	return true;
}
xmlNode.prototype.updateChildPtr = function(idx, newPtr)
{
	this.children[idx] = newPtr;
}



// ----------------------------------------------------------- //
//                        xmlHandler                           //
// ----------------------------------------------------------- //
function xmlHandler() { this.clearAll(); }
xmlHandler.prototype.__bubbleSort = function(method, attrName)
{
	var max = this.currentChildCount();
	var tempNode = null;
	for (var i=0; i<max - 1; i++)
	{
		for (var j=(i+1); j<max; j++)
		{
			switch (method)
			{
				case 0 :
					baseVal = this.childTag(i);
					compVal = this.childTag(j);
					break;
				case 1 :
					baseVal = this.childData(i);
					compVal = this.childData(j);
					break;
				case 2 :
					tempNode = this.childNode(i); 
					baseVal = tempNode.attributeValue(attrName);
					tempNode = this.childNode(j); 
					compVal = tempNode.attributeValue(attrName);
					break;
				case 3 :
					baseVal = this.onSortByOther(this, this.childNode(i));
					compVal = this.onSortByOther(this, this.childNode(j));
			}
			baseVal = baseVal.toLowerCase();
			compVal = compVal.toLowerCase();
			if (compVal < baseVal)
			{
				var basePtr = this.childPtr(i);
				var compPtr = this.childPtr(j);
				tempNode = this.__xmlNodeArray[this.currentPtr];
				tempNode.updateChildPtr(i, compPtr);
				tempNode.updateChildPtr(j, basePtr);
			}
		}
	}
	return true;
}
xmlHandler.prototype.__killNextTag = function()
{
	if (this.work.length > 0) 
	{
        this.searchPtr = this.work.indexOf('>', this.searchPtr) + 1;
	}
}
xmlHandler.prototype.__newXMLNode = function(theHandler)
{
	ptr = this.__xmlNodeArray.length;
    this.__xmlNodeArray[ptr] = new xmlNode(this);
    this.__xmlNodeArray[ptr].handler = this;
    this.__xmlNodeArray[ptr].mID = ptr;
	return ptr;
}
xmlHandler.prototype.__nextIsClose = function()
{
	// This function is called by nodes that want to know if the next 
	// item in the tag list is actually <themseleves> closing...
	result = (this.work.substr(this.searchPtr, 2) == '</');
	return result; 
}
xmlHandler.prototype.__xmlNodeArrayDump = function()
{
    var childCount = this.__xmlNodeArray.length;
    var out = '';
    for (var i=0; i<childCount; i++) 
    {
    	if (this.__xmlNodeArray[i]) 
    	{ 
    		out += this.__xmlNodeArray[i].dump() + '<br>\n'; 
    	}
    }
    return out;
}
xmlHandler.prototype.addChild = function(theTag, theValue, stepIn)
{
	if ((theValue == undefined) || (theValue == null)) { theValue = ''; }
	if ((stepIn == undefined) || (stepIn == null)) { stepIn = false; }
	var newPtr = this.__newXMLNode(this);
	var newNode = this.__xmlNodeArray[newPtr]; 
	newNode.tag = theTag;
	newNode.data = theValue;
	newNode.parent = this.currentPtr;
	var parentNode = this.__xmlNodeArray[this.currentPtr];
	parentNode.addChildPtr(newPtr);
	if (stepIn) { this.currentPtr = newPtr; }
	return this.currentNode();
}
xmlHandler.prototype.attributeValue = function(attrName)
{
	var tempNode = this.__xmlNodeArray[this.currentPtr];
	return tempNode.attributeValue(attrName);
}
xmlHandler.prototype.bindRadioToTag = function(theElem, theTag)
{
	theTag = theTag.toLowerCase();
	this.bindings[theTag] = theElem;
	this.bindingType[theTag] = 'radio';
}
xmlHandler.prototype.bindTextToTag = function(theElem, theTag)
{
	theTag = theTag.toLowerCase();
	this.bindings[theTag] = theElem;
	this.bindingType[theTag] = 'text';
}
xmlHandler.prototype.boundUpdate = function(theElem, groupName)
{
	var theID = theElem.getAttribute('id');
	var theNode = this.reverseBindings[theID];
	if (theNode) { theNode.updateData = theElem.value; } 
	if (this.masterDirty) { this.masterDirty.dirty(groupName); }
	return true;
}
xmlHandler.prototype.childData = function(idx)
{
	var result = '';
	var tempNode = this.childNode(idx);
	if (tempNode) { return tempNode.data };
}
xmlHandler.prototype.childDataNamed = function(tagName)
{
	var result = '';
	var tempNode = this.childNodeNamed(tagName);
	if (tempNode) { return tempNode.data };
}
xmlHandler.prototype.childNode = function(nodeIDX)
{
        var retNode = null;
        if (!isNaN(nodeIDX))
        {
                var tempNode = this.__xmlNodeArray[this.currentPtr];
                var ptr = tempNode.children[nodeIDX];
		if ((nodeIDX >= 0) && (nodeIDX < tempNode.children.length)) { retNode = this.__xmlNodeArray[ptr]; }
        }
        return retNode;
}
xmlHandler.prototype.childNodeNamed = function(nodeIDX)
{
	var retNode = null;
	if (isNaN(nodeIDX))
	{
		var holdPtr = this.currentPtr;
		if (this.stepInto(nodeIDX)) {
			var ptr = this.currentPtr;
			retNode = this.__xmlNodeArray[ptr];
			// Put me back where I was...
			this.currentPtr = holdPtr;
		}
		// if not, then I am where I started anyway...
	}
	return retNode;
}
xmlHandler.prototype.childPtr = function(nodeIDX) 
{
	var tempNode = this.__xmlNodeArray[this.currentPtr];
    return tempNode.children[nodeIDX];
}
xmlHandler.prototype.childTag = function(idx)
{
	var result = '';
	var tempNode = this.childNode(idx);
	if (tempNode) { return tempNode.tag };
}
xmlHandler.prototype.clear = function() 
{
        this.__xmlNodeArray = new Array();
        this.work = new String(); // This will be used by all nodes during the import
        ptr = this.__newXMLNode(this);
        this.rootPtr = ptr;
        this.currentPtr = ptr;
        tempObj = this.__xmlNodeArray[ptr];
        tempObj.parent = ptr; // SPECIAL CASE - root is it's own parent
        this.originalXML = new String();
        this.forAjaxMS = false;
}
xmlHandler.prototype.clearAll = function()
{
	this.bindings = new Array();
	this.bindingType = new Array();
	this.reverseBindings = new Array();
	this.masterDirty = null;
	this.indentTemplate = "                                                                      ";
	this.onSortbyOther = null;
	this.clear();
}
xmlHandler.prototype.clearBound = function(optionalPath)
{
	if (optionalPath) {
		this.moveToRoot();
		this.stepDeep(optionalPath);
	}
	var tempNode = this.__xmlNodeArray[this.currentPtr];
	var max = tempNode.children.length;
	for (var i=0; i<max; i++) 
	{
		var thisTag = tempNode.childTag(i);
		thisElem = this.bindings[thisTag];
		if (this.bindingType[thisTag] == 'radio') {thisElem[0].checked = true; }
		else if (this.bindingType[thisTag] == 'check') { } // What do we do here?
		else {thisElem.value = ''; }
	}
}
xmlHandler.prototype.currentChildCount = function() { return this.__xmlNodeArray[this.currentPtr].children.length; }
xmlHandler.prototype.currentData = function() { return this.__xmlNodeArray[this.currentPtr].data; }
xmlHandler.prototype.currentNode = function() { return this.__xmlNodeArray[this.currentPtr]; }
xmlHandler.prototype.currentNodeID = function() { return this.currentPtr; }
xmlHandler.prototype.currentTag = function() { return this.__xmlNodeArray[this.currentPtr].tag; }
xmlHandler.prototype.deleteCurrentChildren = function() 
{ 
	while (this.currentChildCount()) 
	{ 
		this.deleteChildNode(0); 
	} 
}
xmlHandler.prototype.deleteChildNode = function(childPtr)
{
	var tempNode = this.__xmlNodeArray[this.currentPtr];
	var childArrPtr = tempNode.children[childPtr];
	tempNode.children.splice(childPtr, 1);
	this.__xmlNodeArray[childArrPtr] = null; // I won't get the pointer back, but the memory will be freed...
}
xmlHandler.prototype.evaluateBindings = function(optionalPath)
{
	if (optionalPath) {
		this.moveToRoot();
		this.stepDeep(optionalPath);
	}
	// Look at the tags for all the current children. 
	// if the tag exists in the hash list this.bindings, then
	// the element associated with that item is updated.
	var tempNode = this.__xmlNodeArray[this.currentPtr];
	var max = tempNode.children.length;
	for (var i=0; i<max; i++) 
	{
		var ptr = tempNode.children[i];
		var thisNode = this.__xmlNodeArray[ptr];
		thisNode.updateData = thisNode.data;
		var thisTag = thisNode.tag;
		if (this.bindings[thisTag])
		{
			thisElem = this.bindings[thisTag];
			if (this.bindingType[thisTag] == 'radio')
			{
				atLeastOne = false;
				for (var i=0; i<thisElem.length; i++)
				{
					this.reverseBindings[thisElem[i].getAttribute('id')] = thisNode;
					if ( (thisElem[i]) && (thisElem[i].value === thisNode.data) )
					{
						atLeastOne = true;
						thisElem[i].checked = true;
					} else { thisElem[i].checked = false; }
				}
				if (!atLeastOne)
				{
					thisElem[0].checked = true;
				}
			} else if (this.bindingType[thisTag] == 'check')
			{
			} else { 
				thisElem.value = thisNode.data;
				this.reverseBindings[thisElem.getAttribute('id')] = thisNode;
			}
		}
	}
}
xmlHandler.prototype.firstNode = function()
{
	var tempNode = this.__xmlNodeArray[this.currentPtr];
	var parentNode = this.__xmlNodeArray[tempNode.parent];
	this.currentPtr = parentNode.children[0];
}
xmlHandler.prototype.importXML = function(inputXML)
{
	this.clear();
	this.searchPtr = 0;
	this.originalXML = inputXML;
	this.work = inputXML;

	// Clean the XML first...
	if (this.work.indexOf('ajaxms="1"') == -1)
	{
	    this.work = this.work.replace(/>[\s]*</g, "><"); // Kill all whitespace between tags
	    this.work = this.work.replace(/<\?.*\?>/g, ""); // Kill all comments
	    this.work = this.work.replace(/<[\s]*/g, "<"); // Kill all whitespace between LT and next character
	}

	tempObj = this.__xmlNodeArray[0];
	tempObj.importNode();
}
xmlHandler.prototype.isFirstNode = function()
{
	// Evalute whether the current node is the <zeroth> in its parent's child list
	var tempNode = this.__xmlNodeArray[this.currentPtr];
	tempNode.flagged = true;
	theParent = tempNode.parent;
	parentNode = this.__xmlNodeArray[theParent];
	for (var i=0; i<parentNode.children.length; i++)
	{
		var childNode = this.__xmlNodeArray[parentNode.children[i]];
		if (childNode.flagged) 
		{
			// Found it!
			childNode.flagged = false;
			return (i == 0);
		}
	}
	// I should never be here...
	tempNode.flagged = false;
	return false;
}
xmlHandler.prototype.isLastNode = function()
{
        // Evalute whether the current node is the <zeroth> in its parent's child list
        var tempNode = this.__xmlNodeArray[this.currentPtr];
        tempNode.flagged = true;
        theParent = tempNode.parent;
        parentNode = this.__xmlNodeArray[theParent];
        for (var i=0; i<parentNode.children.length; i++)
        {
                var childNode = this.__xmlNodeArray[parentNode.children[i]];
                if (childNode.flagged)
                {
                        // Found it!
                        childNode.flagged = false;
                        return (i == parentNode.children.length - 1);
                }
        }
        // I should never be here...
        tempNode.flagged = false;
        return false;
}
xmlHandler.prototype.lastNode = function()
{
        var tempNode = this.__xmlNodeArray[this.currentPtr];
        var parentNode = this.__xmlNodeArray[tempNode.parent];
        this.currentPtr = parentNode.children[parentNode.children.length - 1];
}
xmlHandler.prototype.makeUpdatesPermanent = function()
{
	var thisNode = this.__xmlNodeArray[this.currentPtr];
	for (var i=0; i<thisNode.children.length; i++)
	{
		var childNode = this.childNode(i);
		childNode.data = childNode.updateData;
	}
}
xmlHandler.prototype.moveToRoot = function() { this.currentPtr = this.rootPtr; }
xmlHandler.prototype.nextNode = function()
{
        // Evalute whether the current node is the <zeroth> in its parent's child list
        var tempNode = this.__xmlNodeArray[this.currentPtr];
        tempNode.flagged = true;
        theParent = tempNode.parent;
        parentNode = this.__xmlNodeArray[theParent];
        for (var i=0; i<parentNode.children.length; i++)
        {
		var ptr = parentNode.children[i];
                var childNode = this.__xmlNodeArray[ptr];
                if (childNode.flagged)
                {
                        // Found it!
                        childNode.flagged = false;
			if (i < parentNode.children.length - 1)
			{
				this.currentPtr = parentNode.children[i + 1];
				return "";
			}
                }
        }
        // I should never be here...
        tempNode.flagged = false;
        return false;
}
xmlHandler.prototype.outputAllXML = function(makePretty)
{
	// This will, be default, compress the XML on the way out - if the user
	// wants "pretty" xml then TRUE must be passed in here...
	this.forAjaxMS = !makePretty;
	var holdPtr = this.currentPtr;
	this.moveToRoot();
	return this.outputCurrentXML(makePretty);
	this.currentPtr = holdPtr;
}
xmlHandler.prototype.outputCurrentXML = function(makePretty)
{
	// This will, be default, compress the XML on the way out - if the user
	// wants "pretty" xml then TRUE must be passed in here...
	this.forAjaxMS = !makePretty;
	var output = new Array();
	var theNode = this.__xmlNodeArray[this.currentPtr];
	if (this.forAjaxMS) { theNode.updateAttribute('ajaxms', '1'); }
//	theNode.updateAttribute('ajaxms', (this.forAjaxMS) ? '1' : '0');
	theNode.buildXML(output, 0);
	if (this.forAjaxMS) { return output.join(''); }
	else { return output.join('\n'); }
	output = null;
}
xmlHandler.prototype.prevNode = function()
{
        // Evalute whether the current node is the <zeroth> in its parent's child list
        var tempNode = this.__xmlNodeArray[this.currentPtr];
        tempNode.flagged = true;
        theParent = tempNode.parent;
        parentNode = this.__xmlNodeArray[theParent];
        for (var i=0; i<parentNode.children.length; i++)
        {
                var childNode = this.__xmlNodeArray[parentNode.children[i]];
                if (childNode.flagged)
                {
                        // Found it!
                        childNode.flagged = false;
                        if (i > 0)
                        {
				this.currentPtr = parentNode.children[i - 1];
                                return "";
                        }
                }
        }
        // I should never be here...
        tempNode.flagged = false;
        return false;
}
xmlHandler.prototype.returnToID = function(theID) { this.currentPtr = theID; }
xmlHandler.prototype.sortByTag = function() { this.__bubbleSort(0); }
xmlHandler.prototype.sortByData = function() { this.__bubbleSort(1); }
xmlHandler.prototype.sortByAttribute = function(attrName) { this.__bubbleSort(2, attrName); }
xmlHandler.prototype.sortByOther = function()
{
	if (!this.onSortByOther) { return false; }
	this.__bubbleSort(3);
}
xmlHandler.prototype.stepDeep = function(tagPath)
{
	var holdPtr = this.currentPtr;
	tagPath = tagPath.toLowerCase();
	var tempArr = tagPath.split('|');
	for (var i=0; i<tempArr.length; i++)
	{
		if (!this.stepInto(tempArr[i])) 
		{
			this.currentPtr = holdPtr;
			return false;
		}	
	}
	// If I am here then I succeeded!
	return true;
}
xmlHandler.prototype.stepInto = function(tagName)
{
	var tagName = tagName.toLowerCase();
	var tempNode = this.__xmlNodeArray[this.currentPtr];
	for(var i=0; i<tempNode.children.length; i++) {
		var childID = tempNode.children[i];
		var thisNode = this.__xmlNodeArray[childID];
		if ((thisNode.tag === tagName) && (thisNode.tag.length == tagName.length)) {
			this.currentPtr = childID;
			return true;
		}
	}
	return false;
}
xmlHandler.prototype.stepIntoIDX = function(idx)
{
        var tempNode = this.__xmlNodeArray[this.currentPtr];
        if ((idx >= 0) && (idx < tempNode.children.length)) 
	{ 
		this.currentPtr = tempNode.children[idx]; 
		return true; 
	} else { return false; }
}
xmlHandler.prototype.stepOut = function() { this.currentPtr = this.__xmlNodeArray[this.currentPtr].parent; }
xmlHandler.prototype.updateAttribute = function(theName, theValue)
{
	var tempNode = this.__xmlNodeArray[this.currentPtr];
	tempNode.updateAttribute(theName, theValue);
}
xmlHandler.prototype.updateData = function(theData)
{
	var tempNode = this.__xmlNodeArray[this.currentPtr];
	tempNode.data = theData;
}
xmlHandler.prototype.updateTag = function(theName)
{
	var tempNode = this.__xmlNodeArray[this.currentPtr];
	tempNode.tag = theName;
}


// ----------------------------------------------------------- //
//                       Table Manager                         //
// ----------------------------------------------------------- //
// This object is used to manage the creation of tables - not that
// you'd really need it if it wasn't for IE's quirkiness...
function tableManager(cellPadding, cellSpacing, border, width) 
{ 
	this.clear();
	if (cellPadding) { this.cellPadding = cellPadding; }
	if (cellSpacing) { this.cellSpacing = cellSpacing; }
	if (border) { this.border = border; }
	if (width) { this.width = width; }
}
tableManager.prototype.clear = function()
{
	this.cellPadding = 0;
	this.cellSpacing = 0;
	this.border = 0;
	this.width = '100%';
	this.sizingGraphicURL = '/graphics/dot_clear.gif';
	
	this.currentRow = null;
	this.currentCell = null;
	
	this.isIE = ((document.all) && (document.getElementById));
	if (this.isIE) {
		this.mainNode = document.createElement('TBODY');
	} else {
		this.mainNode = document.createElement('TABLE');
	}
}
tableManager.prototype.addTableTo = function(newOwner)
{
	if (this.isIE)
	{
		var tableNode = document.createElement('TABLE');
		this.attribute(tableNode, 'cellpadding', this.cellPadding, 'cellspacing', this.cellSpacing, 'border', this.border, 'width', this.width);
		tableNode.appendChild(this.mainNode);
		newOwner.appendChild(tableNode);
		return tableNode;
	} else {
		this.attribute(this.mainNode, 'cellpadding', this.cellPadding, 'cellspacing', this.cellSpacing, 'border', this.border, 'width', this.width);
		newOwner.appendChild(this.mainNode);
		return this.mainNode;
	}
}
tableManager.prototype.attribute = function(theNode)
{
	for(var i=1; i<arguments.length; i+=2) 
	{ 
		if (this.isIE) { 
			// NN2 attribute the object the old way...
			switch(arguments[i].toLowerCase())
			{
				case 'align' : theNode.align = arguments[i+1]; break;
				case 'bgcolor' : theNode.bgColor = arguments[i+1]; break;
				case 'border' : theNode.border = arguments[i+1]; break;
				case 'cellpadding' : theNode.cellPadding = arguments[i+1]; break;
				case 'cellspacing' : theNode.cellSpacing = arguments[i+1]; break;
				case 'class' : theNode.className = arguments[i+1]; break;
				case 'colspan' : theNode.colSpan = arguments[i+1]; break;
				case 'enctype' : theNode.encoding = arguments[i+1]; break;
				case 'height' : theNode.height = arguments[i+1]; break;
				case 'href' : theNode.setAttribute('href', arguments[i+1]); break;
				case 'id' : theNode.id = arguments[i+1]; break;
				case 'method' : theNode.method = arguments[i+1]; break;
				case 'name' : theNode.name = arguments[i+1]; break;
				case 'nowrap' : theNode.noWrap = 'nowrap'; break;
				case 'onclick' : theNode['onclick'] = new Function(arguments[i+1]); break;
				case 'onblur' : theNode['onblur'] = new Function(arguments[i+1]); break;
				case 'onchange' : theNode['onchange'] = new Function(arguments[i+1]); break;
				case 'onclick' : theNode['onclick'] = new Function(arguments[i+1]); break;
				case 'ondblclick' : theNode['ondblclick'] = new Function(arguments[i+1]); break;
				case 'onfocus' : theNode['onfocus'] = new Function(arguments[i+1]); break;
				case 'onkeypress' : theNode['onkeypress'] = new Function(arguments[i+1]); break;
				case 'onmouseover' : theNode['onmouseover'] = new Function(arguments[i+1]); break;
				case 'onmouseout' : theNode['onmouseout'] = new Function(arguments[i+1]); break;
				case 'rowspan' : theNode.rowSpan = arguments[i+1]; break;
				case 'size' : theNode.size = arguments[i+1]; break;
				case 'src' : theNode.src = arguments[i+1]; break;
				case 'type' : theNode.setAttribute('type', arguments[i+1]); break;
				case 'valign' : theNode.vAlign = arguments[i+1]; break;
				case 'value' : theNode.value = arguments[i+1]; break;
				case 'visibility' : theNode.visibility = arguments[i+1]; break;
				case 'width' : theNode.width = arguments[i+1]; break;
			}
		} else { theNode.setAttribute(arguments[i], arguments[i+1]); }
	}
	return theNode;
}
tableManager.prototype.newCell = function(rowNode)
{
	if (!rowNode) { rowNode = this.currentRow; }
	if (!rowNode) { rowNode = this.newRow(); }
	this.currentCell = document.createElement('TD');
	rowNode.appendChild(this.currentCell);
	return this.currentCell;
}
tableManager.prototype.newCellContent = function(cellContent, rowNode)
{
	if (!rowNode) { rowNode = this.currentRow; }
	this.newCell();
	this.currentCell.appendChild(cellContent);
	return this.currentCell;
}
tableManager.prototype.newFirstCell = function(content)
{
	this.newRow();
	this.newCell();
	if (content) { this.currentCell.appendChild(content); }
	return this.currentCell;
}
tableManager.prototype.newRow = function() 
{ 
	this.currentRow = document.createElement('TR');
	this.mainNode.appendChild(this.currentRow);
	return this.currentRow; 
}
tableManager.prototype.newTextCell = function(theText, theClass)
{
	return this.textCell(theText, theClass);
}
tableManager.prototype.sizingCell = function(height, width, colSpan) { return this._sizingCell(this.currentRow, height, width, colSpan); }
tableManager.prototype._sizingCell = function(theRow, height, width, colSpan)
{
	if (!theRow) { theRow = this.newRow(); }
	var thisContent = document.createElement('img');
	thisContent.src = this.sizingGraphicURL;
	this.attribute(thisContent, 'height', height, 'width', width);
	this.newCell(theRow).appendChild(thisContent);

	if (colSpan) { this.attribute(this.currentCell, 'colSpan', colSpan); }
	return this.currentCell;
}
tableManager.prototype.sizingRow = function(height, colSpan)
{
	this.newRow();
	this.sizingCell(height, 1, colSpan);
	return this.currentCell;
}
tableManager.prototype.textCell = function(theText, textClass)
{
	this.newCell();
	if (textClass) { this.attribute(this.currentCell, 'class', textClass); }
	this.currentCell.appendChild(document.createTextNode(theText));
	return this.currentCell;
}






// ----------------------------------------------------------- //
//                       Miscellaneous                         //
// ----------------------------------------------------------- //

function trim(theStr)
{
 	return theStr.replace(/^\s+/g, '').replace(/\s+$/g, '');
}
function ucwords(theStr)
{
     return theStr.toLowerCase().replace(/\w+/g,function(s) { return s.charAt(0).toUpperCase() + s.substr(1); } );
}
function upper(theStr) { return theStr.toUpperCase(); }
function lower(theStr) { return theStr.toLowerCase(); }
String.prototype.trim = function()
{
 	return this.replace(/^\s+/g, '').replace(/\s+$/g, '');
}
String.prototype.UCWords = function()
{
     return this.toLowerCase().replace(/\w+/g,function(s) { return s.charAt(0).toUpperCase() + s.substr(1); } );
}
function addEventStr(theObj, functionStr)
{
	if ((document.all) && (document.getElementById))
	{
   		theObj["onclick"] = new Function(functionStr);
	} else {
		theObj.setAttribute('onClick', functionStr); 
	}
}
function browserWindowHeight()
{
	if ((document.all) && (document.getElementById))
	{
		return document.body.offsetHeight;
	} else {
		return window.innerHeight;
	}
}
function browserWindowWidth()
{
	if ((document.all) && (document.getElementById))
	{
		return document.body.offsetWidth;
	} else {
		return window.innerWidth;
	}
}
function chr(CharCode)
{
	return String.fromCharCode(CharCode);
}
function removeAllChildren(theNode)
{ 
	while (theNode.childNodes[0]) { theNode.removeChild(theNode.childNodes[0]); } 
}
function getSelectValue(theSelect)
{
	var temp = document.getElementById(theSelect);
	var idx = temp.selectedIndex;
	return temp.options[idx].value;
}
function selectSelectValue(theSelect, theValue)
{
	// Very simply - select the option in the list that has a <value> matching what was passed...
	theValue = theValue.toLowerCase();
	var max = theSelect.options.length;
	for (var i=0; i<max; i++)
	{
		if (theValue == theSelect.options[i].value.toLowerCase())
		{
			theSelect.selectedIndex = i;
			return true;
		}
	}
	return false;
}
function highlightRow(theRow, theRowColor, theTextColor)
{
    if (typeof(document.getElementsByTagName) != 'undefined')
        { var theCells = theRow.getElementsByTagName('td'); }
    else if (typeof(theRow.cells) != 'undefined')
        { var theCells = theRow.cells; }
    else { return false; }

    var rowCellsCnt = theCells.length;
    for (var c=0; c<rowCellsCnt; c++)
    {
        theCells[c].style.backgroundColor=theRowColor;
        if (theTextColor) { theCells[c].style.color=theTextColor; }
        theCells[c].style.cursor='pointer';
    }
    return true;
}
function highlightCell(theCell, theColor, theTextColor)
{
    theCell.style.backgroundColor=theColor;
    if (theTextColor) { theCell.style.color=theTextColor; }
    theCell.style.cursor='pointer';
    return true;
}
function replaceText(text, textarea)
{
	// Attempt to create a text range (IE).
	if (typeof(textarea.caretPos) != "undefined" && textarea.createTextRange)
	{
		var caretPos = textarea.caretPos;

		caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text + ' ' : text;
		caretPos.select();
	}
	// Mozilla text range replace.
	else if (typeof(textarea.selectionStart) != "undefined")
	{
		var begin = textarea.value.substr(0, textarea.selectionStart);
		var end = textarea.value.substr(textarea.selectionEnd);
		var scrollPos = textarea.scrollTop;

		textarea.value = begin + text + end;

		if (textarea.setSelectionRange)
		{
			textarea.focus();
			textarea.setSelectionRange(begin.length + text.length, begin.length + text.length);
		}
		textarea.scrollTop = scrollPos;
	}
	// Just put it on the end.
	else
	{
		textarea.value += text;
		textarea.focus(textarea.value.length - 1);
	}
}
function surroundText(text1, text2, textarea)
{
	// Can a text range be created?
	if (typeof(textarea.caretPos) != "undefined" && textarea.createTextRange)
	{
		var caretPos = textarea.caretPos;

		caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text1 + caretPos.text + text2 + ' ' : text1 + caretPos.text + text2;
		caretPos.select();
	}
	// Mozilla text range wrap.
	else if (typeof(textarea.selectionStart) != "undefined")
	{
		var begin = textarea.value.substr(0, textarea.selectionStart);
		var selection = textarea.value.substr(textarea.selectionStart, textarea.selectionEnd - textarea.selectionStart);
		var end = textarea.value.substr(textarea.selectionEnd);
		var newCursorPos = textarea.selectionStart;
		var scrollPos = textarea.scrollTop;

		textarea.value = begin + text1 + selection + text2 + end;

		if (textarea.setSelectionRange)
		{
			if (selection.length == 0)
				textarea.setSelectionRange(newCursorPos + text1.length, newCursorPos + text1.length);
			else
				textarea.setSelectionRange(newCursorPos, newCursorPos + text1.length + selection.length + text2.length);
			textarea.focus();
		}
		textarea.scrollTop = scrollPos;
	}
	// Just put them on the end, then.
	else
	{
		textarea.value += text1 + text2;
		textarea.focus(textarea.value.length - 1);
	}
}
function getElementLeft(obj)
{
    var curleft = 0;
    if(obj.offsetParent)
    {
        while(true)
        {
                curleft += obj.offsetLeft;
                if(!obj.offsetParent) { break; }
                obj = obj.offsetParent;
        }
    } else if(obj.x) { curleft += obj.x; }
    return curleft;
}
function getElementTop(obj)
{
    var curtop = 0;
    if(obj.offsetParent)
    {
        while(true)
        {
                curtop += obj.offsetTop;
                if(!obj.offsetParent) { break; }
                obj = obj.offsetParent;
        }
    } else if(obj.y) { curtop += obj.y; }
    return curtop;
}

function validEmailAddress(inStr) { return (inStr.match(/^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/) != null); }

function URLEncode(plaintext)
{
	var SAFECHARS = "0123456789" +					// Numeric
					"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +	// Alphabetic
					"abcdefghijklmnopqrstuvwxyz" +
					"-_.!~*'()";					// RFC2396 Mark characters
	var HEX = "0123456789ABCDEF";

	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;
}
function dumpArray(arr, level) 
{
	var dumped_text = "";
	if (!level) { level = 0; }

	// The padding given at the beginning of the line.
	var level_padding = "";
	for (var j=0; j<level; j++) { level_padding += '   '; }

	if (typeof(arr) == 'object') 
	{ 
		// Array, Hashes & Objects
		for (var item in arr) 
		{
			var value = arr[item];
 
			if (typeof(value) == 'object') 
			{ 
				// If it is an array...
				dumped_text += level_padding + "['" + item + "']\n";
				dumped_text += dumpArray(value,level+1);
			} else {
				vSurround = (typeof(value) == 'string') ? "'" : '';
				nSurround = (item.match(/^[0-9]{1,10}$/)) ? '' : "'";
				dumped_text += level_padding + "[" + nSurround + item + nSurround + "] => " + vSurround + value + vSurround + "\n";
			}
		}
	} else { 
		// Stings, Chars & Numbers etc.
		dumped_text = "(" + typeof(arr) + ") " + arr + "\n";
	}
	
	return dumped_text;
}
function json_encode(inVal) { return _json_encode(inVal).join(''); }
function _json_encode(inVal, out) 
{
	out = out || new Array();
	var undef; // undefined
	
	switch (typeof inVal)
	{
		case 'object':
			if (!inVal) 
			{
				out.push('null');
			} else {
				if (inVal.constructor == Array) 
				{
					// Need to make a decision... if theres any associative elements of the array
					// then I will block the whole thing as an object {} otherwise, I'll block it 
					// as a  normal array []
					var testVal = inVal.length;
					var compVal = 0;
					for (var key in inVal) compVal++;
					if (testVal != compVal)
					{
						// Associative
						out.push('{');
						i = 0;
						for (var key in inVal)
						{
							if (i++ > 0) out.push(',\n');
							out.push('"');
							out.push(key);
							out.push('":');
							_json_encode(inVal[key], out);
						}
						out.push('}');
					} else {
						// Standard array...
						out.push('[');					
						for (var i = 0; i < inVal.length; ++i) 
						{
							if (i > 0) out.push(',\n');
							_json_encode(inVal[i], out);
						}
						out.push(']');
					}
					
				} else if (typeof inVal.toString != 'undefined') {
					out.push('{');
					var first = true;
					for (var i in inVal) 
					{
						var curr = out.length; // Record position to allow undo when arg[i] is undefined.
						if (!first) out.push(',\n');
						_json_encode(i, out);
						out.push(':');                    
						_json_encode(inVal[i], out);
						if (out[out.length - 1] == undef)
						{
							out.splice(curr, out.length - curr);
						} else {
							first = false;
						}
					}
					out.push('}');
				}
			}
			return out;

		case 'unknown':
		case 'undefined':
		case 'function':
			out.push(undef);
			return out;
			
		case 'string':
	        out.push('"');
	        out.push(inVal.replace(/(["\\])/g, '\\$1').replace(/\r/g, '').replace(/\n/g, '\\n'));
	        out.push('"');
	        return out;
	        
		default:
			out.push(String(inVal));
			return out;
	}
}