		
function numericField(el, min, max, maxdp) {
	humantitle = compat_getAttribute(el, "humantitle");
	
	el.baddata = false;
	if(el.value) {
		el.value = el.value.replace(/[^0-9.\-]/g	,'');
		testVal = parseInt(el.value);
		if(isNaN(testVal)) {
			alert(humantitle + " must be a number.");
			el.baddata = true;
			return false;		
		}
		
		if(testVal != 0) {
			if(min != null && testVal < min) {
				alert("I'm sorry, but the " + humantitle + " is too low. It must be at least " + min  + '.');
				el.baddata = true;
				el.value = min;
				return false;
			}
			if(max != null && testVal > max) {
				alert("I'm sorry, but the " + humantitle + " is too high. It must be at most " + max + '.');
				el.baddata = true;				
				el.value = max;
				return false;
			}
			
			if(maxdp != null) {								
				// round the value according to maximum number decimal points, 
				// which means if maxdb is 0, then there are no dp allowed.								
				el.value *= Math.pow(10,maxdp);				
				el.value = Math.round(el.value);
				el.value /= Math.pow(10,maxdp);								
				return false;
			}
		}
	}
	return true;
}

function currencyField(el, dp) {
	el.baddata = false;
	if(el.value) {
		el.value = el.value.replace(/[^0-9.\-]/g,'');
		testVal = parseInt(el.value);	
		if(isNaN(testVal)) {
			alert("Please enter a number");
			el.baddata = true;
			return false;
		} else {
			el.value = '$' + thousandCommas(Math.round(testVal) + '');
		}		
	}
	return true;
}
function thousandCommas(strVal) {
	var i = strVal.length, outVal = "";
	while(i > 3) {
		if(outVal) outVal = "," + outVal;
		outVal = strVal.substr(i-3,3) + outVal;
		i-= 3;
	}
		if(outVal) outVal = "," + outVal;
	outVal = strVal.substr(0,i) + outVal;
	return outVal;
}


function emailField(el) {
	if(!el.value) return true;
	el.baddata = false;
 	if(el.value.match(/^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/)) {
 		return true;
 	} else {
		el.baddata = true;
 		alert("'" + el.value + "' is not a valid Email Address. Please check your typing. It should be something like name@company.co.nz");
 		return false;
 	} 	
}

function passwordField(el) {	
	humantitle = compat_getAttribute(el, "humantitle");
	el.baddata = false;
	if (! el.value) return true;
	
 	if (! el.value.match(/[a-zA-Z]+/)) {
 		alert(humantitle +  ' must contain at least one letter of the alphabet.');
 		el.baddata = true;
 		return false; 		
 	}
 	
 	if (! el.value.match(/[0-9]+/)) {
 		alert(humantitle + ' must contain at least one number.');
 		el.baddata = true;
 		return false; 		
 	}
 	
 	if (el.value && el.value.length < 4) {
 		alert(humantitle + ' must be at least four characters long.');
 		el.baddata = true;
 		return false; 		
 	}
}


function updateRequired(formObj, changedEl) {
	formObj = getFormObj(formObj);	
	if(! formObj) return;
	
	checkEventHandlers(formObj);
	
	if(typeof compat_getAttribute != "function") alert("SilverStripe Form Error: /widgets/compat.js does not appear to be loaded, and is required for form validation by formops.js.");
	
	var i, el, isDisplayed, reqImg;
	var isFilledOut = Array();
	var elReq;
	
	// If we've just changed 1 field, we don't need to update everything....
	if(changedEl) {
		el = changedEl;
		elReq = compat_getAttribute(el, 'required');
		// ... as long as there isn't a multi-field requiremenet thang going on
		if(elReq == 'required' + el.name) {
			if(el.className)
				el.className = el.className.replace(/ (emptyfield)|(badfield)/,'');
			else el.className = "";

			// __XXXX signifies a control value, eg (Add Item), that should be cleared
			if(typeof el.value == 'string' && el.value.substr(0,2) == "__")
				el.value = "";
			
			if((el.baddata || isEmpty(el)) && el.className != "") el.className += " ";
			if(el.baddata) el.className += (el.className?' ':'') + 'badfield';
			else if(isEmpty(el)) el.className += (el.className?' ':'') + 'emptyfield';
			
			reqImg = formObj[el.name + "_requiredimg"];
			if(reqImg != null) {
				reqImg.src = isEmpty(el) ? "images/required.gif" : "images/notrequired.gif";
			}
			return;
		}
	} 
	
	// Check which groups are filled out	
	for(i=0;i<formObj.elements.length;i++) {
		el = formObj.elements[i];
		elReq = compat_getAttribute(el, 'required');

		if(elReq && isFilledOut[elReq] == null) isFilledOut[elReq] = false;
		
		// __XXXX signifies a control value, eg (Add Item), that should be cleared
		if(typeof el.value == 'string' && el.value.substr(0,2) == "__")
			el.value = "";
		
		if(elReq && (el.value || el.value == "0") && ((el.type != 'checkbox' && el.type != 'radio') || el.checked)) {
			isFilledOut[elReq] = true;			
		}
	}
	
	// Set the empty / not empty information
	for(i=0;i<formObj.elements.length;i++) {
		el = formObj.elements[i];
		elReq = compat_getAttribute(el, 'required');

		if(el.className)
			el.className = el.className.replace(/ (emptyfield)|(badfield)/,'');
		else el.className = "";
		
		if((el.baddata || isEmpty(el, isFilledOut)) && el.className != "") el.className += " ";
		if(el.baddata) el.className += (el.className?' ':'') + 'badfield';
		else if(isEmpty(el, isFilledOut)) el.className += (el.className?' ':'') + 'emptyfield';
		
		reqImg = formObj[el.name + "_requiredimg"];
		if(reqImg != null) {
			reqImg.src = isEmpty(el, isFilledOut) ? "images/required.gif" : "images/notrequired.gif";
		}
			
	}
}


function checkRequired(formObj) {				
	formObj = getFormObj(formObj);		
	if(! formObj) return;	
	
	var i, el, elReq, elTitle, isDisplayed, reqImg, isMissing;
	var isFilledOut = Array();
	var isValidated = Array();
	var missingFields = "";
	var alreadyListed = Array();

	// Check which groups are filled out	
	for(i=0;i<formObj.elements.length;i++) {
		el = formObj.elements[i];
		elReq = compat_getAttribute(el, 'required');
		
		if(elReq && (el.value || el.value == "0") && ((el.type != 'checkbox' && el.type != 'radio') || el.checked))
			isFilledOut[elReq] = true;
	
				
	}

	// Set the empty / not empty information
	for(i=0;i<formObj.elements.length;i++) {
		el = formObj.elements[i];
		elTitle = compat_getAttribute(el, 'humantitle');
		
		if(isEmpty(el, isFilledOut)) {
			// Focus on the first element
			try {
				if(!isMissing) el.focus();
			} catch(er) {}
			isMissing = true;
			// Add a field to the list of items
			
			if(!alreadyListed[elTitle]) {
				if(elTitle != null) missingFields += '\n * ' + elTitle;
				alreadyListed[elTitle] = true;
			}	
			
		} else {
			elValidation = compat_getAttribute(el, 'validation');
			if (elValidation != null) switch (elValidation) {
				case 'password':
					if (passwordField(el) == false) return false;										
					break;								
				
				case 'confirmpassword':
						var passwordValue 		 = formObj.Password.value;
						var confirmPasswordValue = el.value;
						
						if (passwordValue != confirmPasswordValue) {
							alert("The 'Password' and 'Confirm Password' fields are not the same. Please retype them identically.");							
							return false;	
						}	
						el.focus();
						break;
				
				case 'ccexpiry':
					if (ccExpiryValidate(el) == false) {
						el.focus();
						return false;										
					}
					break;
					
				case 'cc':
					if (ccValidate(el) == false) {
						el.focus();
						return false;										
					}
					break;
											
				case 'email':
					if (emailField(el) == false) {
						el.focus();
						return false;										
					}
					break;					
					
				case 'numeric':					
					if (numericField(el) == false) {
						el.focus();
						return false;										
					}
					break;
								
				case '': alert("Incorrect FormSpec: Blank 'validation' provided for "+elTitle); break;
				default: alert('Sorry, support for validating fields as "'+elValidation+'" is not supported for "'+elTitle+'" in formops.js');
			}
			
		}
	}
	
	if(isMissing) {
		var message = 'Fields with red asterisks are required to be filled in. Please give values for:' + 
			(missingFields ? ':\n' + missingFields : '.')
		alert(message);
		return false;
	}
	
	return true;
}

function ccExpiryValidate(el) { 
	// checks the input to ensure its a valid date.
	// Because the user types in a two digit year, 
	// it expects the current year to be between 2000 and 2100.
	
	el.baddata = false;
	if(el.value == "") return;
	el.value = el.value.replace(/[^0-9]/g,'');
	
	if (el.value.length != 4) {
		alert("The expiry date field must be entered in as 12/09 or 1209.");
	}
	
	month = el.value.substr(0,2);
	year = 2000 + parseInt(el.value.substr(2,2),10); // convert string to numeric (base10 :P )
	
	if (month > 12 || month == 0) {
		alert("The first two digits of the expiry date are supposed to be the month (01 to 12), however you entered " + month + ".");
		el.baddata = true;		
		return false;
	}
		
	var currentdate = new Date();	
	if (currentdate.getFullYear() > year) {
		alert("The expiry date you entered is prior to the current year.");
		el.baddata = true;		
		return false;
	}
	if  (currentdate.getFullYear() == year && currentdate.getMonth() +1 > month) {
		alert("You entered an expiry date which has recently expired.");
		el.baddata = true;		
		return false;
	}
	
	if (year - currentdate.getFullYear() > 10) {
		alert("You entered an expiry date more than ten years away.");
		el.baddata = true;		
		return false;
	}	
	
	return true;

}

function ccValidate(el) {
	// overhaul done by SVM, 4 June 2004, to provide issuer check, and a Luhn check which works on AmEx cards.
	el.baddata = false;
	if(el.value == "") return;

	el.value = el.value.replace(/[^0-9]/g,'');
	
	if(el.value.substr(0,4) == "1234") {
		alert("Test CC# used, the server will not be contacted.");
		return;
	}
	
	// Verify issuer	
	var length = el.value.length;	
	var Issuer = '', lenmsg = '';
	var OneDigit = parseInt( el.value.substr(i,1) );
	var TwoDigit = parseInt( el.value.substr(i,2) );	
	var FourDigit= parseInt( el.value.substr(i,4) );
	
	if (OneDigit == '4') {
		Issuer = "Visa";
		if (! (length == 13 || length == 16)) lenmsg = "13 or 16";			
	
	} else if (TwoDigit >= 51 && TwoDigit <= 55) {
		Issuer = "Mastercard";
		if (length != 16) lenmsg = 16;			
	
	} else if (TwoDigit == 34 || TwoDigit == 37) {
		Issuer = "American Express";
		if (length != 15) lenmsg = 15;			
		
	} else if (FourDigit == 3088 || FourDigit == 3096 || FourDigit == 3112 || FourDigit == 3158 || FourDigit == 3337 || FourDigit == 3528) {
		Issuer = "JCBCard";
		if (length != 15) lenmsg = 15;			
		
	} else if (TwoDigit == 30 || TwoDigit == 36 || TwoDigit == 38) {
		Issuer = "Diners Club";
		if (length != 14) lenmsg = 14;			
	
	} else if (FourDigit == 6011) {
		Issuer = "DiscoverCard";
		if (length != 16) lenmsg = 16;			
	
	} else if (FourDigit == 2014 || FourDigit == 2149) {
		Issuer = "enRouteCard";
		if (length != 15) lenmsg = 15;			
	} else {		
		lenmsg = "The start of your card number prefix (" + FourDigit + ") does not appear to be a valid Visa, Mastercard, Amex, DinersClub, Discover, enRoutecard, or JCBCard.";		
		if (length < 13) lenmsg += "\n\nIn addition, a credit card is expected to have atleast 13 digits, but you typed in " + length + " digits.";
		alert(lenmsg);
				
		el.baddata = true;
		el.focus();
		return false;
	}
	
	if (lenmsg) {
		alert(Issuer + " credit cards are expected to be " + lenmsg + " digits long, but you typed in " + length + " digits.");
		el.focus();
		el.baddata = true;
		return false;
	}
	
	if(el.value.length != 13 && el.value.length != 15 && el.value.length != 16) {
		el.baddata = true;		
		alert("That number does not appear to be a valid length for a card");
		el.focus();
		return;
	}
	
	// Luhn check. Verifies the sum of digits in the number (with every second doubled, to a max of 9),
	// equal a multiple of ten.
	var i,product=0,factor=1,sum=0;		
	for(i=el.value.length-1;i >= 0;i--) {				
		product = factor * parseInt(el.value.substr(i,1));
		if (product > 9) product -= 9				
		sum += product						
		factor = 3 - factor; // alternate between 1 and 2.		
	}
		
	if(sum % 10 != 0) {
		el.baddata = true;		
		el.focus();
		alert("The credit card number you typed does not appear to be a valid " + Issuer + " number.");			
	}
}

/*
 * Returns true if the given tag is visible
 */
function isVisible(el) {
	while(el.tagName != "BODY") {
		if(el.style.display == "none") return false;
		el = el.parentNode;
	}
	return true;	
}

function isEmpty(el, isFilledOut) {
	var elReq = compat_getAttribute(el, 'required');
	if(isFilledOut != null) return (elReq && !isFilledOut[elReq] && isVisible(el));
	else return elReq && !el.value && isVisible(el);
}

/*
 * Show and hide fields depending on the status of checkboxes
 */
function updateFieldList(formObj) {
	if(!(formObj = getFormObj(formObj))) return;
	
	var i, el, displayMe;
	for(i=0;i<formObj.elements.length;i++) {
		el = formObj.elements[i];
		if(el.parentNode != null) {
			el.parentNode.parentNode.style.display = isIncluded(el, formObj) ? "block" : "none";
		}
	}
}

function getFormObj(formObj) {
	if(formObj == '') formObj = null;
		
	
	orig = formObj;
	try {
		if(typeof formObj == 'string'){
			formObj = eval('document.' + formObj);
		}
	} catch(er){		
		 formObj = null; 
	}
	
	// Detect mutiple forms using the same name, which means this function doesn't know what to return!
	if ( formObj.tagName != 'FORM' && formObj.length != null) {
		alert ( "SilverStripe has found " + formObj.length + " forms named " + orig + ", so form validation will not work.");	
		return null;
	}	
	
	if(formObj == null) formObj = document.GenericForm;
	if(formObj == null) {
		alert("SilverStripe cannot find a form named '" + (orig ? orig : 'GenericForm') + "' in the template used to create a form on this page. As a result, field-validation will not work. Ensure your form template contains <form ... name='$FormName' ... >.");
		return false;
	}

	if(formObj.elements == null || formObj.elements.length == null) formObj = null;
				
	return formObj;
}

/*
 * Returns true if the given element is included in the 'active' set of elements
 */
function isIncluded(el, formObj) {
	if(el.requiredin != null) {
		return eval('formObj.' + el.requiredin + '.checked');
	} else {
		return true;
	}
}



/*
 * Date entry field
 */
function date_keyDown(el, evt) {
	if(typeof event == "undefined") event = evt;
	event.returnValue = false;

	var tr = document.selection.createRange();

	if(el.template.length > el.value.length) {
		var extraPart = el.template.substr(el.value.length);
		el.value += extraPart;
		tr.moveStart('character', -extraPart.length);
		tr.moveEnd('character', -extraPart.length);
	}

	// if we've not selected a character, select it
	if(tr.text.length == 0) tr.moveEnd('character',1)

	// if we have more than one character selected, select only the first one	
	// this doesn't apply to deleting text
	if(tr.text.length > 1 && event.keyCode != 46) tr.moveEnd('character', -tr.text.length + 1);

	scanCodes = Array();
	scanCodes[191] = "/?";
	scanCodes[220] = "\\|";
	scanCodes[186] = ";:";
	scanCodes[189] = "-_";
	
	var keyPressed;
	if(scanCodes[event.keyCode]) {
		keyPressed = event.shiftKey ? scanCodes[event.keyCode].substr(1,1) : scanCodes[event.keyCode].substr(0,1);
	} else {
		keyPressed = String.fromCharCode(event.keyCode);
	}
	window.status = event.keyCode;
	


	// Let tab,enter,arrowkeys,home,end and anything with ctrl or shift do their thing
	
	if(event.keyCode == 13 || event.keyCode == 9 || (event.keyCode >= 35 && event.keyCode <= 40) || event.ctrlKey || event.shiftKey) {
		event.returnValue = true;

	// Backspace key 
	} else if(event.keyCode == 8) {
		tr.moveStart('character',-1);

		if(tr.text.length > 1) tr.moveEnd('character',-1);
		
		while(String("0123456789_").indexOf(tr.text) == -1) {
			tr.moveStart('character',-1);
			tr.moveEnd('character',-1);
		}
		tr.text = "_";
		tr.moveStart('character',-1);
		tr.moveEnd('character',-1);
		tr.select();

	// Delete key
	} else if(event.keyCode == 46) {
		tr.text = tr.text.replace(/[0-9]/g,'_');

	} else {
		date_insertCharacter(el, keyPressed, tr) 
	}
}




/*
 * Common to paste and keydown, insert a non-control character
 */
function date_insertCharacter(el, keyPressed, tr) {
	if(tr == null) {
		tr = document.selection.createRange();
		if(tr.text.length == 0) tr.moveEnd('character',1)
		if(tr.text.length > 1 && event.keyCode != 46) tr.moveEnd('character', -tr.text.length + 1);
		tr.select();
	}

	while(String("0123456789_").indexOf(tr.text) == -1) {
		tr.moveStart('character',1); tr.moveEnd('character',1);
	}

	// Numeric char
	if(String("0123456789").indexOf(keyPressed) != -1) {				
		if(tr.text.length > 0) tr.text = keyPressed;
	
		tr.moveEnd('character',1);
		while(String("0123456789_").indexOf(tr.text) == -1) {
			tr.moveStart('character',1); tr.moveEnd('character',1);
		}
		tr.select();

	// Separator char						
	} else if(String("-:/\\").indexOf(keyPressed) != -1) {		
		var tr_prev = tr.duplicate();
		tr_prev.moveStart('character',-1);
		tr_prev.moveEnd('character',-1);	

		// If the previous char wasn't a separator, move to after the next char
		var first = true;
		while(String("-:/\\").indexOf(tr_prev.text) == -1) {
			// Overwrite all the characters getting skipped with a _
			if(!first) {
				tr_prev.text = '_';
				tr_prev.moveStart('character',-1);
				tr_prev.select();
			}
			tr_prev.moveEnd('character',1);	
			tr_prev.moveStart('character',1);
			first = false;
		}
		
		// Select the character after that
		tr_prev.moveEnd('character',1);	
		tr_prev.moveStart('character',1);
		tr_prev.select();
	}
}
 
function date_paste(el, evt) {
	if(typeof event == "undefined") event = evt;
	event.returnValue = false;

	if(el == null) el = this;
	
	var tr = document.selection.createRange();

	if(el.template.length > el.value.length) {
		var extraPart = el.template.substr(el.value.length);
		el.value += extraPart;
		tr.moveStart('character', -extraPart.length);
		tr.moveEnd('character', -extraPart.length);
	}
	//if(tr.text.length == 0) tr = el.createTextRange();
	tr.select();
	
	var pasted = clipboardData.getData('text');
	var nextChar;
	while(pasted.length > 0) {
		nextChar = pasted.substr(0,1);		
		pasted = pasted.substr(1);
		date_insertCharacter(el, nextChar);
	}
	
	event.returnValue = false;
}

/***** COMBO BOX *****/

//----------------------------------------------------------------------
// global variables
//----------------------------------------------------------------------
var buffer, menu;

// -- Determine browser
var IE  = (document.all)? true: false;

//----------------------------------------------------------------------
// toggle/relocate menu on screen
//----------------------------------------------------------------------
function comboBox_toggle(ref, m) {
	menu = document.getElementById(m);
	if (menu.style.display == 'block') {
		menu.style.display = 'none';
		return;
	}

   // -- if we're here, relocate the menu object and make it visible
	var r = getRect(ref);

	/*
	if(menu.parentNode != document.body) {
		menu.parentNode.removeChild(menu);
		document.body.appendChild(menu);
	}	

	with (menu.style) {
   	left    = r.left + "px";
      top     = r.bottom + "px";
   }
  */

	with (menu.style) {
      width   = ref.offsetWidth + "px";
      top	   = (ref.offsetHeight - 3) + "px";
      display = 'block';
	}
	

	// -- wouldn't hurt to save a reference to the active combo element
	menu.caller = ref;
}

function comboBox_blur(menu) {
	if(typeof menu == "string") menu = document.getElementById(menu);
	menu.timer = setTimeout('comboBox_close("' + menu.id + '")', 50);
}
function comboBox_focus(menu) {
	if(menu.timer) {
		clearTimeout(menu.timer);
		menu.timer = null;
	}
}

function comboBox_close(m) {
	menu = document.getElementById(m);
	if(menu.timer) {
		clearTimeout(menu.timer);
		menu.timer = null;
	}
	
	menu.style.display = 'none';
	return;
}


//----------------------------------------------------------------------
// search the menu based on the contents of input field t
//----------------------------------------------------------------------
function comboBox_searchList(ref, m) {
	var i;
   var menu = document.getElementById(m);


	// -- decloak if not already visible
   if (menu.style.display == 'none')
   	comboBox_toggle(ref, m);

	// -- loop over all options until we get a match
	for(i=0; i<menu.length; i++) {
		var opt = menu.options[i];
		if (ref.value != opt.text.substr(0, ref.value.length))
			continue;
		opt.selected = true;
		break;
	}
}


//----------------------------------------------------------------------
// Make menu invisible and take action on the selection
//----------------------------------------------------------------------
function comboBox_searchDone(menu) {
	menu.caller.value = menu.value; //menu.options[menu.selectedIndex].text;
	menu.style.display = 'none';
	if(menu.caller.onchange) menu.caller.onchange();
}


//----------------------------------------------------------------------
// borrowed from a future strange code
//----------------------------------------------------------------------
function getRect(obj) {
	rect = getPos(obj);
	// alert(rect.top);
	rect.bottom = rect.top  + obj.offsetHeight;
	rect.right  = rect.left + obj.offsetWidth;
	return rect;
}

var isIE = (navigator.userAgent.indexOf("MSIE") != -1);
var isNetscape = (navigator.userAgent.indexOf("Netscape") != -1);
function getPos(el) {
	if(isNetscape) return { left: el.offsetLeft, top: el.offsetTop };

/*	
	if(isIE && el.style.overflow == "auto") {
		return { left: el.scrollLeft, top: };
	}
*/	
	
	switch(el.tagName.toUpperCase()) {
		case "BODY": 
			return { left: 0, top: 0 };
		case "TR": case "TBODY": 
			return getPos(el.parentNode);
		default: 
		
			if(el.parentNode.tagName.toUpperCase() == "DIV" && el.tagName.toUpperCase() == "DIV")
				return getPos(el.parentNode);
			else {
				prnt = getPos(el.parentNode);			
				prnt.left += el.offsetLeft - el.scrollLeft /* + el.clientLeft */ ;
				prnt.top += el.offsetTop - el.scrollTop /* + el.clientTop */ ;
				return prnt;
			}
	}
}

function goForm(formName, code) {
	formObj = getFormObj(formName);
	if(formObj.action == "") formObj.action = "index.php";
	formObj.action += "?" + code + "=1";
	formObj.submit();
}

var eventHandlersChecked = false;
function checkEventHandlers(formObj) {
	if(eventHandlersChecked) return;	
	eventHandlersChecked = true;
	
	var i;
	for(i=0;i<formObj.elements.length;i++) {
		if(formObj.elements[i].tagName) {			
			if(formObj.elements[i].type == "submit" || formObj.elements[i].type == "button" || formObj.elements[i].type  == "textarea") continue;
			formObj.elements[i].onkeypress = enterToTab;
		}
	}
}

function enterToTab(evt) {
	if(!evt) evt = event;

	if(evt.keyCode == 13) {
		// our very own 'tab' code
		evt.returnValue = false;
		var nextField = getNextElement(evt.srcElement ? evt.srcElement : evt.originalTarget);
		if(nextField !== null) {
			if(nextField.type == 'submit') nextField.click();
			else nextField.focus();
		}			
		return false;
	}
}
function getNextElement(el) {
	if(el == null) return null;
	
	var formObj = el;
	while(formObj.tagName.toUpperCase() != "FORM") {		
		formObj = formObj.parentNode;
		if(formObj.tagName.toUpperCase() == "BODY") return null;
	}
	var good = false;
	
	for(var i=0;i<formObj.elements.length;i++) {
		// if good, only return a visible form element
		if(good) {
			if(formObj.elements[i].type != "hidden" && isVisible(formObj.elements[i]))
				return formObj.elements[i];
			
		// set the good flag, wait for the next visible form element
		} else if(formObj.elements[i] == el) {
			good = true;
		}
	}
	return null;
}

/*
 * Submit the given form, passing the given actioncode as a $_GET in the action URL
 */
function submitFormWithAction(formObj, code) {
	formObj.action = setGetVar(formObj.action, 'action', code);
	formObj.submit();
}
	
/*
 * Set the given get variable in the given URL, returning the updated URL
 * Both varName and varValue must have been previously urlencoded
 */
function setGetVar(formURL, varName, varValue) {
	formURL = formURL.replace(new RegExp("&" + varName + "=[^&]*"),'');
	formURL = formURL.replace(new RegExp("\\?" + varName + "=[^&]&*"),'?');
	formURL = formURL.replace(new RegExp("\\?" + varName + "=[^&]*"), '');
	
	formURL += (formURL.indexOf('?') != -1) ? '&' : '?';
	formURL += varName + "=" + varValue;
	
	return formURL;
}
 
/*
 * Allows a pair of dropdowns to be paired; the first one alters the options found in
 * the second, e.g. the first dropdown may list countries, the second one a number of
 * cities within a country. This function is called when the parent dropdown changes,
 * to update the child dropdown.
 */
var last_selectSubdropdown;
function selectSubdropdown(el, subdropdownName ) {	
	var formElName 	= '_subdropdown_' + subdropdownName + '_' + el.value
	var formEl 			= compat_getElement(formElName);		
	
	if (! formEl) return false;					
	formEl.style.display = 'block';			
	formEl.selectedIndex = 0;
	
	if (last_selectSubdropdown && last_selectSubdropdown.name != formEl.name) {
		last_selectSubdropdown.style.display = 'none';
		last_selectSubdropdown.value = '';
	}
		
	last_selectSubdropdown = formEl;
}