/**
 * $Id: xdom.js,v 1.69 2010/09/06 04:34:35 cvs Exp $
 *
 * @author Frederick J Richart
 * @copyright Copyright © 2006-2010, Useful Media Planet, LLC, All rights reserved.
 */
function XdomAjaxPurchaseWidget(id, formID, requestURL, extension, callback, varName) {
	this.id = id;
	this.formID = formID;
	this.requestURL = requestURL;
	this.extension = extension;
	this.callback = callback;
	this.varName = varName;
	this.cartSlotID = "";
	Utilities.onload(this);
} 
XdomAjaxPurchaseWidget.prototype = {
	onPageLoad : function() {
		var t = this, f, c;
		t.container = xdom_getElementById(t.id);
		t.form = xdom_getElementById(t.formID);
		t.busy = new BusyIndicator();
		if (t.cartSlotID) {
			if (t.cartSlot = xdom_getElementById(t.cartSlotID)) {
				c = new CookieUtil(document, 'SitePurchaseData');
				if (c.read()) t.sendRequest('cart_check', t.varName);
			}
		}
		t.checkingPurchase = false;
	},
	cartButton : function(id) {
		this.cartSlotID = id;
	},
	sendRequest : function(control, value, form) {
		var xdomAjax,
			post = 'callback=' + this.callback + '&controlID=' + control + "&controlValue=" + encodeURIComponent(value) + '&parameter=' + this.varName;
		if (form) post += '&' + xdom.formPost(form);
		xdomAjax = new XdomAjax(null, "VerifiedControl", this.requestURL);
		xdomAjax.requestor = this;
		xdomAjax.xdomExtension = this.extension;
		xdomAjax.sendRequest(null, post);
		return xdomAjax;
	},
	serverResponse : function(xdomAjax, response) {
		var t = this, e, f, ok = xdomAjax.err == '0', message = xdomAjax.errmsg, result, c;
		t.busy.hide();
		// Load container with possible new content 
		if (e = xdom_GetFirstElementWithTagName(response, 'replace_content')) {
			t.container.innerHTML = decodeURIComponent(decodeURIComponent(xdom_GetData(e)));
		}
		
		// Report any error messages with a popup
		if (!ok) {
			if (typeof console != 'undefined') console.error('Server Response Error: ' + message);
			alert(message);
			return false;
		}
		
		// Check for polling results
		if (e = xdom_GetFirstElementWithTagName(response, 'payment_status')) {
			result = e.getAttribute("value");
			if (result == 'pending') {
				t.checkingPurchase = false;
				return true;
			}
			// Stop polling & close popup window
			t.popupFormClose();
			f = xdom_getElementById(t.formID);
			t.sendRequest('summary_pay', result, f);
		}
		
		// Check for cart button content
		if (e = xdom_GetFirstElementWithTagName(response, 'cart_button')) {
			if (t.cartSlot) t.cartSlot.innerHTML = decodeURIComponent(decodeURIComponent(xdom_GetData(e)));
		}
		return true;
	},
	/**
	 * Send is invoked directly by the Next and Back buttons on the forms.
	 * 
	 * @param e
	 * @param state
	 * @param action
	 * @return
	 */
	Send : function(e, state, action) {
		var t = this, form = e.form;
		t.busy.show();
		t.sendRequest(state, action, form);
	},
	/**
	 * The popupFormClose method is intended to be invoked from a button on the
	 * main form page that can abort the bank payment window.  It is also used
	 * to shutdown the timers and, as such can be used as a general close the
	 * popup method.
	 * 
	 * @param poupup
	 * @return
	 */
	popupFormClose : function() {
		var t = this;
		window.clearInterval(t.popupCheckInterval);
		window.clearInterval(t.checkPurchaseInterval);
		window.clearInterval(t.timoutInterval);
		t.checkingPurchase = false;
		t.timersActive = false;
		// Yet another fix to make IE work.
		try {
			if (t.popup) t.popup.close();
		} catch(ex) {
			return;
		}
	},
	popupFormSend : function(popup, post) {
		// Must return post even if unmodified
		return post;
	},
	/**
	 * The checkPurchase method is invoked by the interval timer every 2 seconds
	 * to use AJAX to check on the status of the current order represented by 
	 * orderID.  The timing is a tradeoff between quick response once the bank
	 * processing indicates a go vs hammering the server.
	 * 
	 * @param orderID
	 * @return
	 */
	checkPurchase : function(orderID) {
		var t = this;
		// Don't stack these requests if server is slow.
		if (t.checkingPurchase) {
			return;
		}
		xdomAjax = t.sendRequest('payment_status', orderID);
		t.checkingPurchase = true;
	},
	/**
	 * The popupFormCheck method is invoked by the interval timer multiple times
	 * per second.  It is used to detect if the user has closed the popup window
	 * and thus aborted the payment.
	 * 
	 * @param orderID
	 * @return
	 */
	popupFormCheck : function() {
		var t = this,f;
		if (!t.popup.closed) return;
		// Window has been closed - abort
		t.popupFormClose();
		f = xdom_getElementById(t.formID);
		t.sendRequest('summary_pay', 'abort', f);
	},
	/**
	 * The timeoutPurchase method is invoked by the interval timer after 20 minutes
	 * of inactivity with the popup payment window. A user that abandons
	 * the payment window will cause the checkPurchase method to  keep pecking away 
	 * at the server literally thousands of times. At a rate of a check every two
	 * seconds it would hit the server 1800 times.  Twenty minutes is a reasonable 
	 * amount of time for a user to enter a credit card number and reduces the hit
	 * count to 600.
	 * 
	 * @param orderID
	 * @return
	 */
	timeoutPurchase : function() {
		var t = this, f;
		if (t.popup.closed) return;
		t.popupFormClose();
		f = xdom_getElementById(t.formID);
		t.sendRequest('summary_pay', 'timeout', f);		
	},
	/**
	 * This is invoked by the individual forms embedded in each payment plugin button.
	 * They are not the form containing all the variables used for the payment processing
	 * widget.  The form parameter represents the bank form that will be submitted
	 * in the popup window to the bank.
	 * 
	 * @param form
	 * @param width
	 * @param height
	 * @return
	 */
	onSubmitPopup : function(form, width, height) {
		var t = this, f, orderID;
		if (!width) width = '500';
		if (!height) height = '400';
		this.popup = window.open('', 'xdom_purchase_popup', 'resizable,scrollbars,width=' + width + ',height=' + height);
		form.target = "xdom_purchase_popup";
		
		/* 
		 * Lookup the form object with our state information. We need to always 
		 * find this because each time the wizard changes pages, the previous
		 * version of this form is gone and replace with a new one. 
		 */ 
		f = xdom_getElementById(t.formID);
		orderID = f.o.value;
		// We seem to be getting an extra pop on the popupFormCheck after killing the timer.
		t.timersActive = true;
		t.popupCheckInterval = window.setInterval(function(){if (t.timersActive) t.popupFormCheck()}, 200);
		t.checkPurchaseInterval = window.setInterval(function(){if (t.timersActive) t.checkPurchase(orderID)}, 2000);
		t.timoutInterval = window.setInterval(function(){if (t.timersActive) t.timeoutPurchase()}, 1200000);
		return true;
	}
}
function BusyIndicator() {
	var img, t = this;
	t.hidden = true;
	t.image = img = document.createElement('img');
	img.setAttribute('src', '/images/busy.gif');
	img.style.position = 'absolute';
	img.style.top = '-200px';
	img.style.left = '-200px';
	img.style.zIndex = '50000';
	document.body.appendChild(img);
	t.othermousemove = document.onmousemove;
	document.onmousemove = function(e){return t.mouse(e)};
}
BusyIndicator.prototype = {
	show : function() {
		var t = this;
		t.hidden = false;
		t.image.style.top = (t.y-15) + "px";
		t.image.style.left = (t.x-15) + "px";
	}, 
	hide : function() {
		var t = this;
		t.hidden = true;
		t.image.style.top = "-200px";
		t.image.style.left = "-200px";
	},
	mouse : function(e) {
		var t = this;
		if (t.othermousemove) t.othermousemove(e);
		if (document.all) {
			// IE is such a piece of shit
	        t.x = window.event.clientX + ((document.documentElement && document.documentElement.scrollLeft) ? document.documentElement.scrollLeft : document.body.scrollLeft);
	        t.y = window.event.clientY + ((document.documentElement && document.documentElement.scrollTop) ? document.documentElement.scrollTop : document.body.scrollTop);
	    } else {
	        t.x = e.pageX;
	        t.y = e.pageY;
	    }
		if (t.hidden) return;
		this.image.style.top = (t.y-15) + "px";
		this.image.style.left = (t.x-15) + "px";
	}
}
function SecureLogin(formID, requestURL, md5URL) {
	this.formID = formID;
	this.requestURL = requestURL;
	this.md5URL = md5URL;
	this.md5Loaded = false;
	this.form = null;
	this.popupForm = null;
	Utilities.onload(this);
}
SecureLogin.prototype = {
	onPageLoad : function() {
		var f, t = this;
		f = xdom_getElementById(t.formID);
		if (/^input$/i.test(f.tagName)) {
			t.form = f.form;
			t.ajax = true;
		} else {
			t.form = f;
			t.ajax = false
			t.busy = new BusyIndicator();
		}
		t.form.SecureLogin = t;
	},
	submit : function() {
		var t = this;	
		if (t.busy) t.busy.show();
		if (typeof hex_md5 != "function") {
			// Try loading md5 on the fly		
			Utilities.loadJavascript(null, t.md5URL);
			t.loadTimer = setInterval(function(){
				if (typeof hex_md5 != "function" || t.md5Loaded) return;
				t.md5Loaded = true;
				clearInterval(t.loadTimer);
				t.sendRequest();
			}, 200);
			return false;
		}
		t.sendRequest();
		return false;
	},
	AjaxFormSend : function(popup, varname, action) {
		var t = this;
		t.popupForm = popup;
		t.popupVarname = varname;
		t.popupAction = action;
		t.submit();
	},
	sendRequest : function() {
		var xdomAjax, post = 'callback=MemberAjaxLoginCallback';
		xdomAjax = new XdomAjax(null, "SecureLogin", this.requestURL);
		xdomAjax.requestor = this;
		xdomAjax.sendRequest(null, post);
		return xdomAjax;
	},
	serverResponse : function(xdomAjax, response) {
		var t = this, f = t.form, e = f.elements, ok = xdomAjax.err == '0', challenge = xdomAjax.errmsg, pw, phash, fhash, c, i;
		if (t.busy) t.busy.hide();
		if (ok) {
			for(i=0; i< e.length; i++) {
				c = e[i];
				if (c.type == 'password') break;
			}
			phash = hex_md5(pw = c.value);
			fhash = hex_hmac_md5(challenge, phash);
			c.value = fhash;
			f.password_key.value = '1';
			if (t.popupForm) t.popupForm.DoSend(t.popupVarname, t.popupAction);
			else {
				f.submit();			
				// Chrome and Opera appear to save password after submit so set it back
				if (Utilities.Chrome || Utilities.Opera) f.password.value = pw;
			}
		}
		return true;
	}
}
function xdom_engine() {
	
}
xdom_engine.prototype = {
	alert : function(err) {
		alert('Oops.  We have encountered a javascript problem.  ' + err + "  If this problem persists please contact Fred's Used Websites.");
		return false;
	},
	/**
	 * The formPost method simulates the regular browser post action by going through
	 * the form elements and generating an AJAX post from those values.  Note however 
	 * that this will not submit an input file control properly so those should not be
	 * used.  Select controls with "multiple" attribute set are also not properly
	 * handled at this time.  
	 * 
	 * This is called from the Send method and shouldn't be called externally.
	 */
	formPost : function(form, varname,action) {
		var post = "", value, name, i, c, o, op, sep='', e = form.elements, pos, indexes=Array(), index;
		if (!e) return '';
		for(i=0; i< e.length; i++) {
			c = e[i];
			if (!c.type) continue;
			name = c.name;
			if (c.type != 'select-multiple' && (pos = name.indexOf('[]')) != -1) {
				name = name.substr(0, pos);
				index = indexes[name] = (typeof(indexes[name]) == 'number') ? indexes[name] + 1 : 0;
				name += '[' + index + ']';
			}
			switch (c.type) {
				case 'text':
				case 'textarea':
					value = c.value;
					break;
				case 'checkbox':
				case 'radio':
					if (!c.checked) continue;
					value = c.value;
					break;
				case 'select-multiple':
					for (o = 0; o < c.options.length; o++) {
						op = c.options[o];
						if (op.selected) post += sep + name + '=' + encodeURIComponent(op.value);
						sep = '&';
					}
					// Need to start new loop or post gets extra value
					continue;
				default:
					value = c.value;
			}
			post += sep + name + '=' + encodeURIComponent(value);
			sep = '&';
		}
		if (varname) post+= sep + varname + '=' + encodeURIComponent(action);
		return post;
	}

}
xdom = new xdom_engine();
var xdom_otherOnLoad, xdom_otherOnUnload;
var xdom_onloadRegistry
if (!xdom_onloadRegistry) {
	xdom_onloadRegistry = new Object();
	xdom_onloadRegistry.controlMatches = new Array();
	xdom_onloadRegistry.controlDependencies = new Array();
	xdom_onloadRegistry.requiredControls = new Array();
	xdom_onloadRegistry.validatedControls = new Array();
	xdom_onloadRegistry.validatedMinMaxControls = new Array();
	xdom_onloadRegistry.listSelectors = new Array();
	xdom_onloadRegistry.autoSubmits = new Array();
	xdom_onloadRegistry.dynamicControls = new Array();
	
	var xdom_labelElements;
	if (window.onload) xdom_otherOnLoad = window.onload;
	else xdom_otherOnLoad = false;
	window.onload=xdom_onLoad;
	if (window.onunload) xdom_otherOnUnload = window.onunload;
	else xdom_otherOnUnload = false;
	window.onunload=xdom_onUnload;
}
function xdom_getElementById(id) {
	var v;
	if ((typeof jQuery != 'undefined') && (v = $('#' + id).get(0))) return v;
	return document.getElementById(id);
}
/**
 * This function is executed when the page is unloaded.
 * @param <none>
 */
function xdom_onUnload() {
	// Call the unload function we replaced
	if (xdom_otherOnUnload && xdom_otherOnUnload != xdom_onUnload) xdom_otherOnUnload();
}
/**
 * This function is executed when the page is loaded.  It calls the initialization
 * function for several Xdom features.  Before doing this, Xdom first calls the 
 * onload function (if one existed) from any previous script that set it.
 * @param <none>
 */
function xdom_onLoad() {
	ajaxDebugElement = xdom_getElementById("ajaxDebugOutputDIV");
	xdom_labelElements = document.getElementsByTagName("LABEL");
	ajaxDebug("AJAX DEBUG OUTPUT:");
	xdom_ConfigureDependentControls();
	xdom_ConfigureRequiredControls();
	xdom_ConfigureMatchingControl();
	xdom_ConfigureValidatedControls();
	xdom_ConfigureDynamicControls();
	xdom_ConfigureValidatedMinMaxControls();
	xdom_ListSelectorInitialize();
	xdom_ConfigureAutoSubmits();
	// Call the load function we replaced AFTER initializing (some scripts depend on this)
	if (xdom_otherOnLoad && xdom_otherOnLoad != xdom_onLoad) {
		// Don't allow other scripts to crash this one.
		try {
			xdom_otherOnLoad();
		}
		catch (e) {
			// IE nonsense (can't use a standalone finally block
		}
	}
}
/**
 * This function is used for internal debugging inside this script.  If an element
 * with the id "ajaxDebugOutputDIV" is located after the page load, debug output is 
 * sent to this page element otherwise the debug output is sent in the form of an alert.
 * @param {string} The debug output to be sent
 */
var ajaxDebugElement
function ajaxDebug(s) {
	if (!ajaxDebugElement) {
		// alert(s);
	} else {
		s = s.replace(/\n/, '<br/>');
		ajaxDebugElement.innerHTML += s + "<br/>";
		ajaxDebugElement.scrollTop = ajaxDebugElement.scrollHeight;
	}
}
function xdom_RegisterAutoSubmit(controlID) {
	var entry = new Object();
	entry.controlID = controlID;
	xdom_onloadRegistry.autoSubmits.push(entry);
}
/**
 * This function registers a dependent control.  It is called from script code on
 * the html page for a form control who's state is dependant on another control. Registrations
 * are queued up in a global array until the page has finished loading, then onload functions
 * initialize those controls in this queue.
 * @param {String} controlID - The identifier (id) of the dependent control
 * @param {String} masterID - The identifier of the control on which this control depends
 * @param {function} ctlFunction - The xdom function which handles the particulars of this dependency
 * @see #xdom_ConfigureDependentControls
 */
function xdom_RegisterDependentControl(controlID, masterID, ctlFunction) {
	var entry = new Object();
	entry.controlID = controlID;
	entry.masterID = masterID;
	entry.ctlFunction = ctlFunction;
	xdom_onloadRegistry.controlDependencies.push(entry);
}
/**
 * This function registers a required control.  A required control is simply one which must have
 * a value.  For a text field this means it cannot be empty for others such as a selection list
 * it cannot have a "0" value.
 * @param {String} controlID - The identifier of the control which cannot be empty
 * @param {String} checkControlID - The identifier of an optional checkbox that must be checked to enforce required
 * @see #xdom_ConfigureRequiredControls
 */
function xdom_RegisterRequiredControl(controlID, checkControlID) {
	var entry = new Object();
	entry.controlID = controlID;
	entry.checkControlID = checkControlID;
	xdom_onloadRegistry.requiredControls.push(entry);
}
/**
 * This function registers a matching control.  The typical application is in password text fields
 * where the value needs to be entered twice to ensure the user typed correctly.  When the control
 * is tested, it simply ensures that this control's value matches that of the master.
 * @param {String} controlID - The identifier of the control to be tested
 * @param {String} masterID - The identifier of the control to match against
 */
function xdom_RegisterMatchingControl(controlID, masterID) {
	var entry = new Object();
	entry.controlID = controlID;
	entry.masterID = masterID;
	xdom_onloadRegistry.controlMatches.push(entry);
}
function xdom_RegisterValidatedControl(controlID, callbackClass, requestURL, parameter, parameterIsOtherControl, allowEmpty, callbackObject, xdomExtension) {
	var entry = new Object();
	entry.controlID = controlID;
	entry.requestURL = requestURL;
	entry.callbackClass = callbackClass;
	entry.parameter = parameter;
	entry.parameterIsOtherControl = parameterIsOtherControl;
	entry.allowEmpty = allowEmpty;
	entry.callbackObject = callbackObject;
	entry.xdomExtension = xdomExtension;
	xdom_onloadRegistry.validatedControls.push(entry);
}
function xdom_RegisterDynamicControl(controlID, callbackClass, requestURL, parameter) {
	var entry = new Object();
	entry.controlID = controlID;
	entry.requestURL = requestURL;
	entry.callbackClass = callbackClass;
	entry.parameter = parameter;
	xdom_onloadRegistry.dynamicControls.push(entry);
}
function xdom_RegisterValidatedMinMaxControl(controlID, minValue, maxValue, decimalPlaces, checkControlID) {
	var entry = new Object();
	entry.controlID = controlID;
	entry.minValue = minValue;
	entry.maxValue = maxValue;
	entry.decimalPlaces = decimalPlaces
	entry.checkControlID = checkControlID;
	xdom_onloadRegistry.validatedMinMaxControls.push(entry);
}
/**
 * Registers a new List Selector.  A list selector is a high level control which has a list of
 * selected items on the left, a list of selections on the right and add/remove buttons in the
 * middle. It is constructed using a table with three cells
 * @param {String} controlID - the identifier of the control (the table)
 * @param {String} listID - an identifier specifying an ID associated with the list (i.e user_id)
 * @param {String} requestURL - the URL to which the Ajax module makes server requests
 * @param {String} callbackClass - a string representing the server PHP class responsible for responding to requests
 * @param {function} callbackFunction - a javascript function which is called  whenever a server response
 * is received.  This must be defined in a separate javascript file provided by the xdom invoker
 */
function xdom_RegisterListSelector(controlID, listID, requestURL, callbackClass, callbackObject, xdomExtension) {
	var entry = new Object();
	entry.controlID = controlID;
	entry.listID = listID;
	entry.requestURL = requestURL;
	entry.callbackClass = callbackClass;
	entry.callbackObject = callbackObject;
	entry.xdomExtension = xdomExtension;
	xdom_onloadRegistry.listSelectors.push(entry);
}
function xdom_ConfigureAutoSubmits() {
	var entry, c = xdom_onloadRegistry.autoSubmits;
	while (c.length && (entry = c.shift())) {
		var e = xdom_getElementById(entry.controlID);
		if (!e) continue;
		e.submit();
	}
}
/**
 * This function configures a dependent control at page load time.  It processes registrations 
 * that have been queued up in a global array.  For each dependent control, a property object named 
 * "xdom_MasterControl" is added to the control element.  This property contains the link to
 * the master element and the function that should execute when the master changes.  The master
 * control receives a new property (xdom_DependentControls) that is an array of objects containing
 * a link to the dependent control and a copy of the function executed when the master changes. 
 */
function xdom_ConfigureDependentControls() {
	var en, c = xdom_onloadRegistry.controlDependencies;
	while (c.length && (en = c.shift())) {
		var control = new Object();
		var ce = xdom_getElementById(en.controlID);
		control.element = ce;
		control.ctlFunction = en.ctlFunction;
		
		var master = new Object();
		var me = xdom_getElementById(en.masterID);
		if (!ce || !me) continue;
		master.element = me;
		master.ctlFunction = en.ctlFunction;
		if (!me || !ce) return false;
		if (!me.xdom_DependentControls) {
			me.xdom_DependentControls = new Array();
		}
		me.xdom_DependentControls.push(control);
		ce.xdom_MasterControl = master;
	}
}
/**
 * This function configures & manages a required control. Anonymous functions within
 * are assigned to properties of the control to enforce the control have value before
 * allowing a form to be submitted.  A number of things are done to facilitate the
 * user in making sure these controls have value.
 * 1. If a class name has not been assigned, the name "xdomRequiredControl" is given to the
 *    control. This allows the style sheet to mark these as special.
 * 2. The class name is changed to "xdomRequiredControlFail" when the test is performed.  The
 *    style sheet can mark these even more so to highlight entries that haven't met the test.
 * 3. If a label is associated with the control, its text gets a "*" appended and has similar
 *    class names set - xdomRequiredControlLabel, xdomRequiredControlLabelFail.
 */
function xdom_ConfigureRequiredControls() {
	var en, c = xdom_onloadRegistry.requiredControls;
	while (c.length && (en = c.shift())) {
		var controlID = en.controlID;
		var controlElement = xdom_getElementById(controlID);
		if (!controlElement) continue;
		if (controlElement.form) {
			if (!controlElement.form.requiredControls) controlElement.form.requiredControls = new Array();
			controlElement.form.requiredControls.push(controlElement);
		}
		xdom_setCheckControl(controlElement, en.checkControlID);
		var func = function() {
			var ck = this.checkControl;
			if (ck && !ck.checked) return true;
			var rv = false, msg;
			var msg = "";
			switch (this.type) {
				case 'text':
				case 'textarea':
				case 'password':
					rv = !isEmpty(this.value);
					msg = "This field cannot be empty";
					break;
				case 'select-one':
					rv = (this.value != "" && this.value != "0");
					msg = "Please make a selection";
					break;
				case 'checkbox':
				case 'radio':
					rv = this.checked;
					msg = "You must check this before continuing";
					break;
				case 'select':
				case 'select-multiple':
					for(var i=0; i<this.options.length; i++) if (this.options[i].selected) rv = true;
					if (!rv) msg = "You must select at least one option.";
					break;
				case 'file':
					rv = !isEmpty(this.value);
					msg = "You must select a file for uploading.";
					break;
				default:
					rv = false;
					msg = "Control type " + this.type + " not supported.";
			}
			xdom_setElementFailState(this, rv, msg);
			return rv;
		};
		xdom_setOnblurEvent(controlElement, func);
		xdom_setOnsubmitEvent(controlElement, func);
		xdom_setFormsubmitHandler(controlElement);
	}
}
/**
 * This function initializes and manages a matching control.  Anonymous functions within
 * are assigned to properties of the control to enforce the matching values.  A number of things
 * are done to facilitate the user's task in getting the controls matching.
 * 1. The class name is changed to "xdomRequiredControlFail" when the test is performed.  The
 *    style sheet can mark these even more so to highlight entries that haven't met the test.
 * 2. If a label is associated with the control, its class name is also set upon failure of the
 *    test to xdomRequiredControlLabelFail.
 */
function xdom_ConfigureMatchingControl() {
	var en, c = xdom_onloadRegistry.controlMatches;
	while (c.length && (en = c.shift())) {
		var controlElement = xdom_getElementById(en.controlID);
		var masterElement = xdom_getElementById(en.masterID);
		if (!controlElement || !masterElement) continue;
		controlElement.xdomMatchingControl = masterElement;
		xdom_findAssociatedLabel(controlElement, false);
		var labelElement = xdom_findAssociatedLabel(masterElement, false);
		controlElement.xdomMatchingControlName = labelElement ? labelElement.textContent : masterElement.name;
		controlElement.savedClassName = controlElement.className;		
		func = function() {
			var rv = (this.value == this.xdomMatchingControl.value);
			xdom_setElementFailState(this, rv, "The contents of this field must match the " + this.xdomMatchingControlName + " field.");
			return rv;
		}
		xdom_setOnblurEvent(controlElement, func);
		xdom_setOnsubmitEvent(controlElement, func);
		xdom_setFormsubmitHandler(controlElement);
	}
}
function xdom_ConfigureValidatedMinMaxControls() {
	var en, c = xdom_onloadRegistry.validatedMinMaxControls;
	while (c.length && (en = c.shift())) {
		var controlID = en.controlID;
		var minValue = en.minValue;
		var maxValue = en.maxValue;
		var decimalPlaces = en.decimalPlaces;
		var controlElement = xdom_getElementById(controlID);
		if (!controlElement) continue;
		if (controlElement.form) {
			if (!controlElement.form.requiredControls) controlElement.form.requiredControls = new Array();
			controlElement.form.requiredControls.push(controlElement);
		}
		xdom_setCheckControl(controlElement, en.checkControlID);
		var validateControl = function() {
			var ck = this.checkControl;
			if (ck && !ck.checked) return true;
			var rv = false;
			var msg = '';
			if (this.disabled) {
				xdom_setElementFailState(this, true, "");
				return true;
			}
			if (this.type == 'text') {
				var value = Number(this.value);
				if (isNaN(value)) {
					msg = 'You must enter a numeric value';
				} else {
					if (value < minValue || value > maxValue) {
						msg = 'Value is out of range (minimum value:' + minValue + '; maximum value: ' + maxValue + ')';
					} else {
						this.value = value.toFixed(decimalPlaces);
						rv = true;
					}
				} 
			} else {
				msg = 'Validation on wrong type of control';
			}
			xdom_setElementFailState(this, rv, msg);
			return rv;
		}	
		xdom_setOnblurEvent(controlElement, validateControl);
		xdom_setOnsubmitEvent(controlElement, validateControl);
		xdom_setFormsubmitHandler(controlElement);
	}	
}
var xdom_SessionKeepAliveAjax;
function xdom_StartSessionKeepAlive(invervalMsec, requestURL, failURL, callbackClass) {
	xdom_SessionKeepAliveAjax = new XdomAjax(null, "KeepAlive", requestURL);
	xdom_SessionKeepAliveAjax.controlHandler = function(response) {
		var ok = this.err == "0";
		if (!ok) {
			var msg = "";
			msg += "The browser has detected a problem with the web server.\n";
			msg += "If you have entered data on this form, there is a potential\n";
			msg += "that this data will be lost.  Do not submit the form until you\n";
			msg += "can access the website and are logged in.  When you click OK\n";
			msg += "a new window should popup.  Use that window to ensure that you\n";
			msg += "are logged in, then return to this window and click submit to\n";
			msg += "save your data";
			window.clearInterval(xdom_SessionKeepAliveAjax.timerID);
			alert(msg);
			xdom_openLinkInNewWindow(xdom_SessionKeepAliveAjax.failURL);
			xdom_SessionKeepAliveAjax.timerID = window.setInterval('xdom_SessionKeepAlive()', xdom_SessionKeepAliveAjax.invervalMsec);
		} 
	}
	xdom_SessionKeepAliveAjax.timerID = window.setInterval('xdom_SessionKeepAlive()', invervalMsec);
	xdom_SessionKeepAliveAjax.invervalMsec = invervalMsec;
	xdom_SessionKeepAliveAjax.failURL = failURL;
	xdom_SessionKeepAliveAjax.callbackClass = callbackClass;
}
function xdom_SessionKeepAlive() {
	xdom_SessionKeepAliveAjax.sendRequest("callback=" + xdom_SessionKeepAliveAjax.callbackClass);
	xdom_SessionKeepAliveAjax.count = 1;
}
function xdom_ConfigureValidatedControls() {
	var en, c = xdom_onloadRegistry.validatedControls;
	while (c.length && (en = c.shift())) {
		var controlID = en.controlID;
		var requestURL = en.requestURL;
		var callbackClass = en.callbackClass;
		var parameter = en.parameter;
		var parameterIsOtherControl = en.parameterIsOtherControl;
		var allowEmpty = en.allowEmpty;
		var controlElement = xdom_getElementById(controlID);
		if (!controlElement) continue;
		controlElement.allowEmpty = allowEmpty;
		// create an instance of XdomAjax
		var xdomAjax = new XdomAjax(controlElement, "VerifiedControl", requestURL);
		controlElement.xdomAjax = xdomAjax;
		xdomAjax.callbackClass = callbackClass;
		xdomAjax.controlID = controlID;
		xdomAjax.controlElement = controlElement;
		xdomAjax.parameter = parameter;
		xdomAjax.parameterIsOtherControl = parameterIsOtherControl;
		xdomAjax.xdomExtension = en.xdomExtension;
		var cb = xdomAjax.callbackObject = en.callbackObject;
		if (cb && cb.initialize) cb.initialize(xdomAjax);
		if (parameterIsOtherControl) {
			var otherControlElement;
			otherControlElement = xdomAjax.otherControlElement = xdom_getElementById(parameter);
			if (otherControlElement) {
				var control = new Object();
				control.element = controlElement;
				control.ctlFunction = function(element, masterElement) {
					if (element.onblur) element.onblur();
				}
				if (!otherControlElement.xdom_DependentControls) {
					otherControlElement.xdom_DependentControls = new Array();
				}
				otherControlElement.xdom_DependentControls.push(control);
				xdom_setOnblurEvent(otherControlElement, function() {
					xdom_OnClick(this);});
			} else {
				xdomAjax.parameterIsOtherControl = false;
				xdomAjax.parameter = "";
			}
		}
		if (controlElement.form) {
			if (!controlElement.form.requiredControls) controlElement.form.requiredControls = new Array();
			controlElement.form.requiredControls.push(controlElement);
		}
		xdom_findAssociatedLabel(controlElement, !allowEmpty);
		
		// Setup the request, response handler, and test functions. This runs in the context of the control
		var validateControl = function() {
			var xdomAjax = this.xdomAjax;
			var value, parameter,cb;
			value = xdom_get_element_value(this);
			parameter = (xdomAjax.parameterIsOtherControl) ? xdom_get_element_value(xdomAjax.otherControlElement) : xdomAjax.parameter;
			this.xdomWaitingForServer = true;
			var post = 'callback=' + xdomAjax.callbackClass + "&controlID=" + xdomAjax.controlID + "&controlValue=" + value + "&parameter=" + parameter;
			if ((cb = xdomAjax.callbackObject) && cb.sendRequest) post += cb.sendRequest(post);
			xdomAjax.sendRequest(null, post);
		};
		// This runs in the context of the XdomAjax class
		xdomAjax.controlHandler = function(response) {
			var e = this.controlElement;
			var ok = this.err == "0";
			e.xdomWaitingForServer = false;
			e.xdomControlIsValid = ok;
			if (!e.xdomInitialValidation) xdom_setElementFailState(e, ok, this.errmsg)
			if (ok && this.errmsg) e.value = this.errmsg;
			e.xdomInitialValidation = false;
			e.xdomServerMessage = this.errmsg;
		};
		// This runs in the context of the control
		var testControl = function() {
			if (this.allowEmpty && this.value == '') return true;
			// Handle the unlikely case where we are still waiting for a response
			if (this.xdomWaitingForServer) {
				// alert("The browser is validating fields of your form and is still waiting for the server.  Please wait a few seconds and try to submit again");
				return false;
			}
			if (this.xdomControlIsValid) return true;
			xdom_setElementFailState(this, false, this.xdomServerMessage);
			return false;
		}
		
		// We will send a request now to validate the control but set this flag so the response
		// handler won't hightlight the control as failed until the user actually has a chance
		// to go into and leave the control
		controlElement.xdomInitialValidation = true;
		controlElement.xdomControlIsValid = false;
		xdom_setOnblurEvent(controlElement, validateControl);
		xdom_setOnchangeEvent(controlElement, validateControl);
		xdom_setOnsubmitEvent(controlElement, testControl);
		controlElement.onblur();
		xdom_setFormsubmitHandler(controlElement);
	}
}
function xdom_ConfigureDynamicControls() {
	var en, c = xdom_onloadRegistry.dynamicControls;
	while (c.length && (en = c.shift())) {
		var controlID = en.controlID;
		var requestURL = en.requestURL;
		var callbackClass = en.callbackClass;
		var parameter = en.parameter;
		var controlElement = xdom_getElementById(controlID);
		if (!controlElement) continue;
		// create an instance of XdomAjax
		var xdomAjax = new XdomAjax(controlElement, "DynamicControl", requestURL);
		controlElement.xdomAjax = xdomAjax;
		xdomAjax.callbackClass = callbackClass;
		xdomAjax.controlID = controlID;
		xdomAjax.parameter = parameter;
		xdom_findAssociatedLabel(controlElement, true, "xdomDynamicControl");
		
		// Setup the request, response handler, and test functions. This runs in the context of the control
		var sendControlUpdate = function() {
			var xdomAjax = this.xdomAjax;
			var value =	xdom_get_element_value(this);
			this.xdomWaitingForServer = true;
			var action = 'callback=' + xdomAjax.callbackClass + "&controlID=" + xdomAjax.controlID + "&controlValue=" + value + "&parameter=" + xdomAjax.parameter;
			xdomAjax.sendRequest(action);
		};
		// This runs in the context of the XdomAjax class
		xdomAjax.controlHandler = function(response) {
			var e = this.controlElement;
			var ok = this.err == "0";
			e.xdomWaitingForServer = false;
			e.xdomControlIsValid = ok;
			if (!e.xdomInitialValidation) xdom_setElementFailState(e, ok, this.errmsg)
			if (ok && this.errmsg) {
				switch (e.type) {
				case 'text':
					case 'textarea':
						e.value = this.errmsg;;
						break;
					case 'checkbox':
						e.checked = (this.errmsg == "1");
						break;
				}
			}
			e.xdomInitialValidation = false;
			e.xdomServerMessage = this.errmsg;
		};
		
		// We will send a request now to validate the control but set this flag so the response
		// handler won't hightlight the control as failed until the user actually has a chance
		// to go into and leave the control
		controlElement.xdomInitialValidation = true;
		controlElement.xdomControlIsValid = false;
		switch (controlElement.type) {
			case 'text':
			case 'textarea':
				xdom_setOnblurEvent(controlElement, sendControlUpdate);
				break;
			case 'checkbox':
				xdom_setOnclickEvent(controlElement, sendControlUpdate);
				break;
		}
		
		// controlElement.onblur();
		// xdom_setFormsubmitHandler(controlElement);
	}
}
function xdom_ListSelectorInitialize() {
	var entry, c = xdom_onloadRegistry.listSelectors;
	while (c.length && (entry = c.shift())) {
		var controlID = entry.controlID;
		var url = entry.requestURL;
		var callbackClass = entry.callbackClass;
		var listID = entry.listID;
		var controlElement = xdom_getElementById(controlID);
		if (!controlElement) continue;
		// create an instance of XdomAjax
		var xdomAjax = new XdomAjax(controlElement, "ListSelector", url);
		controlElement.xdomAjax = xdomAjax;
		xdomAjax.listID = listID;
		xdomAjax.leftCell = xdom_getElementById(controlID + "_left");
		xdomAjax.rightCell = xdom_getElementById(controlID + "_right");
		xdomAjax.addControl = xdom_getElementById(controlID + "_add");
		xdomAjax.removeControl = xdom_getElementById(controlID + "_remove");
		xdomAjax.addControl.onclick = xdom_ListSelectorAct;
		xdomAjax.removeControl.onclick = xdom_ListSelectorAct;
		xdomAjax.addControl.xdomAjax = xdomAjax;
		xdomAjax.removeControl.xdomAjax = xdomAjax;
		xdomAjax.callbackClass = callbackClass;
		var cb = xdomAjax.callbackObject = entry.callbackObject;
		xdomAjax.controlHandler = xdom_ListSelectorRefresh;
		xdomAjax.xdomExtension = entry.xdomExtension;
		var action = 'callback=' + callbackClass + "&action=getLists&listID=" + listID;
		if (cb) {
			cb.initialize(xdomAjax, action);
			action += cb.sendRequest('getLists', listID, null);
		}
		xdomAjax.sendRequest(action);
	}
}
function xdom_ListSelectorRefresh(response) {
	this.controlElement.leftCellList = xdom_ListSelectorRefreshCell(this.controlElement, this.leftCell, "selected", "removeSelection", "xdomListSelectorSelectedList", response);
	this.controlElement.rightCellList = xdom_ListSelectorRefreshCell(this.controlElement, this.rightCell, "selections", "addSelection", "xdomListSelectorSelectionList", response);
}
function xdom_ListSelectorRefreshCell(controlElement, cell, tagname, action, className, response) {
	var html = '<table class="' + className + '"><tbody>';
	var empty = "<tr><td>(none)</td></tr>";
	var list = response.getElementsByTagName(tagname);
	if (list.length) {
		var entries = list[0].getElementsByTagName("entry");
		var numEntries = entries.length;
		if (numEntries) {
			for (var i=0; i<numEntries; i++) {
				var e = entries[i];
				var value = e.firstChild ? e.firstChild.data : "";
				//if (i==1) html += '<tr><td nowrap class="xdomListSelectorSelectedItem">' + value + '</td></tr>'; else
				html += '<tr><td>' + value + '</td></tr>';
			}
		} else html += empty;
	} else html += empty;
	html += "</tbody></table>";
	cell.innerHTML = html;
	// Now Process the newly created table entries
	if (!numEntries) return null;
	var clist = cell.getElementsByTagName("td");
	var numCells = clist.length;
	if (numEntries != numCells) alert("We have some trouble here");
	for (var i=0; i<numCells; i++) {
		var e = clist[i], t;
		e.selectionID = entries[i].getAttribute("id");
		e.controlElement = controlElement;
		if (entries[i].getAttribute("disabled") == "1") {
			e.className = "xdomListSelectorDisabledItem";
			e.disabled = true;
		} else {
			e.onclick = xdom_ListSelectorOnclick;
			e.action = action;
		}
		if (t = entries[i].getAttribute("title")) e.setAttribute("title", t);
		if (t = entries[i].getAttribute("style")) e.setAttribute("style", t);
	}
	return clist;
}
function xdom_ListSelectorAct() {
	var xdomAjax = this.xdomAjax;
	var cb = this.xdomAjax.callbackObject;
	this.disabled = true;
	var action = 'callback=' + xdomAjax.callbackClass + "&action=" + xdomAjax.action + "&listID=" + xdomAjax.listID + "&selectionID=" + xdomAjax.selectionID;
	if (cb) action += cb.sendRequest(xdomAjax.action, xdomAjax.listID, xdomAjax.selectionID);
	xdomAjax.sendRequest(action);
}
function xdom_ListSelectorOnclick() {
	var controlElement = this.controlElement;
	var xdomAjax = controlElement.xdomAjax;
	// Clear any existing selections
	var leftCellList = controlElement.leftCellList;
	var rightCellList = controlElement.rightCellList;
	if (leftCellList) for(var i=0; i<leftCellList.length; i++) if (leftCellList[i].className == "xdomListSelectorSelectedItem") leftCellList[i].className = "";
	if (rightCellList) for(var i=0; i<rightCellList.length; i++) if (rightCellList[i].className == "xdomListSelectorSelectedItem") rightCellList[i].className = "";
	
	// Select me
	this.className = "xdomListSelectorSelectedItem";
	xdomAjax.selectionID = this.selectionID;
	xdomAjax.action = this.action;
	if (this.action == "addSelection") {
		xdomAjax.addControl.disabled = false;
		xdomAjax.removeControl.disabled = true;
	} else {
		xdomAjax.addControl.disabled = true;
		xdomAjax.removeControl.disabled = false;
	}
}
function xdom_disableDependentControl(control, master) {
	var type = master.type;
	var value = (type == 'checkbox' || type == 'radio') ?  master.checked : master.value;
	if (value) control.disabled = false
	else control.disabled = true;
}
function xdom_get_element_value(element) {
	var value;
	switch (element.type) {
		case 'text':
		case 'textarea':
			value = element.value;
			break;
		case 'checkbox':
			value = element.checked ? "1" : "0";
			break;
		case 'select-one':
			var index = element.selectedIndex;
			value = element.options[index].value;
			break;
	}
	return encodeURIComponent(value);
}
function xdom_OnClick(element) {
	var entry, n;
	if (element.xdom_DependentControls) {
		for(n=0; n < element.xdom_DependentControls.length; n++) {
			entry = element.xdom_DependentControls[n];
			entry.ctlFunction(entry.element, element);
		}
	}
}
function xdom_OnBlur(element) {

}
function xdom_onSubmit(element) {

}
function xdom_onReset(element) {

}
function xdom_submit(id) {
	var e = xdom_getElementById(id);
	if (!e) return false;
	e.submit();
}
/**
 * Perform a submit.  This is normally the result of an onclick event for
 * the submit button, but can also be invoked from another control in the form
 * i.e keypress on a text element.  When calling from another control, the value
 * parameter should be set to the value of the submit button.
 * 
 * @param e
 * @param submitName
 * @param cancel
 * @param value
 * @return
 */
function xdom_submitButton(e,submitName,cancel,value){
	var form = e.form;
	if (!form) return;
	if (!value) value = e.value;
	var num = form.elements.length;
	for (var i = 0; i < num; i++) {
		var c = form.elements[i];
		if (c.type == "hidden"  && c.name == submitName) c.value = value;
	}
	if (!form.submit.tagName && (cancel || !form.onsubmit || (form.onsubmit && form.onsubmit()))) form.submit();
}
function xdom_cancelButton(e,cancelName){
	var form = e.form;
	if (!form) return;
	var name = cancelName ? cancelName : "cancelButton";
	// Quick and dirty - just disable onsubmit functions
	var num = form.elements.length;
	for (var i = 0; i < num; i++) {
		var c = form.elements[i];
		c.onsubmit = null;
		if (c.type == "hidden"  && c.name == name) c.value = e.value;
	}
	form.xdomDoingCancel = true;
	// Handle case where this function is overwritten by submit button.
	if (!form.submit.tagName) form.submit();
	else {
		form.submit.name = name;
		form.submit.value = e.value;
		form.submit.click();
	}
}
var xdom_newLinkWindow;
function xdom_openLinkInNewWindow(url) {
	var w = xdom_newLinkWindow;
	if(!w || w.closed) w = window.open(url, window.name + '_print', 'menubar,resizable,scrollbars,status');
	xdom_newLinkWindow = w;
	if(!w) alert('Window for link cannot be opened!  This is likely due to a popup blocker in your browser.');
}
var xdom_newPopupWindowNumber=0;
function xdom_openLinkAsPopupWindow(url, width, height) {
	var w;
	var features = 'menubar,resizable,scrollbars';
	if (width) features += ',width=' + width;
	if (height) features += ',height=' + height;
	w = window.open(url, window.name + '_popup' + xdom_newPopupWindowNumber++, features);
	if(!w) alert('Window for link cannot be opened!  This is likely due to a popup blocker in your browser.');
}
function xdom_executeLink(linkBase, controlID, param) {
	var control = xdom_getElementById(controlID);
	var value = control.value;
	if (!value) value = 0;
	if (!param) param = 'selectID';
	var link = linkBase + '&' + param + '=' + value;
	window.location = link;
}
function xdom_switchLocale(control, linkBase, current_locale) {
	var value = control.value;
	if (!value) return;
	if (value == current_locale) return;
	var sep = (linkBase.indexOf("?") < 0) ? "?" : "&";
	var link = linkBase + sep + "locale=" + value;
	window.location = link;
}
function xdom_helpPopup(controlID) {
	var c = xdom_getElementById(controlID);
	var title = c.SavedTitle ? c.SavedTitle : c.title;
	alert(title);
}
function xdom_FakeCancel(e) {
	var form = e.form;
	var subform = xdom_getElementById("xdomCancelForm");
	//var input = subform.childNodes[0];
	var input = xdom_getElementById("fake");
	input.value = e.value;
	input.name = e.name;
	input.id = e.name;
	subform.submit();
	return false;
}
function xdom_link_ok_cancel() {
	alert("I am here");
	return false;
}


function xdom_GetFirstElementWithTagName(element, tagName) {
	var list = element.getElementsByTagName(tagName);
	if (list.length < 1) return null;
	var e = list[0];
	return e;
}
function xdom_GetData(element) {
	if (!element.firstChild) return "";
	var s = '';
	for (var i=0; i<element.childNodes.length; i++) 
		s += element.childNodes[i].data;
	return s;
}
function xdom_ResponseOK(e){
	var z = e.getAttribute("value");
	return (e.getAttribute("value") == "0") ? true : false;
}

// Utility functions
xdom_eventHandlers = new Object();
xdom_eventHandlers.onblur = new Array();
xdom_eventHandlers.onclick = new Array();
xdom_eventHandlers.onchange = new Array();
xdom_eventHandlers.onsubmit = new Array();
/**
 * These functions facilitate setting the event attributes in elements.  Since we could have
 * multiple functions that need to be called when this event happens, we need a way to call 
 * each in turn.  
 */
function xdom_setOnblurEvent(element, handler) {
	if (!element.xdomOnblur) element.xdomOnblur = new Array();
	if (element.onblur && element.onblur != xdom_handleOnblurEvent) {
		element.xdomOnblur.push(element.onblur);
	}
	element.onblur = xdom_handleOnblurEvent;
	element.xdomOnblur.push(handler);
}
function xdom_handleOnblurEvent() {
	var r, rv = true;
	for (var i=0; i<this.xdomOnblur.length; i++) {
		this.xdomTemp = this.xdomOnblur[i];
		if (typeof this.xdomTemp == "function") r = this.xdomTemp(); 
		else r = this.xdomTemp.onElementBlur(this);
		if (!r) rv = false;
	}
	return rv;
}
function xdom_setOnclickEvent(element, handler) {
	if (!element.xdomOnclick) element.xdomOnclick = new Array();
	if (element.onclick && element.onclick != xdom_handleOnclickEvent) {
		element.xdomOnclick.push(element.onclick);
	}
	element.onclick = xdom_handleOnclickEvent;
	element.xdomOnclick.push(handler);
}
function xdom_handleOnclickEvent() {
	var r, rv = true;
	for (var i=0; i<this.xdomOnclick.length; i++) {
		this.xdomTemp = this.xdomOnclick[i];
		if (typeof this.xdomTemp == "function") r = this.xdomTemp();
		else  r = this.xdomTemp.onElementClick(this);
		if (!r) rv = false;
	}
	return rv;
}
function xdom_setOnchangeEvent(element, handler) {
	if (!element.xdomOnchange) element.xdomOnchange = new Array();
	if (element.onchange && element.onchange != xdom_handleOnchangeEvent) {
		element.xdomOnchange.push(element.onchange);
	}
	element.onchange = xdom_handleOnchangeEvent;
	element.xdomOnchange.push(handler);
}
function xdom_handleOnchangeEvent() {
	var r, rv = true;
	for (var i=0; i<this.xdomOnchange.length; i++) {
		this.xdomTemp = this.xdomOnchange[i];
		if (typeof this.xdomTemp == "function") r = this.xdomTemp();
		else  r = this.xdomTemp.onElementChange(this);
		if (!r) rv = false;
	}
	return rv;
}
function xdom_setFocus(e) {
	e.focus();
}
function xdom_setOnsubmitEvent(element, handler) {
	if (!element.xdomOnsubmit) element.xdomOnsubmit = new Array();
	if (element.onsubmit && element.onsubmit != xdom_handleOnsubmitEvent) {
		element.xdomOnsubmit.push(element.onsubmit);
	}
	element.onsubmit = xdom_handleOnsubmitEvent;
	element.xdomOnsubmit.push(handler);
}
function xdom_handleOnsubmitEvent() {
	var rv = true;
	for (var i=0; i<this.xdomOnsubmit.length; i++) {
		this.xdomTemp = this.xdomOnsubmit[i];
		if (typeof this.xdomTemp == "function") r = this.xdomTemp();
		else  r = this.xdomTemp.onElementSubmit(this);
		if (!r) rv = false;
	}
	return rv;
}
var xdom_alertMessage;
function xdom_alertOnSubmit(msg) {
	
}
/**
 * This function sets attributes of the control (and possibly the control's label
 * element) based on the fail state argument.
 * @param {element} element - the control element on which to operate
 * @param {boolean} ok - boolean indicating whether to set ok or failed state
 * @param {String} message - failure message to set into the elements title attribute
 * @param {String} newValue - if set, the value of the control is replaced by this.
 */
function xdom_setElementFailState(element, ok, message) {
	var saved = (element.savedClassName) ? element.savedClassName + " " : "";
	if (ok) {
		element.className = saved + "xdomRequiredControl";
		if (element.labelElement) element.labelElement.className = "xdomRequiredControlLabel";
		element.controlValidated = true;
	} else {
		element.className = saved + "xdomRequiredControlFail";
		if (element.labelElement) element.labelElement.className = "xdomRequiredControlLabelFail";
		if (!element.SavedTitle) element.SavedTitle = element.title;
		element.title = message;
		element.ajaxMessage = message;
		element.controlValidated = false;
	}
}
/**
 * This function registers a control with it's parent form as a control to be tested
 * before submit.  The form's onsubmit is set to an anonymous function inside this one
 * where the handling occurs.  That function invokes the onsubmit function of each control,
 * and if all return true, the form will be allowed to submit.
 * @param {Element} control - the control to be tested.
 */
function xdom_setFormsubmitHandler(control) {
	var form = control.form;
	if (!form) return;
	var otherhandler = form.onsubmit;
	var rv;
	if (!form.xdom_onsubmitControls) form.xdom_onsubmitControls = new Array();
	form.xdom_onsubmitControls.push(control);
	form.onsubmit = function () {
		//if (otherhandler  &&!otherhandler()) return false;
		var rv = true;
		if (form.xdomDoingCancel) return true;
		var msg = "The following fields have errors. Please correct and re-submit\n";
		for (var i=0; i < form.xdom_onsubmitControls.length; i++) {
			var control = form.xdom_onsubmitControls[i];
			if (!control.onsubmit()) {
				if (control.message) msg += control.message;
				else msg += xdom_getLabelMessage(control);
				rv = false;
			}
		}
		if (!rv) alert (msg);
		return rv;
	}
}
function xdom_getLabelMessage(control) {
	var msg;
	if (control.labelElement) {
		msg = "\n";
		for(var x=0;x<control.labelElement.childNodes.length;x++){
			var node = control.labelElement.childNodes[x];
			if (node.nodeType == 3) msg += node.nodeValue;
		}
		if (control.ajaxMessage) msg += ":   -" + control.ajaxMessage;
	}
	else msg += "\n" + control.name;
	return msg;
}
/**
 * Thi function locates the label for a given control and highlights this label
 * to make it significant.  The class name of the label is set to "xdomRequiredControlLabel"
 * allowing the style sheet to set the visual representation of the marked label
 * @param controlElement
 * @param starTheLabel set to true to add a * to the label text.
 */
function xdom_findAssociatedLabel(controlElement, required, setClass) {
	if (controlElement) return xdom_findLabel(controlElement, controlElement.id, required, setClass);
	return null;
}
function xdom_findLabel(controlElement, id, required, setClass) {
	var le;
	if (!controlElement) return null;
	var form = controlElement.form;
	controlElement.savedClassName = controlElement.className;
	for (var i=0; i<xdom_labelElements.length; i++) 
		if ((le = xdom_labelElements[i]).htmlFor == id 
				&& le.form == form) {
		controlElement.labelElement = le;
		if (controlElement.id == id) {
			le.savedInnerHTML = le.innerHTML;
			le.savedClassName = le.className;
			xdom_setRequiredState(controlElement, required, setClass);
		}
		break;
	}
	return le;	
}
function xdom_setRequiredState(controlElement, required, setClass) {
	var star="", lc="", cc="", le = controlElement.labelElement;
	var csc = controlElement.savedClassName;
	var lsc = le ? le.savedClassName : "";
	var rc = setClass ? setClass : "xdomRequiredControl"
	if (required) {
		star = "*";
		lc = (lsc ? " " : "") + "xdomRequiredControlLabel";
		cc = (csc ? " " : "") + rc;
	}
	if (le) {
		le.innerHTML = le.savedInnerHTML + star;
		le.className = lc;
	}
	controlElement.className =  csc + cc;
}
function xdom_setCheckControl(controlElement, checkControlID) {
	var ck = controlElement.checkControl = checkControlID ? xdom_getElementById(checkControlID) : null;
	xdom_findAssociatedLabel(controlElement, !ck || ck.checked);
	if (ck) {
		if (!ck.checkControlled) ck.checkControlled = new Array();
		ck.checkControlled.push(controlElement);
		var ckfunc = function() {
			for (var i=0; i<this.checkControlled.length; i++) {
				xdom_setRequiredState(this.checkControlled[i], this.checked);
			}
		}
		xdom_setOnchangeEvent(ck, ckfunc);
	}
}
function isblank(s) {
    for(var i = 0; i < s.length; i++) {
        var c = s.charAt(i);
        if ((c != ' ') && (c != '\n') && (c != '\t')) return false;
    }
    return true;
}
function isNumeric(s) {
    return /[0-9]*/.test(s);
}
function isEmpty(s) {
	return ((s == null) || (s == "") || isblank(s));
}
function popup_help_window(application, topic, field, width, height) {
	var url = 'http://fredsusedwebsites.com/helpPopup.php?app=' + application + '&topic=' + topic;
	if (field) url += '&field=' + field;
	if (!width) width = 850;
	if (!height) height = 600;
	window.open(url, 'HelpWindow', 'scrollbars=yes,resizable=yes,width='+ width + ',height=' + height);
}
/**
 * A utility to make the specified element (usually a div) either
 * invisible or inline based on the state of the checkbox this is
 * called from
 */
function xdom_checkboxSetVisibility(e, id) {
	var checked = e.checked;
	var target = xdom_getElementById(id);
	if (!target) return;
	if (checked) target.style.display = "inline";
	else target.style.display = "none";
}
function xdom_checkboxSetEnabled(e, id) {
	var target = xdom_getElementById(id);
	if (!target) return;
	target.disabled = !e.checked;
}
/**
 * Compute the element's position offset from the root of the document
 * 
 * @param e - the subject element
 * @return object containing top and left
 */
function xdom_getOffset(e) {
	for (var l = e.offsetLeft, t = e.offsetTop, p = e.offsetParent; p; p = p.offsetParent) {
		l += p.offsetLeft;
		t += p.offsetTop;
	}
	return {top : t, left : l};
}
/*
 * AJAX Class
 */
function XdomAjax(controlElement, controlName, url) {
	this.controlElement = controlElement;
	this.url = url;
	this.controlName = controlName;
	
	// create an XMLHttpRequest Instance
	// this should work for all browsers except IE6 and older
	try {
		this.xmlHttp = new XMLHttpRequest();  // try to create object
	} catch(e) {
		// Assume IE6 or older
		var xmlHttpVersions = new Array("Msxml2.XMLHTTP.6.0",
										"Msxml2.XMLHTTP.5.0",
										"Msxml2.XMLHTTP.4.0",
										"Msxml2.XMLHTTP.3.0",
										"Msxml2.XMLHTTP",
										"Microsoft.XMLHTTP"
										);
		// try every prog id until one works
		for(var i=0; i < xmlHttpVersions.length && !this.xmlHttp; i++) {
			try {
				this.xmlHttp = new ActiveXObject(xmlHttpVersions[i]);  // try to create object
			} catch(e) {}
		}
	}
	// Return the created object or display an error message
	if (!this.xmlHttp) alert("Error creating the XMLHttpRequest object.");
}
XdomAjax.prototype = {
	// Instance Methods
	// Called to send a request to the server
	sendRequest : function(request, postString) {
		// Get the two values entered
		var params = "?xdomControl=" + this.controlName;
		if (request) params += "&" + request;
		if (this.xdomExtension) params += '&xdomExtension=' + this.xdomExtension;
		this.sendRawRequest(this.url + params, postString);
	},
	sendRawRequest : function(requestString, postString) {
		// Only continue if xmlHttp isn't void
		this.requestString = requestString;
		if (this.xmlHttp) {
			// Don't make a request if already busy
			if (this.xmlHttp.readyState != 0 && this.xmlHttp.readyState != 4) {
				// alert("Server busy, try again later.");
				return
			}
			// Try to connect to the server
			try {
				var _this = this;
				// Critical NOTE: the open must be called before the onreadystatechange is
				// set or IE fails on the second time this is used!!!
				this.xmlHttp.open(postString ? "POST" : "GET", requestString, true);
				this.xmlHttp.onreadystatechange = function() {
					var x = _this;
					x.handleRequestStateChange();
				};
				//ajaxDebug("Sending Server Request: " + this.url + params);
				if (postString) {
					this.xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
					this.xmlHttp.send(postString);
				} else { 
					this.xmlHttp.send(null);
				}
			} catch(e) {
				alert("Can't connect to server:\n" + e.toString());
			}
		}
	},	
	// Function to handle the HTTP response
	handleRequestStateChange : function() {
	// handleRequestStateChange = function(object) {
		// When readyState is 4 we are ready to read the server response
		if (this.xmlHttp.readyState != 4) return;
		// Continue only if HTTP status is OK
		if (this.xmlHttp.status != 200) {
			ajaxDebug("xmlHttp.status = " + this.xmlHttp.status);
			alert("There was a problem retrieving the data:\n" + this.xmlHttp.statusText + "Request: " + this.requestString);
			return;
		}
		try {
			this.handleServerResponse();
		} catch(e) {
			var msg = "Error with the server response: " + e.toString();
			this.x = e;
			if (typeof console != 'undefined') {
				console.error(msg);
				console.dirxml(this.xmlHttp.responseXML);
			}
			alert(msg);
		}
	},	
	// Handle the Server Response
	handleServerResponse : function() {
		// Read the message from the server
		var xmlResponse = this.xmlHttp.responseXML,
			responseText = this.xmlHttp.responseText, e, n, v;
		
		// Catch potential errors with IE and Opera
		if (!xmlResponse || !xmlResponse.documentElement) {
			throw("Invalid XML Structure:\n" + responseText);
		}
		
		// Catch potential errors with Firefox
		var rootNodeName = xmlResponse.documentElement.nodeName;
		if (rootNodeName == "parsererror") {
			throw("Invalid XML Structure:\n" + responseText);
		}
		
		// Get the document element
		var xmlRoot = xmlResponse.documentElement;
		// Make sure we received the XML document we expect
		if (rootNodeName != "response" || !xmlRoot.firstChild) {
			throw("Invalid XML structure:\n" + responseText);
		}
		
		var errElement = xdom_GetFirstElementWithTagName(xmlRoot, "error");
		this.err = errElement.getAttribute("value");
		this.errmsg = xdom_GetData(errElement);
		if (this.err == "2") throw("Server exception\n" + this.errmsg);	
		
		// Output debug element if it exists
		var dbg = xdom_GetFirstElementWithTagName(xmlRoot, "debug");
		if (dbg) {
			ajaxDebug("Server Debug: \n" + xdom_GetData(dbg));
		}
		// ajaxDebug(responseText);
		if (this.requestor) {
			 this.requestor.serverResponse(this, xmlRoot);
		} else {
		// Pass the response document to the conrol's handler and callback object
			this.controlHandler(xmlRoot);
			if (this.callbackObject) this.callbackObject.serverResponse(this, xmlRoot);
		}
		// Reload page if server requests
		if (e = xdom_GetFirstElementWithTagName(xmlRoot, "reloadpage")) {
			var newLoc = xdom_GetData(e);
			if (newLoc == "1") document.location.reload();
			else document.location = newLoc;
		}
		// Set browser cookie if cookie supplied
		if (e = xdom_GetFirstElementWithTagName(xmlRoot, "cookie")) {
			v = xdom_GetData(e);
			if (n = e.getAttribute('name')) {
				var c = new CookieUtil(document, n, e.getAttribute('time'), e.getAttribute('path'), e.getAttribute('domain'), e.getAttribute('secure'));
				c.store(v);
			}
		}
		// Check for Ajax scripts
		if (e = xdom_GetFirstElementWithTagName(xmlRoot, "ajax_script")) {
			Utilities.loadJavascript(xdom_GetData(e));
		}
	}
}
function XdomAjaxServerWidget(id, url, delay, interval, params) {
	var t = this;
	t.id = id;
	t.url = url;
	t.delay = delay * 1000;
	t.interval = interval * 1000;
	t.params = params;
	Utilities.onload(this);
}
XdomAjaxServerWidget.prototype = {
	onPageLoad : function() {
		var t = this;
		if (!(t.content = xdom_getElementById(t.id))) return;
		if (t.delay <= 0) t.serverRequest();
		else t.timer = window.setInterval(function(){t.serverRequest()}, t.delay);
	},
	serverRequest : function() {
		var t = this, e = t.content, ajax, post='', key, params = t.params; sep = '';
		window.clearInterval(t.timer);
		ajax = t.xdomAjax = new XdomAjax(e, "ServerWidget", t.url);
		ajax.requestor = t;
		for(key in params) {
			post += sep + key + '=' + encodeURIComponent(params[key]);
			sep = '&';
		}
		ajax.sendRequest(null, post);
	},
	serverResponse : function(ajax, response) {
		var t = this, e, ok;
		if (t.interval > 0) t.timer = window.setInterval(function(){t.serverRequest()}, t.interval);
		if (ajax.err != '0') {
			alert(ajax.errmsg);
			return;
		}
		e = xdom_GetFirstElementWithTagName(response, "content");
		ok = e.getAttribute("value") == "0";
		if (e && ok) t.content.innerHTML = decodeURIComponent(xdom_GetData(e));
		Utilities.onPageUpdate();				
	}
}
function PayPalSetCmd(id, value) {
	var cmdElement = document.getElementById(id);
	if (!cmdElement) return false;
	// alert("Changing cmd from " + cmdElement.value + " to " + value);
	cmdElement.value = value;
	return true;	
}
function paypalEnableCheckSubmit(id) {
	var cmdElement = document.getElementById(id);
	var func = function() {
		var e = document.getElementById(id);
		alert("Cmd: " + e.value);
	}
	xdom_setOnsubmitEvent(cmdElement, func);
}