if (document.all){document.write("<style>.hideImgs img {visibility:hidden;}</style>");}
if (document.all){document.write("<style>.lightBox {visibility:hidden;}</style>");}// stop foc in IE
/*ON DOM READY FUNCTIONS*/
window.initiated=false;
function init()	{
	if (window.initiated)	{
		return;
	}
	window.initiated=true;
	Minibasket.init();
	HostingFreeDomains.init();
	popup(); //progressively enhance 'popup' links to open a popup window
	printpage(); // progressively enhance print buttons
	setupExpandableAreas();
	setupExpandableFromSelect();
	switchExpand();
	colClears(2,"product2col","li","product2colitem");
	limitSelectionNumber("product2col","select");
	unselectForm();
	gallery("mainPic","thumbs","standard"); //progressively enhance a picture gallery to show different views
	greyOut();
	//productSummary("summaryHead", "summaryDescription");
	applySifr(); // apply text flash replacement
	hideWithJS(); // 
	pngHandling("progressStatus");
	//your account functions
	accountNamesTruncate(); //cuts account names in the panel title
	inputBorders();   //add border onto input field on focus, specific to your account
	clearFocus(); //clear a field of its initial value on focus
	clearOnClick();//clears a group of fields when a specified link is clicked
	checkAllBoxesOnClick();//checks all boxes in a wrapper div when a specified link is clicked
	contextualHelp(); //progressively enhances the help paragraphs for field focus
	Validator.init();  //validate form inputs
	dependentFields(); //fields are disabled until a specific field has a value
	passwordStrengthCheck();
	paginate(); //turn specified areas into paginated sections
	Lightbox.init(); //set up lightboxes	
	imgSwap(); //progressively enhanced image rollovers
	sum(); //client-side addition of user inputs to get a total
	barChart(); // take a table with currency value and add a bar chart
	toggleView(); //enable switching of displayed content using grouped links
	calendar(); //progressively enhanced calendars
	billSavePrint(); // handle AJAX requests on the save or print bill lightbox
	trHovers();  //background change on mouse over a table row
	searchFocus();
	searchFocusweb();
	inputFocusOnLoad();//find any inputs to focus on when load, do this last	
}



// ondomready code

if (!/Opera/i.test(navigator.userAgent)){
	if (document.addEventListener) {document.addEventListener("DOMContentLoaded", init, null);}
}	else	{
	G_wk = setInterval(function(){if (document.getElementById("Page")) {clearInterval(G_wk);delete G_wk;init();}},1000);
}
if (/WebKit/i.test(navigator.userAgent)){G_wk = setInterval(function(){if (/loaded|complete/.test(document.readyState)) {clearInterval(G_wk);delete G_wk;init();}},50);}
/* DO NOT REMOVE THIS SECTION - IE CONDITIONAL COMPILATION */
/*@cc_on @*/
/*@if (@_win32)
document.write("<script id=el_ie defer src=/static/c/javascript/blank.js><\/script>");var script = document.getElementById("el_ie");script.onreadystatechange = function() {
if (this.readyState == "complete") {init();}}
/*@end @*/
/* IE CONDITIONAL COMPILATION ENDS */
//failsafe
window.onload=init;
/* - */
/*END ON DOM READY FUNCTIONS*/

//onload event
function addLoadEvent(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      oldonload();
      func();
    }
  }
}

window.onunload = function(){
	//kill all event handlers for fields
	var inputs=document.getElementsByTagName("input");
	var selects=document.getElementsByTagName("select");
	for(var i=0;i<inputs.length;i++){
		inputs[i].onblur=null;
		inputs[i].onclick=null;
		inputs[i].onfocus=null;
	}
	for(var s=0;s<selects.length;s++){
		selects[s].onblur=null;
		selects[s].onclick=null;
		selects[s].onfocus=null;
	}
}

/*
Gives focus to a specified element when the page loads.
Focuses on the first one found from input, select then anchor.  If there is more than one, that is a logic error so subsequent elements are ignored
REQUIRED
	focusable element (input, select, anchor) with class 'PE_focusOnLoad'
*/
function inputFocusOnLoad(){
	var el_focus = fuzzyClassName("input","PE_focusOnLoad");
	if(!el_focus){el_focus = fuzzyClassName("select","PE_focusOnLoad")};
	if(!el_focus){el_focus = fuzzyClassName("a","PE_focusOnLoad");}
	if(el_focus.length == 0){return;}
	el_focus[0].focus();
}

// -------------------------------- 
// Rollver code for images
function MM_findObj(n, d) { //v4.01
  var p,i,x;  if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
    d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
  if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
  for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
  if(!x && d.getElementById) x=d.getElementById(n); return x;
}

function MM_swapImgRestore() { //v3.0
  var i,x,a=document.MM_sr; for(i=0;a&&i<a.length&&(x=a[i])&&x.oSrc;i++) x.src=x.oSrc;
}

function MM_preloadImages() { //v3.0
  var d=document; if(d.images){ if(!d.MM_p) d.MM_p=new Array();
    var i,j=d.MM_p.length,a=MM_preloadImages.arguments; for(i=0; i<a.length; i++)
    if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}}
}

function MM_swapImage() { //v3.0
  var i,j=0,x,a=MM_swapImage.arguments; document.MM_sr=new Array; for(i=0;i<(a.length-2);i+=3)
   if ((x=MM_findObj(a[i]))!=null){document.MM_sr[j++]=x; if(!x.oSrc) x.oSrc=x.src; x.src=a[i+2];}
}




// -------------------------------- 
// Clear Input box 
function ClearBox(myObj, myText)
{
	if(myObj.value == myText) myObj.value='';
}




// -------------------------------- 
// Sort code
var autoFocusOn = true;

function autofocus(field, limit, next ) {
	if(autoFocusOn){
		if(next != 'LAST'){
		   	limit = 2;
			if (field.value.length == limit) {
			    if(field.form.elements[next]){field.form.elements[next].focus();}
		    	}
		}else{
			autoFocusOn = false;
		}
	}
}



/*GALLERY
generic gallery code, origins: Mobile
*/
function gallery(mainPicDiv,thumbsDiv,largeNaming){
//allow for many galleries on one page.
	var galleries=getElementsByClassName(document,"div","gallery");
	for(var i=0;i<galleries.length;i++){
		var picArea=getElementsByClassName(galleries[i],"div",mainPicDiv)[0];
		if(!picArea){return;}
		
		var gal_Div=getElementsByClassName(galleries[i],"div",thumbsDiv)[0];
		if(!gal_Div){return;}
		gal_Div.style.display="block";
		var gal_imgs=gal_Div.getElementsByTagName("img");
		var firstImg=gal_imgs[0].src;
		gal_imgs[0].className+=" selected";
		var picMain=picArea.getElementsByTagName("img")[0];
		if(!picMain){picMain=picArea.firstChild;}
		var picObj=document.createElement("img");
		picObj.alt=picMain.alt;
		picObj.src=firstImg.substring(0,firstImg.lastIndexOf("_")+1) + largeNaming + firstImg.substring(firstImg.lastIndexOf("."));
		picArea.replaceChild(picObj,picMain);
		picArea.style.display="block";
		for(var j=0;j<gal_imgs.length;j++){
			var domAnchor=document.createElement("a");
			domAnchor.href="#";
			domAnchor.appendChild(gal_imgs[j].cloneNode(true));
			gal_Div.replaceChild(domAnchor,gal_imgs[j]);
			domAnchor.onclick=function(){
				for(var k=0;k<gal_imgs.length;k++){
					gal_imgs[k].className=gal_imgs[k].className.replace(/selected/g,"");
				}
				this.childNodes[0].className+=" selected";
				var src=this.childNodes[0].src;
				var imgRoot=src.substring(0,src.lastIndexOf("_")+1);
				var imgType=src.substring(src.lastIndexOf("."));
				var fullImg=imgRoot + largeNaming + imgType;
				var picMain=picArea.getElementsByTagName("img")[0];
				var picObj=document.createElement("img");
				picObj.alt="";
				picObj.src=fullImg;
				picArea.replaceChild(picObj,picMain);
				return false;
			}
			domAnchor.onfocus=domAnchor.onclick;
		}
	}
}

function getSelectBoxTotals(parent){
	var totalCount=0;
	var selBoxes=parent.getElementsByTagName("select");
		for(var j=0;j<selBoxes.length;j++){
			totalCount=totalCount + parseInt(selBoxes[j].options[selBoxes[j].selectedIndex].value);
		}
	return totalCount;
}
function getSelectBoxPotential(selBoxes)	{
	var highest=0;
	for (var j=0;j<selBoxes.length;j++)	{
		// get the highest POTENTIAL value of each item - this dictates the maximum number of handsets allowed
		var curQuan=(selBoxes[j].options[selBoxes[j].length-1].value);
		if (curQuan>highest){highest=curQuan;}
	}
	return highest;
}
function limitSelectionNumber(containerId,elemType){
	var container=document.getElementById(containerId);
	if(!container){return;}
	var countInputs=container.getElementsByTagName(elemType);
	var maxCount=getSelectBoxPotential(countInputs);
	for(var j=0;j<countInputs.length;j++){
		countInputs[j].containerParent=container;
		countInputs[j].elemType=elemType;
		countInputs[j].maxCount=maxCount;
		(countInputs[j].addEventListener) ? (countInputs[j].addEventListener("change",boxChange,true)) : 
(countInputs[j].attachEvent("onchange",boxChange));
	}
}

function boxChange(evt){
	var elem=(evt.target) ? (evt.target) : (evt.srcElement);
	var errDivs=getElementsByClassName(elem.containerParent,"div","error");
	for(var k=0;k<errDivs.length;k++){
		errDivs[k].innerHTML="<!--error-->";
		errDivs[k].style.display="none";
	}
	if(elem.elemType=="select"){
		var totals=getSelectBoxTotals(elem.containerParent);
	}
	else{
		var totals=0; //ERROR
		alert("DEVELOPER ERROR - elemType not received, or not accounted for");
	}
	if(totals>elem.maxCount){
		var errormsg="Sorry, you can only select up to " + elem.maxCount + ".  You currently have " + totals + " selected";
		var parent=elem.parentNode;
		while(getElementsByClassName(parent,"div","error").length=="0"){
			parent=parent.parentNode;
			if(!parent || parent.nodeName=="#document"){break;}
		}
		var errDiv=getElementsByClassName(parent,"div","error")[0];
		if(typeof errDiv == "undefined"){return;}
		errDiv.innerHTML=errormsg;
		errDiv.style.display="block";
	}
}

function unselectForm(){
	var aUnsel=getElementsByClassName(document,"p","unselect");
	for(var i=0;i<aUnsel.length;i++){
		var frm=getParentByTagName(aUnsel[i],"form");
		var unselectInput=getElementsByClassName(aUnsel[i],"input","reset");
		for(j=0;j<unselectInput.length;j++){
			var unselectText=unselectInput[j].value;
			aUnsel[i].removeChild(unselectInput[j]);
			var unselectLink=document.createElement("a");
				unselectLink.appendChild(document.createTextNode(unselectText));
				unselectLink.href="#";
				unselectLink.onclick=function(){
					frm.reset();
					//safari does not reset dropdowns
					var selBoxes = document.getElementsByTagName("select");
					for(var k=0;k<selBoxes.length;k++){
						selBoxes[k].selectedIndex=selBoxes[k].options[0];
						if(selBoxes[k].onclick){selBoxes[k].onclick();}
						if(selBoxes[k].onchange){selBoxes[k].onchange();}
					}
					//remove error messages
					var allErrDivs=getElementsByClassName(document,"div","error");
					for(var m=0;m<allErrDivs.length;m++){
						allErrDivs[m].innerHTML="<!--error-->";
						allErrDivs[m].style.display="none";
					}
					return false;
				}
			aUnsel[i].appendChild(unselectLink);
		}
	}
}
function colClears(col,parentID,elemType,elemClass){
	var container=document.getElementById(parentID);
	if(!container){return;}
	var elems=getElementsByClassName(container,elemType,elemClass);
	for(var i=0;i<elems.length;i++){
		if(i % col == 0){
			var clrdiv=document.createElement("div");
			clrdiv.className="clear";
			if(!document.createComment){var clrTxt=document.createTextNode(" ");}
			else{var clrTxt=document.createComment("clear");}
			clrdiv.appendChild(clrTxt);
			elems[i].parentNode.insertBefore(clrdiv,elems[i]);
		}
	}
	return;
}



function greyOut (){
		greyAreas = getElementsByClassName(document, 'div', 'pcodeSearch');
		//assign ability to grey out to link that greys out areas
		for (i=0;i<greyAreas.length;i++){
			var imgrey = getElementsByClassName(document, 'a', 'showGrey');
			for (var j=0;j<imgrey.length;j++){
				function doGrey(){
					greyThese = getElementsByClassName(document, 'input', 'greyMe');
					for(var k=0;k<greyThese.length;k++)
					{
						if(greyThese[k].className == 'greyMe inputWidth4'){
							greyThese[k].disabled = true;
							greyThese[k].className = 'greyMe disabl';
						}
						else {
							greyThese[k].disabled = false;
							greyThese[k].className = 'greyMe inputWidth4';
						}
					}
				}
				imgrey[j].addEventListener ? (imgrey[j].addEventListener("click",doGrey,false)) : (imgrey[j].attachEvent("onclick",doGrey));
			}
		}
}


/*
DOMAINS
	domains with a particular TLD are free when a hosting package is chosen.
	busines pack: .co.uk or .org.uk free
	advanced: .com (except for centralnic's .eu.com, .gb.com or .uk.com) AND 1 of .co.uk or .org.uk free
	Initially, the page will be scanned to count how many free ones a user has and this is compared with whats in the minibasket. Minibasket products are updated when the select boxes are changed
REQUIRED:
	class of PE_hosting on the select box where the user chooses their hosting package
	hosting package set up as a normal minibasket product, with the domain name that the hosting package is for in one of the product description entries

*/

HostingFreeDomains = {
	freeDotUKTLDs:0,
	freeDotComTLDs:0,
	init: function(){
		var hostingSelects = HostingFreeDomains.getHostingFields();
		HostingFreeDomains.getFreeDomainsTally();
		for(var i=0,j=hostingSelects.length;i<j;i++){
			//get the select's UID to retrieve its descriptions, one of which will name the domain that it is for.
			hostingSelects[i].productUID = getPEClassInfo(hostingSelects[i],"PE_product");
			var descs = Minibasket.Products[hostingSelects[i].productUID].desc;
			for(var k=0,m=descs.length;k<m;k++){
				//get the domain product object that the host is for
				if(HostingFreeDomains.getDomainType(descs[k]) != false){
					hostingSelects[i].forDomainProduct = HostingFreeDomains.getDomainProduct(descs[k]);
				}
			}
			if(hostingSelects[i].options[hostingSelects[i].selectedIndex].value != ""){
				HostingFreeDomains.evalProduct(hostingSelects[i].forDomainProduct);
			}
			addEvent(hostingSelects[i],"change",HostingFreeDomains.update);
		}
		HostingFreeDomains.evalPage();
	},
	update: function(){
		HostingFreeDomains.getFreeDomainsTally();
		HostingFreeDomains.evalProduct(this.forDomainProduct);
		HostingFreeDomains.evalPage();
	},
	//evaluates single product against how many free domains are allowed to see if it should be changed to free/paid for
	evalProduct: function(product){
		if(HostingFreeDomains.freeDotUKTLDs == 0 && HostingFreeDomains.freeDotComTLDs == 0){return;}
		if(!product){return false;}
		if(!product.productName){return false;}
		var tldType = HostingFreeDomains.getDomainType(product.productName);
		if(tldType == "isDotUKTLD"){
			if(HostingFreeDomains.freeDotUKTLDs > 0){
				if(product.oneoffCost_OLD){return;}
				HostingFreeDomains.freeDomainProduct(product);
			}
			else if(HostingFreeDomains.freeDotUKTLDs < 0){
				HostingFreeDomains.payDomainProduct(product);
			}
		}
		else if(tldType == "isDotComTLD"){
			if(HostingFreeDomains.freeDotComTLDs > 0){
				if(product.oneoffCost_OLD){return;}
				HostingFreeDomains.freeDomainProduct(product);
			}
			else if(HostingFreeDomains.freeDotComTLDs < 0){
				HostingFreeDomains.payDomainProduct(product);
			}
		}
	},
	//evaluates whole list of products, use sparingly
	evalPage: function(){
		if(HostingFreeDomains.freeDotUKTLDs != 0 || HostingFreeDomains.freeDotComTLDs != 0){
			for(var i in Minibasket.Products){
				HostingFreeDomains.evalProduct(Minibasket.Products[i]);
			}
		}
	},
	//get product object from the minibasket object
	getDomainProduct: function(domainDesc){
		for(var i in Minibasket.Products){
			if(Minibasket.Products[i].productName){
				if(Minibasket.Products[i].productName.indexOf(domainDesc) != -1){
					//this is the product to which the select box applies, check this first
					return Minibasket.Products[i];
				}
			}
		}
		return false;
	},
	//get the TLD type of the domain if it is a .co.uk, .org.uk or .com, exclude everything else including the centralnic .coms
	getDomainType: function(domainURL){
		if(!domainURL){return false;}
		if(domainURL.indexOf(".eu.com") != -1 || domainURL.indexOf(".gb.com") != -1 || domainURL.indexOf(".uk.com") != -1){return false;}
		else if(domainURL.indexOf(".co.uk") != -1 || domainURL.indexOf(".org.uk") != -1){
			return "isDotUKTLD";
		}
		else if(domainURL.indexOf(".com") != -1){
			return "isDotComTLD";
		}
		return false;
	},
	//get the tally of how many free domains and of what type the user should have available
	getFreeDomainsTally: function(){
		var hostingSelects = HostingFreeDomains.getHostingFields();
		var freeUKs = 0;
		var freeComs = 0;
		for(var i=0,j=hostingSelects.length;i<j;i++){
			var selectedOption = hostingSelects[i].options[hostingSelects[i].selectedIndex].text.toLowerCase();
			if(selectedOption.indexOf("business pack") != -1 || selectedOption.indexOf("advanced") != -1){
				freeUKs++;
			}
			if(selectedOption.indexOf("advanced") != -1){
				freeComs++;
			}
		}
		//check products object for products that are already marked out as free
		for(var i in Minibasket.Products){
			if(Minibasket.Products[i].oneoffCost_OLD){
				var tldType = HostingFreeDomains.getDomainType(Minibasket.Products[i].productName);
				if(tldType == "isDotUKTLD"){
					freeUKs--;
				}
				else if(tldType == "isDotComTLD"){
					freeComs--;
				}
			}
		}
		HostingFreeDomains.freeDotUKTLDs = freeUKs;
		HostingFreeDomains.freeDotComTLDs = freeComs;
	},
	// get the fields marked out as being for choosing hosting
	getHostingFields: function(){
		var hostingFields = getElementsByClassName(document,"select","PE_hosting");
		return hostingFields;
	},
	//make a domain product free
	freeDomainProduct: function(product){
		if(!product.oneoffCost_OLD){Minibasket.updateProduct(product.uid,"oneoffCost_OLD",product.oneoffCost);}
		Minibasket.updateProduct(product.uid,"oneoffCost","0.00");
		if(Minibasket.isExistingBasketProduct(product.uid) == true){Minibasket.updateInBasket(product);}
		var tldType = HostingFreeDomains.getDomainType(product.productName);
		if(tldType == "isDotUKTLD") {
			HostingFreeDomains.freeDotUKTLDs--;
		}
		else if(tldType == "isDotComTLD"){
			HostingFreeDomains.freeDotComTLDs--;
		}
	},
	//make a domain product paid for again
	payDomainProduct: function(product){
		if(product.oneoffCost_OLD){
			Minibasket.updateProduct(product.uid,"oneoffCost",product.oneoffCost_OLD);
			delete product.oneoffCost_OLD;
			if(Minibasket.isExistingBasketProduct(product.uid) == true){Minibasket.updateInBasket(product);}
			var tldType = HostingFreeDomains.getDomainType(product.productName);
			if(tldType == "isDotUKTLD") {
				HostingFreeDomains.freeDotUKTLDs++;
			}
			else if(tldType == "isDotComTLD"){
				HostingFreeDomains.freeDotComTLDs++;
			}
		}
	}
}

/*MINIBASKET
All products are retrieved from the page (both those in the minibasket statically and those with form fields for selection in page).  Their details are extracted to create a new Product object, which is then added to the Products object.

REQUIRED:
	PE_Product(uid) is put onto the main product input (supplementary details might be set by a select box, but that does not have PE_Product on it).
	PE_productName(uid) wraps the name for the product that appears in the minibasket.  This can either be on a select box or on an element that wraps CDATA.
	PE_productCost(uid,type=[monthly|oneoff]) wraps the cost for the product.  This can either be in a select box or an element that wraps CDATA.
	The contents of the product cost must start with a GB pound sign and be followed by 2 numbers and optionally a dot and 2 numbers.
OPTIONAL:
	PE_productDesc(uid) is optional description for the product that goes after the name in the minibasket, it can go on more than one element, these are concatenated to form a full description
	PE_productQuantity(uid) is optional and is added to the class name of the select box where the user can choose a quantity

*/


Minibasket = {
	Products: {
		length : 0
	},

	//run at start
	init: function(){
		//get products from page, createProduct for each of them
		var newProducts = Minibasket.getProductFields();
		var basketProducts = Minibasket.getExistingBasketProducts();
		var productQuantities = Minibasket.getProductQuantityFields();
		//get existing ('static') products in the basket before creating new ones so that pre-checked elements don't get done twice
		for(var i=0,j=basketProducts.length;i<j;i++){
			Minibasket.extractProduct(basketProducts[i]);
		}
		for(var i=0,j=newProducts.length;i<j;i++){
			newProducts[i].product = Minibasket.createProduct(newProducts[i]);
		}
		for(var i=0,j=newProducts.length;i<j;i++){
			var quantityFields = fuzzyClassName("select","PE_productQuantity("+newProducts[i].product.uid+")");
			if(quantityFields.length > 0){
				newProducts[i].quantityField = quantityFields[0];
			}
			//CHECKBOX PRODUCTS
			if(newProducts[i].type == "checkbox"){
				addEvent(newProducts[i],"click",Minibasket.checkboxProductAction);
			}
			//RADIO PRODUCTS
			else if(newProducts[i].type == "radio"){
				addEvent(newProducts[i],"click",Minibasket.radioProductAction);
			}
			//SELECT PRODUCTS
			else if(newProducts[i].type == "select-one"){
				addEvent(newProducts[i],"change",Minibasket.selectboxProductAction);
			}
			else{
				continue;
			}
		}
		for(var i=0,j=productQuantities.length;i<j;i++){
			productQuantities[i].uid = getPEClassInfo(productQuantities[i],"PE_productQuantity");
			if(productQuantities[i].className.indexOf("PE_product(") == -1){
				var quantityFor = fuzzyClassName("*","PE_product("+productQuantities[i].uid+")");
				if(quantityFor.length > 0){
					productQuantities[i].quantityFor = quantityFor[0];
				}
			}
			addEvent(productQuantities[i],"change",Minibasket.updateProductQuantity);
		}
		//check page for pre-chosen items
		Minibasket.evalProductsFields();
	},
	//handle checkbox product states
	checkboxProductAction : function(){
		var isInBasket = Minibasket.isExistingBasketProduct(this.product.uid);
		if(this.checked == true){
			if(isInBasket == false){
				Minibasket.addToBasket(this.product);
				if(this.quantityField){
					Minibasket.resetLinkedFields(this.quantityField,true);
					Minibasket.updateProductQuantity.call(this.quantityField);
				}
			}
			else{
				Minibasket.updateInBasket(this.product);
			}
		}
		else{
			if(isInBasket == true){
				Minibasket.removeFromBasket(this.product);
				if(this.quantityField){
					Minibasket.resetLinkedFields(this.quantityField,false);
					Minibasket.updateProductQuantity.call(this.quantityField);
				}
			}
		}
	},
	//handle radio product states
	radioProductAction : function(){
		var radios = getByAttribute(document,"input","name",this.name);
		//loops through radio group, if one of the radios has a product in the basket, overwrite it
		for(var k=0,m=radios.length;k<m;k++){
			if(radios[k].product.uid == this.product.uid){continue;}
			if(Minibasket.isExistingBasketProduct(radios[k].product.uid) == true && this.checked == true){
				Minibasket.updateInBasket(this.product,radios[k].product.uid)
				if(this.quantityField){
					Minibasket.resetLinkedFields(this.quantityField,true);
					Minibasket.updateProductQuantity.call(this.quantityField);
				}
				if(radios[k].quantityField){
					Minibasket.resetLinkedFields(radios[k].quantityField,false);
					Minibasket.updateProductQuantity.call(radios[k].quantityField);
				}
			}
		}
		//if product is not in basket after that loop, then there was nothing to update. add it.
		if(this.checked == true){
			if(Minibasket.isExistingBasketProduct(this.product.uid) == false){
				Minibasket.addToBasket(this.product);
				if(this.quantityField){
					Minibasket.resetLinkedFields(this.quantityField,true);
					Minibasket.updateProductQuantity.call(this.quantityField);
				}
			}
		}
		else{
			if(Minibasket.isExistingBasketProduct(this.product.uid) == true){
				Minibasket.removeFromBasket(this.product);
				if(this.quantityField){
					Minibasket.resetLinkedFields(this.quantityField,false);
					Minibasket.updateProductQuantity.call(this.quantityField);
				}
			}
		}
	},
	//handle select box product states
	selectboxProductAction : function(){
		var key="";
		if(this.className.indexOf("PE_productQuantity") != -1){
			return true;//handle with quantity function below instead
		}
		else if(this.className.indexOf("PE_productName") != -1){
			key = "productName";
			var newValue = this.options[this.selectedIndex].text;
		}
		else if(this.className.indexOf("PE_productDesc") != -1){
			key = "desc";
			var newValue = Minibasket.getDescription(this.product.uid);//this will be an array
		}
		if(key==""){alert("DEV ERROR: no key specified");return false;}
		var isInBasket = Minibasket.isExistingBasketProduct(this.product.uid);
		//remove or update/add
		if(this.options[this.selectedIndex].value == ""){
			if(isInBasket == true){
				Minibasket.removeFromBasket(this.product);
				if(this.quantityField){
					Minibasket.resetLinkedFields(this.quantityField,false);
					Minibasket.updateProductQuantity.call(this.quantityField);
				}
			}
		}
		else{
			//check for cost also existing in the select box
			if(this.className.indexOf("PE_productCost") != -1)
			{
				var cost = Minibasket.getCost(this.options[this.selectedIndex].text);
				var costDetails = getPEClassInfo(this,"PE_productCost");
				var chargeFrequency = costDetails.substring(costDetails.indexOf("type=")+5);
				Minibasket.updateProduct(this.product.uid,chargeFrequency+'Cost',cost);
				if(typeof newValue == "string"){
					newValue = Minibasket.removeCost(newValue);
					newValue = newValue.replace(Minibasket.free_rexp,"");
				}
			}
			Minibasket.updateProduct(this.product.uid,key,newValue);
			//test for existence in the minibasket then update or add
			if(isInBasket == true){
				Minibasket.updateInBasket(Minibasket.Products[this.product.uid]);
			}
			else{
				Minibasket.addToBasket(Minibasket.Products[this.product.uid]);
				if(this.quantityField){
					Minibasket.resetLinkedFields(this.quantityField,true);
					Minibasket.updateProductQuantity.call(this.quantityField);
				}
			}
		}
	},
	updateProductQuantity : function(){
		var quantity = parseFloat(this.options[this.selectedIndex].text);
		var isInBasket = Minibasket.isExistingBasketProduct(this.uid);
		if(quantity == "" || isNaN(quantity)){
			quantity = 0;
		}
		if(quantity > 0){
			Minibasket.updateProduct(this.uid,"quantity",quantity);
			if(isInBasket == false){
				Minibasket.addToBasket(Minibasket.Products[this.uid]);
			}
			else{
				Minibasket.updateInBasket(Minibasket.Products[this.uid]);
			}
		}
		else{
			if(isInBasket == true){
				Minibasket.removeFromBasket(Minibasket.Products[this.uid]);
				if(this.quantityFor){Minibasket.resetLinkedFields(this.quantityFor,false);}
				Validator.nestedValidation();
			}
		}
	},
	resetLinkedFields : function(elem,boolOn){
		if(elem.type == "checkbox" || elem.type == "radio"){
			elem.checked = boolOn;
		}
		else if(elem.type == "select-one"){
			if(boolOn == true && elem.selectedIndex == 0){
				elem.selectedIndex = 1;
			}
			else if(boolOn == false && elem.selectedIndex != 0){
				elem.selectedIndex = 0;
			}
		}
		else{
			return false;
		}
	},
	//extracts a product's details from a minibasket entry
	extractProduct: function(elem){
		var uid = getPEClassInfo(elem,"PE_basketProduct");
		if(!uid){return false;}
		if(!Minibasket.Products[uid]){
			var finalProduct = new Minibasket.Product();
			finalProduct.uid = uid;
			var productSpan = getElementsByClassName(elem,"span","product");
			if(productSpan.length == 0){return false;}
			
			var desc = [];
			var descSpans = getElementsByClassName(elem,"span","desc");
			if(descSpans.length > 0){
				var descTrimmed = descSpans[0].innerHTML.replace(/^\((.+?)\)$/,"$1");
				desc = descTrimmed.split(", ");
			} 
			finalProduct.desc = desc;
			var quantity = 1;
			var quantityRegEx = /\[x(\d+)\]/i;
			if(quantityRegEx.exec(productSpan[0].innerHTML)){
				quantity = quantityRegEx.exec(productSpan[0].innerHTML)[1];
			}
			finalProduct.quantity = quantity;
			finalProduct.productName = productSpan[0].innerHTML.replace(quantityRegEx,"");
			
			Minibasket.Products[uid] = finalProduct;
			Minibasket.Products.length++;
		}
		//product may appear on 2 lists
		var list = elem.parentNode;
		if(list.className.indexOf("oneoff") != -1){
			var frequency = "oneoff";
		}
		else if(list.className.indexOf("monthly") != -1){
			var frequency = "monthly";
		}
		var priceSpan = getElementsByClassName(elem,"span","price");
		if(priceSpan.length == 0){return false;}
		var price = Minibasket.getCost(priceSpan[0].innerHTML);
		Minibasket.Products[uid][frequency+"Cost"] = price;
	},
	//gets either an input or a select
	createProduct: function(elem){
		var uid = getPEClassInfo(elem,"PE_product");
		//create product object once we have uid
		var finalProduct = new Minibasket.Product();
		finalProduct.uid = uid;
		//name
		var nameElem = fuzzyClassName("*","PE_productName("+ uid +")");
		if(nameElem.length < 1){
			alert("DEV ERROR: product has no name marked out");
			finalProduct = null;
			return false;
		}
		var name;
		if(nameElem[0].type == "select-one"){
			name = Minibasket.removeCost(nameElem[0].options[nameElem[0].selectedIndex].text);
			name = name.replace(Minibasket.free_rexp,"");
			if(nameElem[0].className.indexOf("PE_product(") == -1){
				nameElem[0].product = finalProduct;
				addEvent(nameElem[0],"change",function(){
					var name = Minibasket.removeCost(this.options[this.selectedIndex].text);
					name = name.replace(Minibasket.free_rexp,"");
					Minibasket.selectboxProductAction.call(this);
				});
			}
		}
		else{
			name = nameElem[0].innerHTML;
		}
		finalProduct.productName = name;

		//cost and frequency (there may be both types of frequency)
		var costElem = fuzzyClassName("*","PE_productCost("+ uid + ",type");
		/**/if(costElem.length < 1){
			alert("DEV ERROR: product has no cost marked out");
			finalProduct = null;
			return false;
		}
		for(var i=0;i<costElem.length;i++){
			var costDetails = getPEClassInfo(costElem[i],"PE_productCost");
			costElem[i].chargeFrequency = costDetails.substring(costDetails.indexOf("type=")+5);

			var cost="";
			if(costElem[i].type == "select-one"){
				cost = Minibasket.getCost(costElem[i].options[costElem[i].selectedIndex].text);
			}
			else{
				cost = Minibasket.getCost(costElem[i].innerHTML);
			}
			finalProduct[costElem[i].chargeFrequency+'Cost'] = cost;
		}
		//description
		var descElem = fuzzyClassName("*","PE_productDesc("+ uid +")");
		var desc = Minibasket.getDescription(uid);
		for(var a=0,b=descElem.length;a<b;a++){
			if(descElem[a].type == "select-one"){
				if(descElem[a].className.indexOf("PE_product(") == -1){
					descElem[a].product = finalProduct;
					addEvent(descElem[a],"change",function(){
						Minibasket.selectboxProductAction.call(this);
					});
				}
			}
		}
		finalProduct.desc = desc;
		//quantity, change event handled in init()
		var quantityElem = fuzzyClassName("select","PE_productQuantity("+ uid +")");
		var quantity = 1;
		if(quantityElem.length > 0){
			quantity = quantityElem[0].options[quantityElem[0].selectedIndex].text;
			if(isNaN(quantity)){
				quantity = 1;
			}
		}
		finalProduct.quantity = quantity;
		Minibasket.Products[uid] = finalProduct;
		Minibasket.Products.length++;
		return finalProduct;
	},
	updateProduct: function(uid,key,newValue){
		if(!uid || !key){return false;}
		return Minibasket.Products[uid][key]=newValue;
	},
	//removes a product from the products object.  do not use to remove a product from the minibasket, this is only for destroying a product altogether.  removeFromBasket() will remove a product from the basket.
	removeProduct: function(uid){
		if(!uid){return false;}
		Minibasket.removeFromBasket(Minibasket.Products[uid]);
		delete Minibasket.Products[uid];//remove the key and its contents from the hash table
		Minibasket.Products.length--;
	},
	//gets the description from a select box or text, strip out cost details if there
	getDescription : function(uid){
		var descElem = fuzzyClassName("*","PE_productDesc("+ uid +")");
		var aDesc = [];
		var desc="";
		for(var a=0,b=descElem.length;a<b;a++){
			if(descElem[a].type == "select-one"){
				desc = descElem[a].options[descElem[a].selectedIndex].text;
				desc = Minibasket.removeCost(desc);
				desc = desc.replace(Minibasket.free_rexp,"");
			}
			else{
				desc = descElem[a].innerHTML;
			}
			desc = desc.replace(/\s+$/,"");//trim extraneous spaces off the end
			aDesc.push(desc);
		}
		return aDesc;
	},
	//get the products in the page that the user can select
	getProductFields: function(){
		var productInputs = fuzzyClassName("input","PE_product(");
		var productSelects = fuzzyClassName("select","PE_product(");
		productInputs = productInputs.concat(productSelects);
		return productInputs;
	},
	getProductQuantityFields: function(){
		var quantitySelects = fuzzyClassName("select","PE_productQuantity");
		return quantitySelects;
	},
	getExistingBasketProducts: function(){
		var products = fuzzyClassName("li","PE_basketProduct(");
		return products;
	},
	isExistingBasketProduct: function(uid){
		if(!uid){return false;}
		var products = fuzzyClassName("li","PE_basketProduct("+uid+")");
		return (products.length > 0) ? true : false;
	},
	//evaluates the entire product list and updates the basket if necessary.  intensive process, use sparingly.
	evalProductsFields : function(){
		var newProducts = Minibasket.getProductFields();
		for(var i=0,j=newProducts.length;i<j;i++){
			Minibasket.evalProductField.call(newProducts[i]);
		}
		var productQuantities = Minibasket.getProductQuantityFields();
		for(var i=0,j=productQuantities.length;i<j;i++){
			Minibasket.updateProductQuantity.call(productQuantities[i]);
		}
	},
	//evaluates one product input item, useful for calls from non-minibasket functions to force minibasket updates. less intensive than checking the whole page
	evalProductField : function(){
		if(this.type == "checkbox"){
			Minibasket.checkboxProductAction.call(this);
		}
		else if(this.type == "radio"){
			Minibasket.radioProductAction.call(this);
		}
		else if(this.type == "select-one"){
			Minibasket.selectboxProductAction.call(this);
		}
		else{
			return false;
		}
	},
	
	//product object
	Product: function(){
		this.uid;
		this.productName;
		this.desc;
		this.oneoffCost;
		this.monthlyCost;
		this.quantity = 1;
	},
	basketDiv: function(){
		var basketDiv = getElementsByClassName(document,"div","PE_basket_list");
		if(basketDiv.length != 1){return false;}
		return basketDiv[0];
	},
	createBasket: function(){
		var basket = document.createElement("ul");
		var basketDiv = Minibasket.basketDiv();
		var basketPara = basketDiv.getElementsByTagName("p");
		for(var i=0,j=basketPara.length;i<j;i++){
			basketPara[i].className += " nodisplay";
		}
		basketDiv.appendChild(basket);
		return basket;
	},
	createSubBaskets: function(frequency){
		var basketDiv = Minibasket.basketDiv();
		var basketList = basketDiv.getElementsByTagName("ul");
		if(basketList.length == 0){
			basketList = Minibasket.createBasket();
		}
		else{
			basketList = basketList[0];
		}
		var subBaskets = [["oneoff","One-off"],["monthly","Monthly"]];
		for(var s in subBaskets){
			var basketItem = document.createElement("li");	
			var basketSubList = document.createElement("ul");
				basketSubList.className = subBaskets[s][0] + " PE_"+subBaskets[s][0];
			var basketSubTotal = document.createElement("li");
				basketSubTotal.className = "total";
			var basketSubTotalText = document.createElement("span");
				basketSubTotalText.className = "hidden";
				basketSubTotalText.appendChild(document.createTextNode(subBaskets[s][1] + " total"));
			basketSubTotal.appendChild(basketSubTotalText);
			
			var basketSubTotalText = document.createElement("span");
			basketSubTotalText.className = "total";
			basketSubTotalText.appendChild(document.createTextNode("Total"));
			basketSubTotal.appendChild(basketSubTotalText);
			basketSubTotal.appendChild(document.createTextNode(" \u00A30.00"));
			basketSubList.appendChild(basketSubTotal);
			basketItem.appendChild(document.createTextNode(subBaskets[s][1] + " charges (ex VAT)"));
			basketItem.appendChild(basketSubList);
			basketList.appendChild(basketItem);
			if(subBaskets[s][0] == frequency){
				var returnBasket = basketSubList;
			}
		}
		return returnBasket;
	},
	removeBaskets: function(){
		var basketDiv = Minibasket.basketDiv();
		var basketPara = basketDiv.getElementsByTagName("p");
		for(var i=0,j=basketPara.length;i<j;i++){
			basketPara[i].className = basketPara[i].className.replace("nodisplay","");
		}
		var basketList = basketDiv.getElementsByTagName("ul");
		for(var i=0;i<basketList.length;i++){
			basketList[i].parentNode.removeChild(basketList[i]);
		}
	},
	//basket contents
	addToBasket: function(product){
		if(!product){return false;}
		if(arguments.length > 1){
			var addAtIndex = arguments[1];
		}
		var basketDiv = Minibasket.basketDiv();
		if(!(isNaN(product.monthlyCost))){
			addToList("monthly",product);
		}
		if(!(isNaN(product.oneoffCost))){
			addToList("oneoff",product);
		}
		function addToList(frequency,product){
			var chargeList = getElementsByClassName(basketDiv,"ul","PE_"+frequency);
			if(chargeList.length == 0){
				chargeList = Minibasket.createSubBaskets(frequency);
			}
			else{
				chargeList = chargeList[0];
			}
			var newLi = document.createElement("li");
			newLi.className = "clearfix PE_basketProduct(" + product.uid + ")";
			var productSpan = document.createElement("span");
			productSpan.className = "product";
			productSpan.appendChild(document.createTextNode(product.productName));

			if(product.quantity > 1){
				productSpan.appendChild(document.createTextNode(" [x" + product.quantity + "]"));
			}
			newLi.appendChild(productSpan);
			var productPrice = (parseFloat(product[frequency + "Cost"]) * product.quantity).toFixed(2);
			var priceSpan = document.createElement("span");
			priceSpan.className = "price";
			productPrice = productPrice.replace(/(\-)?(\d)/,"$1\u00A3$2");
			priceSpan.appendChild(document.createTextNode(productPrice));
			newLi.appendChild(priceSpan);
				var productDesc = document.createElement("span");
				productDesc.className = "desc";
			if(product[frequency + "Cost"] == 0 || product.desc.length > 0){
				productDesc.appendChild(document.createTextNode("("));
			}
			//add in Included for free products before any other item
			//handle at minibasket level, as product may be included in one cost list but not in the other
			if(product[frequency + "Cost"] == 0){
				productDesc.appendChild(document.createTextNode("Included"));
				if(product.desc.length == 0){
					productDesc.appendChild(document.createTextNode(")"));
				}
				else{
					productDesc.appendChild(document.createTextNode(", "));
				}
			}
			for(var p=0,q=product.desc.length;p<q;p++){
				productDesc.appendChild(document.createTextNode(product.desc[p]));
				if(p==q-1){
					productDesc.appendChild(document.createTextNode(")"));
				}
				else{
					productDesc.appendChild(document.createTextNode(", "));
			}
			
			}
			newLi.appendChild(productDesc);
			if(typeof addAtIndex == "number"){
				var chargeListItems = chargeList.getElementsByTagName("li");
				chargeList.insertBefore(newLi,chargeListItems[addAtIndex]);
			}
			else{
				newLi.className += " last";
				var lastLi = getElementsByClassName(chargeList,"li","last");
				if(lastLi.length > 0){lastLi[0].className = lastLi[0].className.replace("last","")};
				var totalLi = getElementsByClassName(chargeList,"li","total")[0];
				chargeList.insertBefore(newLi,totalLi);
			}
			Minibasket.updateTotals(frequency);
		}
	},
	updateInBasket: function(product){
		if(!product){return false;}
		var productInBasket;
		if(arguments.length > 1){
			var replaceProductUID = arguments[1];
			productInBasket = fuzzyClassName("li","PE_basketProduct("+replaceProductUID+")");
		}
		else{
			productInBasket = fuzzyClassName("li","PE_basketProduct("+product.uid+")");
		}
		for(var i=0,j=productInBasket.length;i<j;i++){
			if(typeof replaceProductUID == "string"){
				productInBasket[i].className = productInBasket[i].className.replace("PE_basketProduct("+replaceProductUID+")","PE_basketProduct("+product.uid+")");
			}
			var productSpan = getElementsByClassName(productInBasket[i],"span","product");
			var priceSpan = getElementsByClassName(productInBasket[i],"span","price");
			var descSpan = getElementsByClassName(productInBasket[i],"span","desc");
			var newProductOutput = product.productName;
			if(product.quantity > 1){
				newProductOutput += " [x" + product.quantity + "]";
			}
			var productPrice;
			if(productInBasket[i].parentNode.className.indexOf("PE_oneoff") != -1){
				productPrice = (parseFloat(product.oneoffCost) * product.quantity).toFixed(2);
				frequency="oneoff";
			}
			if(productInBasket[i].parentNode.className.indexOf("PE_monthly") != -1){
				productPrice = (parseFloat(product.monthlyCost) * product.quantity).toFixed(2);
				frequency="monthly";
			}
			productPrice = productPrice.replace(/(\-)?(\d)/,"$1\u00A3$2");
			var newPriceOutput = productPrice;
			productSpan[0].innerHTML  = newProductOutput;
			priceSpan[0].innerHTML = newPriceOutput;
			//rewrite desc area, with Included first
			if((frequency == "oneoff" && product.oneoffCost == 0) || (frequency == "monthly" && product.monthlyCost == 0) || product.desc.length > 0){
				if(descSpan.length > 0){
					productDesc = descSpan[0];
				}
				else{
					productDesc = document.createElement("span");
					productDesc.className = "desc";
					productInBasket[i].appendChild(productDesc);
				}
				productDesc.innerHTML = "(";
			}
			else{//no included or desc, destroy the span element
				if(descSpan.length > 0){
					descSpan[0].parentNode.removeChild(descSpan[0]);
				}
			}

			if((frequency == "oneoff" && product.oneoffCost == 0) || (frequency == "monthly" && product.monthlyCost == 0)){
				productDesc.innerHTML += "Included";
				if(product.desc.length == 0){
					productDesc.innerHTML += ")";
				}
				else{
					productDesc.innerHTML += ", ";
				}
			}
			for(var p=0,q=product.desc.length;p<q;p++){
				productDesc.innerHTML += product.desc[p];
				if(p==q-1){
					productDesc.innerHTML += ")";
				}
				else{
					productDesc.innerHTML += ", ";
				}
			}
			Minibasket.updateTotals(frequency);
		}
	},
	removeFromBasket: function(product){
		var productInBasket = fuzzyClassName("li","PE_basketProduct("+product.uid+")");
		for(var i=0,j=productInBasket.length;i<j;i++){
			if(productInBasket[i].className.indexOf("last") != -1){
				var prevSibling = productInBasket[i].previousSibling;
				while(prevSibling){
					if(prevSibling.nodeName.toLowerCase() == "li"){
						prevSibling.className += " last";
						break;
					}
					prevSibling = prevSibling.previousSibling;
				}
			}
			var frequency;
			if(productInBasket[i].parentNode.className.indexOf("PE_oneoff") != -1){
				frequency="oneoff";
			}
			else if(productInBasket[i].parentNode.className.indexOf("PE_monthly") != -1){
				frequency="monthly";
			}
			productInBasket[i].parentNode.removeChild(productInBasket[i]);
			Minibasket.updateTotals(frequency);
		}
		var productsLeftInBasket = fuzzyClassName("li","PE_basketProduct(");
		if(productsLeftInBasket.length == 0){Minibasket.removeBaskets();}
		return (j > 0) ? product.uid : false;
	},
	updateTotals: function(frequency){
		if(!frequency){return false;}
		var list = fuzzyClassName("ul","PE_"+frequency);
		if(list.length != 1){return false;}
		list = list[0];
		
		var total = getElementsByClassName(list,"li","total");
		if(total.length == 0){return false;}
		var priceSpans = getElementsByClassName(list,"span","price");
		var subtotal = 0;
		for(var i=0,j=priceSpans.length;i<j;i++){
			subtotal = parseFloat(subtotal) + parseFloat(Minibasket.getCost(priceSpans[i].innerHTML));
		}
		subtotal = subtotal.toFixed(2);
		subtotal = subtotal.replace(/(\-)?(\d)/,"$1\u00A3$2");
		total[0].innerHTML = Minibasket.removeCost(total[0].innerHTML);
		total[0].innerHTML += subtotal;
	},
	VAT_rexp : /\(?(ex|inc) VAT\)?/i,
	cost_rexp : /\-?\u00A3\-?\d+(\.\d{2})?/i,
	free_rexp : /\b(free|included)\b/i,
	//for safari
	amount_rexp : /\-?\d+(\.\d{2})?/i, //this must NOT be global
	//used to remove cost from strings, needed because safari fails to parse unicode characters in regexps correctly
	removeCost : function(str){
		var newStr = str.replace(Minibasket.cost_rexp,"");
		if(str != newStr){newStr = newStr.replace(Minibasket.VAT_rexp,"");return newStr;}
		newStr = newStr.replace(Minibasket.VAT_rexp,"");
		var poundAt = newStr.indexOf("\u00A3");
		if(poundAt == -1){return newStr;}
		var afterPound = newStr.substring(poundAt+1);
		var removeCost = afterPound.replace(Minibasket.amount_rexp,"");
		if(str.charAt(poundAt-1) == "-"){
			poundAt--;
		}
		newStr = newStr.substring(0,poundAt) + removeCost;
		return newStr;
	},
	getCost : function(costInput){
		var cost;
		costEntry = Minibasket.cost_rexp.exec(costInput);
		if(!costEntry){
			if(Minibasket.free_rexp.test(costInput) == true){cost = "0";}
			else{
				if(/WebKit/i.test(navigator.userAgent)){  //safari reg exp engine is broken, handle here
					var poundAt = costInput.indexOf("\u00A3");
					if(poundAt == -1){return false;}
					var afterPound = costInput.substring(poundAt+1);
					var cost = Minibasket.amount_rexp.exec(afterPound);
					if(!cost){return false;}
					cost = cost[0];
					if(costInput.charAt(poundAt-1) == "-"){
						cost = "-" + cost;
					}
				}
				else{
					return false;
				}
			}
		}
		else{
			cost = costEntry[0];
		}
		cost = parseFloat(cost.replace(/\u00A3/,""));
		if(isNaN(cost)){
			cost = 0;
		}
		cost = cost.toFixed(2);
		return cost;
	}
};
/* END MINIBASKET */


/* EXPANDABLE CONTENT FUNCTIONS */
// -------------------------------- 
// Show or hide state of a div and alter the associated icons accordingly
function expandArea(img1,divObj,img2){
	
	// editable information......
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~
	imgPrefix = "../img/buttons/";    // this is the path to the images... 
	hiddenClass = "hiddenArea";
	showingClass = "unhiddenArea";
	
	// images  - these are hard coded image file names.
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	expandIcon = "btn_expand_collapse_right_10x10.gif"; // this is the right facing triangle
	collapseIcon = "btn_expand_collapse_down_10x10.gif"; // this is the downfacing triangle
	minimizeIcon = "btn_info_close_10x11.gif"; // this is the X icon
	infoIcon = 'btn_info_10x11.gif'; // this is the i icon
	//~~~~~~~~~~~ no need to edit below ~~~~~~~~~~~~~~~
	
	//~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~	
	d = document.getElementById(divObj);
	openArray = new Array(collapseIcon,showingClass,minimizeIcon);// stuf showing when div is open
	closedArray = new Array(expandIcon,hiddenClass,infoIcon); // stuf showing when div is closed
	currentClass = d.className;
	if(currentClass == 'hiddenArea') { //alert('div is closed');
		d.className = openArray[1];
		if(document.getElementById(img1)){
			document.getElementById(img1).src = imgPrefix + openArray[0];
			}
		if(document.getElementById(img2)){
			document.getElementById(img2).src = imgPrefix + openArray[2];
			}
	}else if(currentClass == 'unhiddenArea'){//alert('div is open');
		d.className = closedArray[1];
		if(document.getElementById(img1)){
			document.getElementById(img1).src = imgPrefix + closedArray[0];
			}
		if(document.getElementById(img2)){
			document.getElementById(img2).src = imgPrefix + closedArray[2];
			}
	}
}

//function switchClass(obj,newClass){
//	obj.className = newClass;
//	return true;
//	}

// -------------------------------- 

	function accdiv()
	{
		document.getElementById('rad1').checked=true;
		expandArea1('div1','div2');
	    showArea('nextA');
	}

	function userdiv()
	{	
		document.getElementById('rad2').checked=true;
		expandArea1('div2','div1'); 
		showArea('nextA');     

	}





// Show/hide divs
function expandArea1(divShow,divHide){
	d1 = document.getElementById(divShow);
	d2 = document.getElementById(divHide);
	currentClass = d1.className;
	//if(currentClass == 'hiddenArea') {
		//d1.className = 'unhiddenArea';
		//d2.className = 'hiddenArea';
	//} else {
		d1.className = 'unhiddenArea';
		d2.className = 'hiddenArea';
	//}
}
// -------------------------------- 
// -------------------------------- 
// Show divs
function showArea(divShow){
	d1 = document.getElementById(divShow);
	currentClass = d1.className;
	if(currentClass == 'hiddenArea') {
		d1.className = 'unhiddenArea';
	}
}
// -------------------------------- 
// -------------------------------- 


// Hide divs
function hideArea(divShow,div1value){
	
	d1 = document.getElementById(divShow);
	
	
	if(divShow=='div1' && div1value=='CSS') {
		
//if(currentClass == 'unhiddenArea') {
	currentClass = d1.className;
		d1.className = 'unhiddenArea';
//	} 
	} else {
		currentClass = d1.className;
			
		d1.className = 'hiddenArea';
	}
}
function setupExpandableAreas () {
	//find all expandable areas on page
	expandableAreas=fuzzyClassName("div","expandableContent");

	for (var i=0;i<expandableAreas.length;i++)	{
		expandableAreas[i].id = "expandableArea_"+i;
		//update: DB 2008-01-10: added in handling of minimum number of areas to have before expandable handling is activated

		if(getPEClassInfo(expandableAreas[i],"expandableContent") != ""){
			var classInfo = getPEClassInfo(expandableAreas[i],"expandableContent")
			var relatedAreas = 	fuzzyClassName("div","expandableContent("+classInfo+")");
			if(classInfo.indexOf("min-") != -1){
				var minTrigger = classInfo.substring(classInfo.indexOf("-")+1);
				if(relatedAreas.length < minTrigger){
					continue;
				}
			}
		}
		//collapse all expandable areas unless set to open by default
		//update: DB 2008-01-09: added in handling for defaultToOpen
		//update: DB 2008-01-09: changed straight rewrite of classname to appending.
		document.getElementById(expandableAreas[i].id).className += " expandableContentCollapsed";
		if(expandableAreas[i].className.indexOf("defaultToOpen") !=-1){
			document.getElementById(expandableAreas[i].id).className += " expandableContentShow";
			
		}
				
		//remove link from anchor, assign id
		expandableAreaAnchor = getElementsByClassName(expandableAreas[i], 'div', 'shortDesc')[0].getElementsByTagName("a")[0];
		if (typeof expandableAreaAnchor != "undefined") {
			expandableAreaAnchor.id = "expandableAreaAnchor_"+i;
			expandableAreaAnchor.removeAttribute("href");
		}

		//updated: DB 2008-01-09: if there is no content in the open/close link, add open/close buttons
		if(expandableAreaAnchor.innerHTML == "&nbsp;"){
			expandableAreaAnchor.isDynamicText = true;
			if(document.getElementById(expandableAreas[i].id).className.indexOf("expandableContentShow") != -1){
				expandableAreaAnchor.innerHTML = "<span>Close</span>";
			}
			else{
				expandableAreaAnchor.innerHTML = "<span>Open</span>";
			}
		}
		
		//remove 'for' attribute from label
		expandableAreaLabel = getElementsByClassName(expandableAreas[i], 'div', 'shortDesc')[0].getElementsByTagName("label")[0];

		if(typeof expandableAreaLabel != "undefined" && expandableAreaLabel != null) {
			expandableAreaLabel.removeAttribute("for");
		}

		//assign onclick function to open / close expandable areas
		expandableAreaAnchor.onclick = function () {
			elementId = this.id.split("_").reverse()[0]; //puts the element id number at the front of array
			if(this.isDynamicText){
				if(this.innerHTML == "<span>Close</span>"){
					this.innerHTML = "<span>Open</span>";
				}
				else if(this.innerHTML == "<span>Open</span>"){
					this.innerHTML = "<span>Close</span>";
				}
			}
			openOrCloseExpandableContent("expandableArea_" + elementId );
		}
	}
}
function setupExpandableFromSelect(){
	//find all expandable areas on page
	expandableRows=fuzzyClassName("table","expandableTable");
	
	for (var i=0;i<expandableRows.length;i++)	{
		expandableRows[i].id = "expandableRows_"+i;
		//collapse all expandable areas
		document.getElementById(expandableRows[i].id).className = "expandableTable expandableTableCollapsed";
		//assign onchange function to correct select option
		var findIt = expandableRows[i].getElementsByTagName('td');
		for (var l=0;l<findIt.length;l++){
			var tessi = findIt[l].childNodes;
			for (var j=0;j<tessi.length;j++){
				if (tessi[j].className){
					if(tessi[j].className.indexOf('selectExpand') != -1){
						var toop = tessi[j];
						var theElement = expandableRows[i];
						toop.onchange = function() {
							var myindex  = toop.selectedIndex;
							var SelValue = toop.options[myindex].className;
							if (SelValue.indexOf('expandOption') != -1){								
								elementId = theElement.id.split("_").reverse()[0]; //puts the element id number at the front of array
								var whas = ("expandableRows_" + elementId );
								document.getElementById(whas).className = "expandableTable expandableContentShow";
							}
							else{
								elementId = theElement.id.split("_").reverse()[0]; //puts the element id number at the front of array
								var whas = ("expandableRows_" + elementId );
								document.getElementById(whas).className = "expandableTable expandableContentCollapsed";
								
							}
						}
					}
				}
			}
		}
	}
}
function switchExpand (){
	//find all switch expand areas on page
	switchAreas = fuzzyClassName("div","switchableContent");
	var count = 0;
	for (var i=0;i<switchAreas.length;i++)	{
		switchAreas[i].id = "switchArea_"+i;
		//collapse all expandable areas
		document.getElementById(switchAreas[i].id).className = "switchArea hideBothSwitch";	
			
		//assign checked to radio buttons within area
		var allinputs = switchAreas[i].getElementsByTagName('input').length;
		var radios = getElementsByClassName(switchAreas[i], 'input', 'aswitch');
		var theElement = switchAreas[i];
		radios[0].onclick = function(){
			elementId = theElement.id.split("_").reverse()[0]; //puts the element id number at the front of array
			var whas = ("switchArea_" + elementId );
			document.getElementById(whas).className = "switchArea showfirstswitch";
		}
		radios[1].onclick = function(){
			elementId = theElement.id.split("_").reverse()[0]; //puts the element id number at the front of array
			var whas = ("switchArea_" + elementId );
			document.getElementById(whas).className = "switchArea showsecondswitch";
		}
	}
}

//update: DB 2008-01-09: changed handling of classes so that they are appended not overwritten 
//update: DB 2008-01-09: changed conditional logic to detect expandableContentShow
function openOrCloseExpandableContent (id) {
	accountTotalLine(id);
	document.getElementById(id).className=document.getElementById(id).className.replace(" defaultToOpen","");
	if (document.getElementById(id).className.indexOf("expandableContentShow") == -1) {
		document.getElementById(id).className += " expandableContentShow";
	} else {
		document.getElementById(id).className = document.getElementById(id).className.replace(" expandableContentShow","");	
	}
} 



//special case for your account make a payment where border on an adjoining div must be removed when a div is collapsed
function accountTotalLine(id){
	var nextSib = document.getElementById(id).nextSibling;
	while(nextSib){
		if(nextSib.nodeName.toLowerCase() == "div"){
			if(nextSib.className.indexOf("accountTotal") != -1){
				if(nextSib.className.indexOf("collapsed") !=-1){
					nextSib.className = nextSib.className.replace("collapsed","");
				}
				else{
					nextSib.className += " collapsed";
				}
			}		
			break;return;
		}
		nextSib = nextSib.nextSibling;
	}	
}

//mandates
function mandateType(clicked, showme, selected){
	//First hide day of month dropdown
	var hideMe=document.getElementById(showme);
	if(!hideMe){return;}
	hideMe.className='hidden';
	//If correct mandate type, show day of month dropdown
	var fromMe=document.getElementById(clicked);
	fromMe.onchange = function() {
		var myindex  = fromMe.selectedIndex;
		var SelValue = fromMe.options[myindex].value;
		if (SelValue == selected){
			hideMe.className='show';
		}
		else{
			hideMe.className='hidden';
		}
	}	
}
/* end expandable content functions */



/* Pop-up windows */
function openPopup(href)
{
	var rand="window"+Math.floor(Math.random()*9999999999)
	var newWin = window.open(href,rand,"width=600,height=500,titlebar=no,scrollbars=yes");
	newWin.focus();
	return false;
}
function popup(){
	var els=fuzzyClassName("a","popup:");
	for (var i=0;i<els.length;i++)	{
		els[i].onclick = function ()
		{
			this.removeAttribute("target");
			var cc=this.className;var cs=cc.substr(cc.indexOf("popup:")+6,cc.length);
			// QAS variant specifically for this process
			if (cs.indexOf("qas")==0) {
				var dma=(cs.substr(4,cs.indexOf(")")-1)).split(",");
				// get fields
				var grandparent=getParentByTagName(this.parentNode,"div");
				var supp=(grandparent.id=="id_div_sec2")?"_sec2":"";
				window.QAS_opener=(supp=="")?"_sec1":"_sec2";
				var postcode=document.getElementById(window.VF_postcode+supp).value;
				var house=document.getElementById(window.VF_house+supp).value;
				var extras="?postcode="+postcode+"&house="+house;
			}	else	{
				var dma=(cs.substr(1,cs.indexOf(")")-1)).split(",");
				var extras="";
			}
			window.open(this.href+extras,'popupwindow','width='+dma[0]+',height='+dma[1]+',resizable=1,scrollbars=1,true,true');
			return false;
		}
	}
}
/* End Pop-up windows */

//Check for Parallel processing
var processing = false;
function checkprocessing(form) {
    if(!processing) {
        processing = true;
	if (document.getElementById('BtnContinue') != null) {
		document.getElementById('BtnContinue').disabled=true;
	}
        return true;
    } else {
        //alert("Parallel processing not allowed.");
        return false;
    }
}



/*function productSummary(clickme, showme){
	//First hide product description
	var showMe=document.getElementById(showme);
	if(!showMe){
		return;
	}
	showMe.className='hidden';-->
	//If product description clicked
	var clickMe=document.getElementById(clickme);
	if(!clickMe){
		return;
	}
	clickMe.onclick = function(){
		if(showMe.className == 'hidden'){
			showMe.className='show';
			clickMe.firstChild.className='openSummary';
		}
		else{
			showMe.className='hidden';
			clickMe.firstChild.className='show';
		}
	}
}*/

/*applies sifr to targeted elements*/
function applySifr(){
	if(typeof sIFR == "function"){
		sIFR.setup();
		sIFR();
		sIFR.replaceElement("h2.PE_sifr", named({sFlashSrc: "../img/newbt.swf", sColor: "#64379b",sWmode: "transparent"}))
	};
	
}

/*
handles content that should be hidden only if javascript is on
REQUIRED
	element with class 'PE_hidden'
*/
function hideWithJS(){
	var els = getElementsByClassName(document,"*","PE_hidden");
	for(var i=0,j=els.length;i<j;i++){
		els[i].className += " hidden";
	}
}

/*your account 2007/08 DB*/

/* truncate the name of the account that appears in the title area, make the full name appear in the li's title attribute and change the cursor to the pointer
REQUIRED
	li with classname 'PE_accountName'
*/
function accountNamesTruncate(){
	var titleArea = document.getElementById("titleContainer_panel");
	if(!titleArea){return false;}
	var accountNames = getElementsByClassName(titleArea,"li","PE_accountName");
	for(var i=0;i<accountNames.length;i++){
		var newName = truncate(accountNames[i].innerHTML,14);
		accountNames[i].title = accountNames[i].innerHTML;
		accountNames[i].className += " handCursor";
		accountNames[i].innerHTML = newName;
	}
}
/* apply class that adds purple borders to form fields on focus and remove on blur
REQUIRED
	body with class 'youraccount'
EXCEPTIONS
	only text, password or file inputs get the border, other types look bad
 */
function inputBorders(){
	//only apply to your account and SSO pages
	if(getElementsByClassName(document,"body","youraccount").length < 1 && getElementsByClassName(document,"body","sso").length < 1 && getElementsByClassName(document,"body","generic08").length < 1){return;}
	var inputs = document.getElementsByTagName("input");
	var selects = document.getElementsByTagName("select");
	for(var i=0;i<inputs.length;i++){
		if(inputs[i].type != "text" && inputs[i].type != "password" && inputs[i].type != "file"){continue;}
		addEvent(inputs[i],'focus',fieldActive);
		addEvent(inputs[i],'blur',fieldOff);
		

	}
	for(var s=0;s<selects.length;s++){
		addEvent(selects[s],'focus',fieldActive);
		addEvent(selects[s],'blur',fieldOff);
	}
	function fieldActive(){
		if(this.className.indexOf("active")==-1){
			this.className += " active";
		}
	}
	function fieldOff(){
		this.className = this.className.replace("active","");
	}
}

function clearFocus(){
	var clearInputs = getElementsByClassName(document,"input","PE_clearFocus");
	for(var i=0;i<clearInputs.length;i++){
		clearInputs[i].initValue = clearInputs[i].value;
		addEvent(clearInputs[i],"focus",function(){
			if(this.value == this.initValue){
				this.value = "";
			}
		});
	}
}

function searchFocus(){
	var clearInputs1 = getElementsByClassName(document,"input","PE_searchFocus");
	for(var i=0;i<clearInputs1.length;i++){
		clearInputs1[i].value="Start by choosing your domain name..";
		clearInputs1[i].initValue = clearInputs1[i].value;
		addEvent(clearInputs1[i],"focus",function(){
			if(this.value == this.initValue){
				this.value = "www.";
				//prevent IE jumping to beginning
				if(document.selection){
					var range = document.selection.createRange();
					range.moveStart('character', this.value.length);
					range.moveEnd('character', this.value.length);
					range.select();
				}
			}
		});
		
		addEvent(clearInputs1[i],"blur",function(){
			if((this.value == "www.") ||(this.value == "")){
				this.value = "Start by choosing your domain name..";
			}
		});
	}
}

function searchFocusweb(){
	var clearInputs = getElementsByClassName(document,"input","PE_searchFocusweb");
	for(var i=0;i<clearInputs.length;i++){
		clearInputs[i].value="www.";
		clearInputs[i].initValue = clearInputs[i].value;
		addEvent(clearInputs[i],"focus",function(){
			if(this.value == this.initValue){
				this.value = "";
			}
		});
		
		addEvent(clearInputs[i],"blur",function(){
			if(this.value == ""){
				this.value = "www.";
			}
		});
	}
}

/* clearOnClick
clears all fields within the same wrapping div
REQUIRED
	<a> with class PE_clearFields inside same div as wraps the fields to be cleared
	<div> with class PE_clearFields that wraps the fields to be cleared
*/
function clearOnClick(){
	var clearLinks = getElementsByClassName(document,"a","PE_clearFields");
	for(var i=0,j=clearLinks.length;i<j;i++){
		if(clearLinks[i].className.indexOf("nodisplay") != -1){
			clearLinks[i].className = clearLinks[i].className.replace("nodisplay","");
		}
		clearLinks[i].wrapper = getParentByClassName(clearLinks[i],"PE_clearFields");
		if(!clearLinks[i].wrapper){continue;}
		clearLinks[i].inputs = clearLinks[i].wrapper.getElementsByTagName("input");
		clearLinks[i].selects = clearLinks[i].wrapper.getElementsByTagName("select");
		clearLinks[i].onclick = function(){
			for(var k=0,m=this.inputs.length;k<m;k++){
				if(this.inputs[k].type == "radio"){
					this.inputs[k].checked = false;
					if(this.inputs[k].className.indexOf("PE_product") != -1){
						Minibasket.evalProductField.call(this.inputs[k]);
					}
				}
				else if(this.inputs[k].type == "checkbox"){
					this.inputs[k].checked = false;
					if(this.inputs[k].className.indexOf("PE_product") != -1){
						Minibasket.evalProductField.call(this.inputs[k]);
					}
				}
				else{
					this.inputs[k].value = "";
				}
			}
			for(var k=0,m=this.selects.length;k<m;k++){
				this.selects[k].value = "";
				if(this.selects[k].className.indexOf("PE_product") != -1){
					Minibasket.evalProductField.call(this.selects[k]);
				}
			}
			dependentFields();//ensures dependent fields are disabled if this is cleared
			return false;
		}
	}
}
/* checkAllBoxesOnClick
checks all checkboxes within the same wrapping div
REQUIRED
	<a> with class PE_checkAllBoxes inside same div as wraps the checkboxes to be checked
	<div> with class PE_checkAllBoxes that wraps the checkboxes to be checked
*/
function checkAllBoxesOnClick(){
	var checkLinks = getElementsByClassName(document,"a","PE_checkAllBoxes");
	for(var i=0,j=checkLinks.length;i<j;i++){
		if(checkLinks[i].className.indexOf("nodisplay") != -1){
			checkLinks[i].className = checkLinks[i].className.replace("nodisplay","");
		}
		checkLinks[i].wrapper = getParentByClassName(checkLinks[i],"PE_checkAllBoxes");
		if(!checkLinks[i].wrapper){continue;}
		checkLinks[i].inputs = checkLinks[i].wrapper.getElementsByTagName("input");
		checkLinks[i].onclick = function(){
			for(var k=0,m=this.inputs.length;k<m;k++){
				if(this.inputs[k].disabled == true){continue;}
				if(this.inputs[k].type == "checkbox"){
					this.inputs[k].checked = true;
					if(this.inputs[k].className.indexOf("PE_product") != -1){
						Minibasket.evalProductField.call(this.inputs[k]);
					}
				}
			}
			return false;
		}
	}
}
/*contextualHelp finds all paragraphs with the class name PE_help, gets their parent div to find out which field to attach them to, hides and visually enhances them,  and shows them onfocus and hides them onblur 
REQUIRED:
	<p> with class 'PE_help' nested inside a div which also contains the relevant input or select
*/
function contextualHelp(){
	var helpPs = getElementsByClassName(document,"p","PE_help");
	for(var i=0;i<helpPs.length;i++){
		//get parent div before progressively enhancing with more divs
		var parentDiv = getParentByTagName(helpPs[i],"div");
		//help div top
		var helpDiv = document.createElement("div");
			helpDiv.className = "helpDiv";
			var helpDivTop = document.createElement("div");
			helpDivTop.className = "helpDiv-top";
		var helpDivTopL = document.createElement("div");
			helpDivTopL.className = "helpDiv-top-left";
			helpDivTop.appendChild(helpDivTopL);
		var helpDivTopR = document.createElement("div");
			helpDivTopR.className = "helpDiv-top-right";
			helpDivTopL.appendChild(helpDivTopR);
		helpDiv.appendChild(helpDivTop);			
		//help div middle
		var helpDivL = document.createElement("div");
			helpDivL.className = "helpDiv-left";
		var helpDivR = document.createElement("div");
			helpDivR.className = "helpDiv-right";
			helpDivL.appendChild(helpDivR);
			helpDivR.appendChild(helpPs[i]);
		helpDiv.appendChild(helpDivL);	
		//help div base
		var helpDivBase = document.createElement("div");
			helpDivBase.className = "helpDiv-base";
		var helpDivBaseL = document.createElement("div");
			helpDivBaseL.className = "helpDiv-base-left";
			helpDivBase.appendChild(helpDivBaseL);
		var helpDivBaseR = document.createElement("div");
			helpDivBaseR.className = "helpDiv-base-right";
			helpDivBaseL.appendChild(helpDivBaseR);
		helpDiv.appendChild(helpDivBase);
		//hide the help
		helpDiv.className+=" hidden";
		//get all contained fields
		var inputs = parentDiv.getElementsByTagName("input");
		var selects = parentDiv.getElementsByTagName("select");
		function showHelp(){
			var parent = this.offsetParent;
			var parentRelativeTop = 0;
			while(parent){
				if(parent.nodeName=="#document"){break;}
				if(getStyle(parent,"position") == "relative"){
					parentRelativeTop += parent.offsetTop;
				}
				parent = parent.offsetParent;
			}

			var fieldTop = getAbsoluteY(this) - parentRelativeTop;
			this.helpP.className = this.helpP.className.replace(" hidden","");
			//for IE5, parentRelativeTop gets wrong value
			/*@cc_on @*/
			/*@if (@_jscript_version < 5.6)
			return;
			/*@end @*/		
			this.helpP.style.top = fieldTop+"px";
		}
		function hideHelp(){
			this.helpP.className += " hidden";
		}
		function hideAllHelp(){
		
			var helpDivs = getElementsByClassName(document,"div","helpDiv");
			for(var a=0;a<helpDivs.length;a++){
				if(helpDivs[a].className.indexOf("hidden") ==-1){
					helpDivs[a].className += " hidden";
				}
			}
			if(this.helpP.className.indexOf("hidden") != -1){
				var parentRelativeTop = 0;
				var parent = this.helpP.parentNode;
				while(parent){
					if(parent.nodeName=="#document"){break;}
					if(getStyle(parent,"position") == "relative"){
						parentRelativeTop += parent.offsetTop;
					}
					parent = parent.parentNode;
				}
				var fieldTop = getAbsoluteY(this) - parentRelativeTop;
				this.helpP.style.top = fieldTop+"px";
				this.helpP.className = this.helpP.className.replace("hidden","");
			}
		}
		//add event handlers for fields
		for(var n=0;n<inputs.length;n++){
			inputs[n].helpP = helpDiv;
			if(/WebKit/i.test(navigator.userAgent)){  //safari doesn't focus/blur radio or checkboxes
				if(inputs[n].type == "radio" || inputs[n].type == "checkbox"){
					addEvent(inputs[n],'click',hideAllHelp);
				}
				else{
					addEvent(inputs[n],'focus',hideAllHelp);
				}
			}
			else{
				addEvent(inputs[n],'focus',showHelp);
			}
			addEvent(inputs[n],'blur',hideHelp);
		}
		for(var s=0;s<selects.length;s++){
			selects[s].helpP = helpDiv;
			if(/WebKit/i.test(navigator.userAgent)){  //safari doesn't focus/blur radio or checkboxes
				addEvent(selects[s],'focus',hideAllHelp);
			}
			else{
				addEvent(selects[s],'focus',showHelp);
			}
			addEvent(selects[s],'blur',hideHelp);
		}
		parentDiv.appendChild(helpDiv);
	}
}

/*
dependentFields deals with fields that are dependent on another field having a value.  if it has no value, the fields are disabled so the user does not input unnecessary data and the fields are not validated
e.g. credit card details when entering a cash amount and not evoucher
REQUIRED
	input or select with class PE_depended(identifer)
	div wrapping the affected fields with class PE_dependent(identifier)
*/
function dependentFields(){
	var inputs = fuzzyClassName("input","PE_depended");
	var selects = fuzzyClassName("select","PE_depended");
	for(var i=0,j=inputs.length;i<j;i++){
		if(inputs[i].value == ""){
			updateDependence.call(inputs[i]);
		}
		addEvent(inputs[i],"keyup",updateDependence);
	}
	for(var k=0,m=selects.length;k<m;k++){
		if(selects[k].value == ""){
			updateDependence.call(selects[k]);
		}
		addEvent(selects[k],"change",updateDependence);
	}
	
	function updateDependence(){
		var identifier = getPEClassInfo(this,"PE_depended");
		var wrapperDivs = fuzzyClassName("div","PE_dependent("+identifier+")");
		if(this.value == ""){
			for(var a=0,b=wrapperDivs.length;a<b;a++){
				Validator.disableChildFields(wrapperDivs[a],true);
				//clear error divs
				var errDivs = fuzzyClassNameBlock(wrapperDivs[a],"div","PE_error");
				for(var er=0;er<errDivs.length;er++){
					Validator.clearError(errDivs[er]);
				}
			}
		}
		else{
			for(var a=0,b=wrapperDivs.length;a<b;a++){
				Validator.disableChildFields(wrapperDivs[a],false);
			}
		}
		//run so that if a dependent field is a nestparent, nests are dealt with too
		Validator.nestedValidation();
	}
}


/*	VALIDATION
if a field has an error, the class PE_error will be added to its parent div

REQUIRED:
	div around the field with class PE_validate(type) where type is one of the entries 

	if there is no entry it will be checked for blank content only
	
	if the field can be either a specific type OR blank the the classname should end 'sometype_or_empty' where sometype matches the types in validationData
OPTIONAL:
	LIGHTBOX DISPLAY ON SUBMIT
	class "PE_lightboxIfInvalid(id_of_lightbox)" on the same div as carries the PE_validate(type) class to display a lightbox on form submission if a field is invalid
		
	NESTED VALIDATION
	nested validation means that some fields should only be validated if a 'parent' field is checked
	IMPLEMENTATION:
		the radio, checkbox or select element that is the parent should have the class 'PE_nestValidation'
		the nested fields should be contained in a div with class 'PE_nestedValidation(id_of_parent)'
		
	ONE IN GROUP MUST BE VALID
	this checks that at least one entry in a specified group of fields has content.  use for groups of fields like textboxes, radio groups are already handled by their name
	IMPLEMENTATION:
		wrapping div with class PE_validOneInGroup
*/
Validator = {
	
	//hash of error checking functions and messages
	//max err message length 42-48 characters
	validationData : function() { 
		//generic checking for empty fields, message string determined by field type
		this.empty={
			"errormsg" : {
				"radio" : "Please choose an option",
				"checkbox" : "Please check this box",
				"checkbox-group" : "Please check at least one box",
				"checkbox-terms" : "Please check terms and conditions and privacy policy",
				"text" : "Mandatory field",
				"password" : "Mandatory field",
				"file" : "Mandatory field",
				"select-one" : "Mandatory field",
				"select-multiple" : "Mandatory field",
				"group" : "Please fill in at least one field"
			},
			"logic" : Validator.valtype_empty
		};
		this.match = {
			"errormsg" : "Does not match",
			"logic" : Validator.valtype_match
		};
   		this.globalError={
			"errormsg" : "Please ensure all required information is complete. See below for details.",
			"logic" : "n/a"
		};
		this.bt_account={
			"errormsg" : "Not a valid BT account number",
			"logic" : Validator.valtype_bt_account
		};
		this.card_expiry={  //to ensure date is in the future
			"errormsg" : "Expiry date must be in the future",
			"logic" : Validator.valtype_cardExpiry
		};
		this.card_start={ // to ensure date is in the past
			"errormsg" : "Start date must be in the past",
			"logic" : Validator.valtype_cardStart
		};
		this.currency={
			"errormsg" : "Please enter in the format pounds.pence",
			"logic" : Validator.valtype_currency
		};
		this.ddate={
			"errormsg" : "That is not a valid date",
			"logic" : Validator.valtype_date
		};
		this.daterange={
			"errormsg" : "Invalid date range",
			"logic" : Validator.valtype_daterange
		};
		this.email={
			"errormsg" : "Please enter a valid email address",
			"logic" : Validator.valtype_email
		};
		this.fullname={
			"errormsg" : "Please enter your name",
			"logic" : Validator.valtype_names
		};
		this.number={
			"errormsg" : "Please enter numbers only",
			"logic" : Validator.valtype_number
		};
		this.password_min6={
			"errormsg" : "Your password must be at least 6 characters long",
			"logic" : Validator.valtype_minlength_6
		};
		this.postcode={
			"errormsg" : "Not a postcode",
			"logic" : Validator.valtype_postcode
		};
		this.securitycode={
			"errormsg" : "Please enter your 3 or 4 digit security code",
			"logic" : Validator.valtype_securitycode
		};
		this.sortcode_threepart={
			"errormsg" : "Please enter your 6-digit sortcode",
			"logic" : Validator.valtype_sortcode_threepart
		};
		this.telephone={
			"errormsg" : "Not a phone number",
			"logic" : Validator.valtype_phone
		};
		this.max10={
			"errormsg" : "Maximum length 10 characters",
			"logic" : Validator.valtype_maxlength_10
		};
	},
	// handle nested validation by disabling nested fields inside unchecked or disabled inputs / selects

	nestedValidation : function(){
		for(var n=0;n<Validator.nestParents.length;n++){
			var nests = fuzzyClassName("div","PE_nestedValidation(" + Validator.nestParents[n].id + ")");
			if(nests.length > 0){
				var nest = nests[0];
				if(Validator.nestParents[n].type == "radio" || Validator.nestParents[n].type == "checkbox"){
					disabledTrigger = !Validator.nestParents[n].checked;
				}
				else if(Validator.nestParents[n].type == "select-one"){
					var selectedValue = Validator.nestParents[n].options[Validator.nestParents[n].selectedIndex].value;
					disabledTrigger = (selectedValue == "");
				}
				Validator.disableChildFields(nest,disabledTrigger);
				if(Validator.nestParents[n].disabled == true){Validator.disableChildFields(nest,true);}
				// if parent is not checked or is disabled, clear error messages
				if(disabledTrigger || Validator.nestParents[n].disabled == true){
					var errDivs = fuzzyClassNameBlock(nest,"div","PE_error");
					for(var er=0;er<errDivs.length;er++){
						Validator.clearError(errDivs[er]);
					}
				}
			}
		}
	},
	//takes parent element and true (to disable) or false (to enable) as args
	disableChildFields : function(el,bool){
		var inputs = el.getElementsByTagName("input");
		var selects = el.getElementsByTagName("select");
		for(var i=0;i<inputs.length;i++){
			inputs[i].disabled = bool;
			if(bool){
				inputs[i].className = inputs[i].className.replace("active","");
				if(inputs[i].className.indexOf("PE_product") != -1){
					Minibasket.resetLinkedFields(inputs[i],false);
					Minibasket.evalProductField.call(inputs[i]);
				}
			}
			if(inputs[i].type == "checkbox" || inputs[i].type == "radio"){
				continue;
			}
			if(!bool){
				inputs[i].className = inputs[i].className.replace("disabled","");
			}
			else if(inputs[i].className.indexOf("disabled") == -1){
				inputs[i].className += " disabled";
			}
		}
		for(var s=0;s<selects.length;s++){
			selects[s].disabled = bool;
			if(bool){
				selects[s].className = selects[s].className.replace("active","");
				//leave Minibasket selects as they are, nested should not have a null state
			}
			if(!bool){
				selects[s].className = selects[s].className.replace("disabled","");
			}
			else if(selects[s].className.indexOf("disabled") == -1){
				selects[s].className += " disabled";
			}
		}
	},
	//set up nestParent changes, field blur and form submit event handlers to handle validation
	validationEventTriggers : function(){
		//nestParent clicks
		
		for(var n=0;n<Validator.nestParents.length;n++){
			//if nestParent is a radio button, there may be other related radio buttons without nested fields 
			if(Validator.nestParents[n].type == "radio"){
				var radios = getByAttribute(document,"input","name",Validator.nestParents[n].name);
				for(var i=0;i<radios.length;i++){
					//using addEvent in IE runs a lot of CPU for this command.  make this the primary function of these buttons.
					radios[i].onclick = Validator.nestedValidation;
				}
			}
			else if(Validator.nestParents[n].type == "select-one"){
				Validator.nestParents[n].onchange = Validator.nestedValidation;
			}
			else{
				Validator.nestParents[n].onclick = Validator.nestedValidation;
			}
		}		
		//field blurs
		var validateParentDivs = fuzzyClassName("div","PE_validate");
		for(var d=0;d<validateParentDivs.length;d++){
			var inputs = validateParentDivs[d].getElementsByTagName("input");
			var selects = validateParentDivs[d].getElementsByTagName("select");
			for(var i=0;i<inputs.length;i++){
				inputs[i].validateType = getPEClassInfo(validateParentDivs[d],"PE_validate");
				if(inputs[i].validateType.indexOf("_or_empty") != -1){
					inputs[i].allowEmpty = true;
					inputs[i].validateType = inputs[i].validateType.replace("_or_empty","");
				}
				if(inputs[i].validateType.indexOf("emptyMessage_") != -1){
					var substrStart = inputs[i].validateType.indexOf("emptyMessage_")+13;
					var emptyMessage = inputs[i].validateType.substring(substrStart);
					var emptyMessageLength = emptyMessage.search(/\w\b/);
					emptyMessage = emptyMessage.substring(0,emptyMessageLength+1);
					inputs[i].specificEmptyMessage = emptyMessage;
					inputs[i].validateType = inputs[i].validateType.replace("emptyMessage_"+emptyMessage,"");
				}
				if(inputs[i].validateType.indexOf("match=") != -1){
					var regexpMatch = /match=([^,\)]+)[,]?/gi;
					var fieldMatch = inputs[i].validateType.match(regexpMatch).toString();
					inputs[i].matchToField = fieldMatch.replace(regexpMatch,"$1");
					inputs[i].validateType = inputs[i].validateType.replace(regexpMatch,"");
				}
				
				inputs[i].parentDiv = validateParentDivs[d];
				if(inputs.length > 1){
					inputs[i].allSiblings = inputs;
				}
				addEvent(inputs[i],'focus',Validator.clearError);
				addEvent(inputs[i],'blur',Validator.validate);
				if(inputs[i].matchToField) {
					var fieldMatch = document.getElementById(inputs[i].matchToField);
					fieldMatch.matchBind = inputs[i];
					addEvent(fieldMatch,'blur',function(){
						Validator.validate.apply(this.matchBind);
					});
				}
				if((inputs[i].type=="radio" || inputs[i].type=="checkbox") && (/WebKit/i.test(navigator.userAgent))){  //safari doesn't focus/blur radio or checkboxes
					addEvent(inputs[i],'click',Validator.validate);
				}
			}
			for(var s=0;s<selects.length;s++){
				selects[s].validateType = getPEClassInfo(validateParentDivs[d],"PE_validate");
				if(selects[s].validateType.indexOf("_or_empty") != -1){
					selects[s].allowEmpty = true;
					selects[s].validateType = selects[s].validateType.replace("_or_empty","");
				}
				if(selects[s].validateType.indexOf("match=") != -1){
					var regexpMatch = /match=([^,\)]+)[,]?/gi;
					var fieldMatch = selects[s].validateType.match(regexpMatch).toString();
					selects[s].matchToField = fieldMatch.replace(regexpMatch,"$1");
					selects[s].validateType = selects[s].validateType.replace(regexpMatch,"");
				}
				selects[s].parentDiv = validateParentDivs[d];
				if(selects.length > 1){
					selects[s].allSiblings = selects;
				}
				addEvent(selects[s],'focus',Validator.clearError);
				addEvent(selects[s],'blur',Validator.validate);
			}
		}
		//submit button
		//if submit button is in a lightbox, only validate the lightbox contents
		var submitElems = getElementsByClassName(document,"a","PE_submitFormLink");
		if(!submitElems){submitElems = [];}
		var submitInputs = document.getElementsByTagName("input");

		for(var a=0;a<submitInputs.length;a++){
			if((submitInputs[a].type == "submit" || submitInputs[a].type == "image" || submitInputs[a].type == "button")){
				submitElems.push(submitInputs[a]);
			}
		}	
		for(var n=0,p=submitElems.length;n<p;n++){
			if(getParentByClassName(submitElems[n],"PE_lightBox") != false){
				submitElems[n].onclick = function(){
					var lightbox = getParentByClassName(this,"PE_lightBox");
					return Validator.validateChildren(getParentByClassName(this,"PE_lightBox"));
				}
			}
			else{
				submitElems[n].onclick = function(){
					return Validator.validateChildren(getParentByTagName(this,"form"));
				}
			}
		}

		//form submit
		//1 - validate only ENABLED fields If anything returns as invalid, isAllValid flag is set to false
		//1b - show lightboxes (where set) to display if the result is invalid
		//2 - scrub data from disabled fields so they are not passed to the server
		//3 - return false if not all enabled fields are valid
		/*var forms = document.getElementsByTagName("form");
		for(var f=0;f<forms.length;f++){
			forms[f].onsubmit = function(){
				//return Validator.validateChildren(this);
			}
		}*/
	},
	//validates the child fields of a given container object 
	validateChildren : function(container){
		var isAllValid=true;
		var validateParentDivs = fuzzyClassNameBlock(container,"div","PE_validate");
		for(var d=0;d<validateParentDivs.length;d++){
			var inputs = validateParentDivs[d].getElementsByTagName("input");
			var selects = validateParentDivs[d].getElementsByTagName("select");

			for(var i=0;i<inputs.length;i++){
				if(inputs[i].disabled != true){
					if(Validator.validate.call(inputs[i]) == false){
						if(validateParentDivs[d].className.indexOf("PE_lightboxIfInvalid") != -1){
							var thisLightbox = document.getElementById(getPEClassInfo(validateParentDivs[d],"PE_lightboxIfInvalid"));
							Lightbox.showLightbox(thisLightbox);
							Lightbox.tintBg();
						}
						isAllValid = false;
					}
				}
				else{
					inputs[i].value="";
					inputs[i].checked=false;
				}
			}
			for(var s=0;s<selects.length;s++){
				if(selects[s].disabled != true){
					if(Validator.validate.call(selects[s]) == false){
						if(validateParentDivs[d].className.indexOf("PE_lightboxIfInvalid") != -1){
							var thisLightbox = document.getElementById(getPEClassInfo(validateParentDivs[d],"PE_lightboxIfInvalid"));
							Lightbox.showLightbox(thisLightbox);
							Lightbox.tintBg();
						}
						isAllValid = false;
					}
				}
				else{
					selects[s].selectedIndex = 0;
				}
			}
		}				
		//handle validation on 'standalone' inputs not in one container validation div
		//built for handling grouped inputs, such as radio buttons or checkboxes sharing a name
		var validateInputs = fuzzyClassNameBlock(container,"input","PE_validate");
		var groupName = "";
		for(var s=0;s<validateInputs.length;s++){
			if(validateInputs[s].disabled == true){continue;}//don't validate disabled fields
			validateInputs[s].validateType = getPEClassInfo(validateInputs[s],"PE_validate");
			if(validateInputs[s].validateType.indexOf("_or_empty") != -1){
				validateInputs[s].allowEmpty = true;
				validateInputs[s].validateType = validateInputs[s].validateType.replace("_or_empty","");
			}
			if(groupName == validateInputs[s].name){continue;}
			if(!validateInputs[s].parentDiv){
				var errTop = document.createElement("div");
					errTop.className = "errorTop";
				validateInputs[s].parentNode.insertBefore(errTop,validateInputs[s].parentNode.firstChild);
				validateInputs[s].parentDiv = errTop;
			}
			groupName = validateInputs[s].name;
			if(Validator.validate.call(validateInputs[s]) == false){
				isAllValid = false;
				var groupFields = getByAttribute(document,"input","name",groupName);
				for(var g=0;g<groupFields.length;g++){
					groupFields[g].parentDiv = validateInputs[s].parentDiv;
					addEvent(groupFields[g],'blur',Validator.validate);
					if((/WebKit/i.test(navigator.userAgent))){ //safari doesn't focus/blur radio or checkboxes
						addEvent(groupFields[g],'click',Validator.validate);
					}
				}
			}
		}
		//testing a section to see that at least one element is filled in
		var sections = fuzzyClassNameBlock(container,"div","PE_validOneInGroup");
		for(var i=0;i<sections.length;i++){
			if(!sections[i].errorDiv){
				var errTop = document.createElement("div");
				errTop.className = "errorTop";
				sections[i].insertBefore(errTop,sections[i].firstChild);
				sections[i].errorDiv = errTop;
			}
			if(!Validator.validateGroup(sections[i])){
				isAllValid = false;
			}
		}
		
		if(!isAllValid && Lightbox.active < 1){
			//get first error and jump to it
			var firstError = getElementsByClassName(container,"div","PE_error")[0];
			var firstErrorYPos = getAbsoluteY(firstError);
			window.scrollTo(0,firstErrorYPos-20);
			if(firstError.getElementsByTagName("input").length > 0){
				firstError.getElementsByTagName("input")[0].focus();
			}
			else if(firstError.getElementsByTagName("select").length > 0){
				firstError.getElementsByTagName("select")[0].focus();
			}
			else{
				var aErr = document.createElement("a");
				aErr.href="#";
				aErr.onclick = function(){return false;}
				aErr.appendChild(document.createTextNode("Errors in form."));
				firstError.insertBefore(aErr,firstError.firstChild);
				aErr.focus();
				
				aErr.className = "hidden";
			}
		}
		
		return isAllValid;
	},
	//validate a field and its siblings if it has them
	validate : function(){
		if(this.disabled == true){return;}//do not validate disabled fields
		if(this == window){return;} //prevent calls of validate without a field object
		var isValid = true;
		//if it is empty and is allowed to be empty, stop validating and return true
		if(this.allowEmpty && validData["empty"].logic(this)){return true;}
		isValid = !validData["empty"].logic(this);
		var emptyMessage = this.type;  //error message for blank depends on type
		if(this.type == "checkbox"){
			var checkboxes = getByAttribute(document,"input","name",this.name);
			if(checkboxes.length > 1){
				emptyMessage = "checkbox-group";
			}
		}
		if(this.specificEmptyMessage){
			emptyMessage = this.type + "-" + this.specificEmptyMessage;
		}
		var errorMessage = validData["empty"].errormsg[emptyMessage];
		//check siblings for empty now otherwise we might get a misleading error message
		if(isValid && this.allSiblings){
			for(var s=0;s<this.allSiblings.length;s++){
				if(this == this.allSiblings[s]){continue;}//skip self
				if(this.allSiblings[s].type == "image" || this.allSiblings[s].type == "submit" || this.allSiblings[s].type == "clear" || this.allSiblings[s].type == "button" || this.allSiblings[s].disabled == true){continue;}
				var isValid = !validData["empty"].logic(this.allSiblings[s]);
				if(!isValid){break;}
			}
		}
		if(isValid && this.validateType){
			isValid = validData[this.validateType].logic(this);
			errorMessage = validData[this.validateType].errormsg;
			if(isValid && this.allSiblings){
				for(var s=0;s<this.allSiblings.length;s++){
					if(this == this.allSiblings[s]){continue;}//skip self
					if(this.allSiblings[s].type == "image" || this.allSiblings[s].type == "submit" || this.allSiblings[s].type == "clear" || this.allSiblings[s].type == "button"){continue;}
					isValid = validData[this.allSiblings[s].validateType].logic(this.allSiblings[s]);
					if(!isValid){
						errorMessage = validData[this.allSiblings[s].validateType].errormsg;
						break;
					}
				}
			}
		}
		if(isValid && this.matchToField){
			isValid = validData["match"].logic(this);
			errorMessage = validData["match"].errormsg;
		}
		if(!isValid) {Validator.writeError(this.parentDiv,errorMessage);return false;}
		else {Validator.clearError(this.parentDiv);return true;}
		
	},
	//check a whole group for at least one filled-in field
	validateGroup : function(wrapper){
		var isValid = false;
		var sectionInputs = wrapper.getElementsByTagName("input");
		var sectionSelects = wrapper.getElementsByTagName("select");
		for(var i=0;i<sectionInputs.length;i++){
			if(!validData["empty"].logic(sectionInputs[i])){isValid=true;break;}
		}
		for(var s=0;i<sectionSelects.length;s++){
			if(!validData["empty"].logic(sectionSelects[s])){isValid=true;break;}
		}
		if(!isValid){Validator.writeError(wrapper.errorDiv,validData["empty"].errormsg["group"]);return false;}
		else{Validator.clearError(wrapper.errorDiv);return true;}
	},
	//takes parent div of input as argument
	//write out error if one isn't already there
	writeError : function(el,msg){
		if(el.className.indexOf("PE_error") != -1){
			var message = getElementsByClassName(el,"span","PE_error")[0];
			//check if error text needs updating
			if(message.innerHTML.indexOf(msg) == -1){
				var messageAsString = message.innerHTML.toLowerCase()
				var regx = /(<img[^>]+?>).+/gi;
				message.innerHTML = messageAsString.replace(regx,"$1" + msg);
			}
			return;
		}

		var errImg = document.createElement("img");
			errImg.src = "../img/icons/ico_alert_ya.gif";
			errImg.alt = "Error: ";
		var errMsgTxt = document.createTextNode(msg);
		var errMsg = document.createElement("span");
			errMsg.className = "PE_error clearfix";
			errMsg.appendChild(errImg);
			errMsg.appendChild(errMsgTxt);
		el.className += " PE_error";
		var errMsgDiv = document.createElement("div");
			errMsgDiv.className = "PE_errWrapper clearfix";
			errMsgDiv.appendChild(errMsg);
		el.insertBefore(errMsgDiv,el.firstChild);
	},

	//takes parent div of input as argument
	//remove error if one is there
	clearError : function(el){
		if(el.target || el.srcElement){
			var target = (el.target) ? el.target : el.srcElement;
			el = target.parentDiv;
		}
		if(el.className.indexOf("PE_error") == -1){return;}
		var errMsg = getElementsByClassName(el,"div","PE_errWrapper")[0];
		el.removeChild(errMsg);
		el.className = el.className.replace("PE_error","");
	},
	
	//VALID TYPES

	//returns true if it is a checkbox which is unchecked or a checkbox group with none checked, or a radio button group with none selected or a field with an empty value
	valtype_empty : function(field){
		if(field.type=="checkbox"){
			var checkboxes = getByAttribute(document,"input","name",field.name);
			if(checkboxes.length > 1){
				for(var i=0;i<checkboxes.length;i++){
					if(checkboxes[i].checked == true){return false;}
				}
			}
			if(field.checked){return false;}
			return true;
		}
		else if(field.type=="radio"){
			var radios = getByAttribute(document,"input","name",field.name);
			for(var i=0;i<radios.length;i++){
				if(radios[i].checked == true){return false;}
			}
			return true;
		}
		else{
			if(field.value==""){return true;}	
		}
		return false;
	},
	//checks one field matches another
	//return true if the field is not found so user can continue but alert developer
	valtype_match : function(field){
		if(!field.matchToField){alert("DEV ERROR - matching field not set");return true;}
		var matchingField = document.getElementById(field.matchToField);
		if(!matchingField){alert("DEV ERROR - matching field not found");return true;}
		return(field.value == matchingField.value);
	},
	//accept characters that can go in a name
	valtype_names : function(field){
		var rexp=/^([a-z\-\x80-\xFF]+(. )?[ ']?)+$/i;
		if(field.value.search(rexp)!=-1){return true;}
		return false;
	},
	//ensures a field is at least 6 characters long
	valtype_minlength_6 : function(field){
		return(field.value.length >= 6);		
	},
	//ensures a fields is at most 10 characters long
	valtype_maxlength_10 : function(field){
		return(field.value.length <= 10);
	},
	//check for valid postcode format
	valtype_postcode : function(field){
		var rexp=/(^gir\s0aa$)|(^[a-pr-uwyz]((\d{1,2})|([a-hk-y]\d{1,2})|(\d[a-hjks-uw])|([a-hk-y]\d[abehmnprv-y]))\s?\d[abd-hjlnp-uw-z]{2}$)/i; // optional space in postcode validation
		if(field.value.search(rexp)!=-1){return true;}
		return false;
	},
	//check contains only numbers
	valtype_number : function(field){
		var rexp = /^\d+$/;
		if(field.value.search(rexp)!=-1){return true;}
		return false;
	},
	//check contains only letters and spaces
	valtype_letters : function(field){
		var rexp = /^[a-z ]+$/i;
		if(field.value.search(rexp)!=-1){return true;}
		return false;
	},
	//check for valid email address
	valtype_email : function(field)	{
		var rexp=/^[a-z[\w\.-]*[a-z0-9]@[a-z0-9][\w\.-]*[a-z0-9]\.[a-z][a-z\.]*[a-z]$/i;
		if(field.value.search(rexp)!=-1){return true;}
		return false;
	},
	//check for valid UK phone numbers
	valtype_phone : function(field)	{
		var rexp=/^((\(?0\d{5}\)?\s?\d{5})|(\(?0\d{4}\)?\s?\d{3}\s?\d{3})|(\(?0\d{3}\)?\s?\d{3}\s?\d{4})|(\(?0\d{2}\)?\s?\d{4}\s?\d{4}))+$/;
		if(field.value.search(rexp)!=-1){return true;}
		return false;
	},
	//test for a valid BT account number
	valtype_bt_account : function(field)	{
		var rexp=/^([a-z]{2})?\d{8}$/i;
		if(field.value.search(rexp)!=-1){return true;}
		return false;
	},
	//test for 3-char minimum digit security code (amex has 4)
	valtype_securitycode : function(field){
		var rexp=/^([\d]{3,4})$/i;
		if(field.value.search(rexp)!=-1){return true;}
		return false;
	},
	//test for date before today
	valtype_cardStart : function(field){
		var fieldsToTest = field.parentDiv.getElementsByTagName("select");
		if(Validator.compareSelectedDateToNow(fieldsToTest) == "isPast"){
			return true;		
		}
		return false;
	},
	//test for date after today
	valtype_cardExpiry : function(field){
		var fieldsToTest = field.parentDiv.getElementsByTagName("select");
		if(Validator.compareSelectedDateToNow(fieldsToTest) == "isFuture"){
			return true;		
		}
		return false;
	},
	//check for a valid UK sort code, user input split into 3 fields
	valtype_sortcode_threepart : function(field){
		var rexp=/^\d{2}$/i;
		if(field.value.search(rexp)!=-1){return true;}
		return false;
	},
	//check for something which could represent money
	//pattern - optional £, 1 or more numbers, optional '.' followed by 2 numbers
	valtype_currency : function(field){
		var rexp=/^\£?\d+(\.\d{2})?$/i;
		if(field.value.search(rexp)!=-1){return true;}
		return false;
	},
	//test a selection of fields for a valid date
	
	valtype_date : function(field){
		var inputs = field.parentDiv.getElementsByTagName(field.tagName);
		if(inputs.length > 1){//handles 3 inputs, either selects or text
			for(var i=0;i<inputs.length;i++){
				if(inputs[i].id.indexOf("date") != -1){
					var fDate = inputs[i].value;
				}
				else if(inputs[i].id.indexOf("month") != -1){
					var fMonth = inputs[i].value;
				}
				else if(inputs[i].id.indexOf("year") != -1){
					var fYear = inputs[i].value;
				}
			}
		}
		if(typeof fDate == "undefined"){
			var fDate = "01";	
		} 
		if (typeof fMonth == "undefined") {
			var fMonth = "01" 
		}
		if(typeof fYear == "undefined"){
			fYear = new Date().getFullYear().toString();
		}
		return (Validator.createDate(fDate,fMonth,fYear) ? true : false);
	},
	// test that a start date is before an end date
	valtype_daterange : function(field){
		var fieldsToTest = field.parentDiv.getElementsByTagName("input");
		for(var i=0,j=fieldsToTest.length;i<j;i++){
			var dateRegExp = /^\d{1,2}(([\.\-\/\s])\d{1,2}\2)(\d{2}|\d{4})$/gi;
			//
			if(fieldsToTest[i].className.indexOf("PE_daterange_start") != -1){
				var start = fieldsToTest[i].value.toString();
				dateRegExp.lastIndex = 0;
				var startRegExp = dateRegExp.exec(start);
				if(startRegExp == null){return false;}
				var aStart = start.split(startRegExp[2]);
				var startDay = aStart[0];
				var startMonth = aStart[1];
				var startYear = aStart[2];
				var startDate = Validator.createDate(startDay,startMonth,startYear);
				if(!startDate){return false;}
			}
			else if(fieldsToTest[i].className.indexOf("PE_daterange_end") != -1){
				var end = fieldsToTest[i].value.toString();
				dateRegExp.lastIndex = 0;
				var endRegExp = dateRegExp.exec(end);
				if(endRegExp == null){return false;}
				var aEnd = end.split(endRegExp[2]);
				var endDay = aEnd[0];
				var endMonth = aEnd[1];
				var endYear = aEnd[2];
				var endDate = Validator.createDate(endDay,endMonth,endYear);
				if(!endDate){return false;}
			}
		}
		if(startDate <= endDate){
			return true;
		}
		return false;
	},
	//date creation - does not allow junk data input
	createDate : function(fDate,fMonth,fYear){
		var regexpDM = /^\d{1,2}$/;
		var regexpY = /^(\d{2}|\d{4})$/;
		if(regexpDM.test(fDate)==false || regexpDM.test(fMonth)==false || regexpY.test(fYear)==false){return false;}
		fDate = parseInt(fDate,10);
		fMonth = parseInt(fMonth,10);
		fYear = parseInt(fYear,10);
		if(fMonth < 1 || fMonth > 12){return false;}
		var isLeapYear = (fYear % 4 == 0 && (fYear % 100 != 0 || fYear % 400 == 0)) ? true : false;
		
		var lastDate = 31;
		if(fMonth == 2){
			lastDate = (isLeapYear) ? 29 : 28;			
		}
		
		if (fMonth == 4 || fMonth == 6 || fMonth == 9 || fMonth == 11) {
			lastDate = 30;
		}
		if(fDate > lastDate){
			return false;
		}

		var ddate = new Date(fYear,fMonth-1,fDate);
		return ddate;
	},
	// internal function an array of select boxes, goes and gets all select boxes within its parent div
	compareSelectedDateToNow : function(allFields){
		var now = new Date();
		var ddate = 01;
		for(var i=0;i<allFields.length;i++){
			if(allFields[i].name.indexOf("date") != -1){
				ddate = allFields[i].options[allFields[i].selectedIndex].value;
			}
			else if(allFields[i].name.indexOf("month") != -1){
				var month = allFields[i].options[allFields[i].selectedIndex].value;
			}
			else if(allFields[i].name.indexOf("year") != -1){
				var year = allFields[i].options[allFields[i].selectedIndex].value;
			}
		}
		//will work correctly until the year 2090
		if(year.length === ""){return false;} //interrupt to stop undefined being converted to 1900
		if(year.length == 2){
			year = (year < 90) ? "20" + year : "19" + year;
		}
		if(month === ""){return false;} //interrupt to stop undefined being converted to 0 (January)
		var uInput = Validator.createDate(ddate,month,year);
		if(uInput < now){
			return "isPast";
		}
		else if(uInput > now){
			return "isFuture";
		}
	},
	//run
	init : function(){
		this.nestParents = fuzzyClassName("input","PE_nestValidation");
		var selectNest = fuzzyClassName("select","PE_nestValidation");
		for(var i=0,j=selectNest.length;i<j;i++){
			this.nestParents.push(selectNest[i]);
		}
		window.validData = new Validator.validationData();
		Validator.nestedValidation();
		Validator.validationEventTriggers();
	}
}

/*LIGHTBOX
	a lightbox is a box of content that sits above the main area, which is usually faded out
	designed as an object because validation needs to access some of its functions.
	setting up the tint bg element at init phase works around a obscure IE bug
	REQUIRED:
		class 'PE_lightBox'  (NB case-sensitive) on the div to become a lightbox, this area must also have an id
		class 'PE_lightboxOpen(id_of_lightbox)' on the anchor that triggers the lightbox if triggered by a link
		class 'PE_lightboxIfInvalid(id_of_lightbox)' on the div with the PE_validate() class if the lightbox is triggered by an invalid field (see validate section comments for further details)
*/
Lightbox = {
//	active : false,
	active : 0,
	height: 0,
	init : function(){
		var lightboxes = fuzzyClassName("div","PE_lightBox");
		var lightboxLinks = fuzzyClassName("a","PE_lightboxOpen");
		for(var i=0;i<lightboxes.length;i++){
			var lightboxContainer = getParentByTagName(lightboxes[0],"form");
			if(!lightboxContainer){lightboxContainer = document.getElementById("Page")};
			lightboxes[i].style.visibility = "visible";  //remove styles from IE foc prevention
			//take out of position in HTML and insert at end of page, necessary for placing above the tinted background in IE

			var lightbox = lightboxes[i].parentNode.removeChild(lightboxes[i]);
			lightboxContainer.appendChild(lightbox);
			lightbox.className += " hasJS";
			Lightbox.hideLightbox(lightbox);
			Lightbox.addBoxStyles(lightbox);
			Lightbox.addBoxClose(lightbox);
		}
		Lightbox.active = 0;
		Lightbox.setUpTintBg();
		for(var h=0;h<lightboxLinks.length;h++){
			lightboxLinks[h].className = lightboxLinks[h].className.replace("hidden","");
			lightboxLinks[h].onclick=function(){
				var thisLightbox = document.getElementById(getPEClassInfo(this,"PE_lightboxOpen"));
				if(typeof thisLightbox == "undefined"){alert("DEV ERROR: no element with ID specified in link's class");return;}
				Lightbox.showLightbox(thisLightbox);
				Lightbox.tintBg();
				
				return false;
			}
		}
		//lightbox on page load - this should be the last part of init()
		if(location.hash.indexOf("lightbox_") != -1){
			var thisLightbox = document.getElementById(location.hash.replace("#",""));
			if(!thisLightbox){return;}
			Lightbox.showLightbox(thisLightbox);
			Lightbox.tintBg();
		}
	},
	/*dynamically creates a lightbox.
	takes either a string or node object for headNode and contentNode and a string for id*/
	//TODO: how to handle attempts to create lightboxes with a duplicate id
	//TODO: reuse and alter or destroy?
	create: function(headNode,contentNode,id){
		var lightbox = document.createElement("div");
			lightbox.className = "lightBox PE_lightBox";
			lightbox.id = id;
		var lightboxHead = document.createElement("div");
			lightboxHead.className = "heading";
			if(typeof(headNode) == "string"){
				headNode = document.createTextNode(headNode);
			}
			lightboxHead.appendChild(headNode);
		lightbox.appendChild(lightboxHead);
		var lightboxContent = document.createElement("div");
			lightboxContent.className = "content";
			if(typeof(contentNode) == "string"){
				contentNode = document.createTextNode(contentNode);
			}
			lightboxContent.appendChild(contentNode);
		lightbox.appendChild(lightboxContent);
		lightbox.className += " hasJS";
		Lightbox.hideLightbox(lightbox);
		Lightbox.addBoxStyles(lightbox);
		Lightbox.addBoxClose(lightbox);
		var pageDiv = document.getElementById("Page");
		pageDiv.appendChild(lightbox);
		lightbox.style.visibility = "visible";  //remove styles from IE foc prevention
		return lightbox;
	},
	addBoxStyles : function(elem_lightbox){
		//middle - take all children of lightbox and put them into the boxRight div
		var boxRight = document.createElement("div");
			boxRight.className = "lightbox-right";
		while(elem_lightbox.childNodes.length > 0){
			boxRight.appendChild(elem_lightbox.firstChild);
		}
		var boxMain = document.createElement("div");
			boxMain.className = "lightbox-main";
			boxMain.appendChild(boxRight);
		elem_lightbox.appendChild(boxMain);
		//top
		var boxTop = document.createElement("div");
			boxTop.className = "lightbox-top"
		var boxTopLeft = document.createElement("div");
			boxTopLeft.className = "lightbox-top-left";
		var boxTopRight = document.createElement("div");
			boxTopRight.className = "lightbox-top-right";
			boxTopLeft.appendChild(boxTopRight);
			boxTopLeft.appendChild(boxTop);
			elem_lightbox.insertBefore(boxTopLeft,elem_lightbox.firstChild);
		//base
		var boxBase = document.createElement("div");
			boxBase.className = "lightbox-base";
		var boxBaseLeft = document.createElement("div");
			boxBaseLeft.className = "lightbox-base-left";
		var boxBaseRight = document.createElement("div");
			boxBaseRight.className = "lightbox-base-right";
			boxBaseLeft.appendChild(boxBaseRight);
			boxBaseLeft.appendChild(boxBase);
			elem_lightbox.appendChild(boxBaseLeft);
	},
	addBoxClose : function(elem_lightbox){
		var closeLinkImg = document.createElement("img");
			closeLinkImg.src = "../img/buttons/btn_close.gif";
			closeLinkImg.alt = "Close this window.";
		var closeLinkTxt = document.createTextNode("Close");
		closeLink = document.createElement("a");
			closeLink.href = "#";
			closeLink.id = "close_" + elem_lightbox.id;
			closeLink.className = "closeBtn clearfix";
			closeLink.onclick = function(){
				Lightbox.rehideLightbox(elem_lightbox);
				Lightbox.removeTintBg();
				//accessibility aid: refocus on the link or validated field that triggered the lightbox
				var lightboxTrigger = fuzzyClassName("a","PE_lightboxOpen(" + elem_lightbox.id + ")")[0];
				if(!lightboxTrigger){
					var lightboxDiv = fuzzyClassName("div","PE_lightboxIfInvalid(" + elem_lightbox.id + ")")[0];
					if(lightboxDiv){lightboxTrigger = lightboxDiv.getElementsByTagName("input")[0];}
				}
				if(lightboxTrigger){lightboxTrigger.focus();}
				return false;
			}
		var closeSpan = document.createElement("span");
		closeSpan.appendChild(closeLinkTxt);
		closeSpan.appendChild(closeLinkImg);
		closeLink.appendChild(closeSpan);
		var lightboxHeading = getElementsByClassName(elem_lightbox,"*","heading")[0];
		lightboxHeading.insertBefore(closeLink,lightboxHeading.firstChild);
		lightboxHeading.className += " clearfix";
		//accessibility aid: add hidden close at bottom of lightbox
		var closeLinkHidden = document.createElement("a");
			closeLinkHidden.appendChild(document.createTextNode("End of in page popup. Close using this link to return to main content."));
			closeLinkHidden.href = "#";
			closeLinkHidden.className = "hidden";
			closeLinkHidden.onclick = closeLink.onclick;
		elem_lightbox.appendChild(closeLinkHidden);
		elem_lightbox.closeButtons = [closeLink,closeLinkHidden];
		//detect cancel buttons in HTML and use those too
		var cancelLinks = fuzzyClassNameBlock(elem_lightbox,"a","PE_closeLightbox");
		for(var i=0;i<cancelLinks.length;i++){
			cancelLinks[i].onclick = closeLink.onclick;
			elem_lightbox.closeButtons.push(cancelLinks[i]);
		}
	},
	setUpTintBg : function(){
		/*@cc_on @*/
		/*@if (@_jscript_version < 5.6)
			return; // impossible in IE5
		/*@end @*/
		var pageDiv = document.getElementById("Page");
		var tint = document.createElement("div");
		tint.className = "tintedBg";
		if(null!=pageDiv)
		pageDiv.insertBefore(tint,pageDiv.firstChild);
	},
	tintBg : function(){
		if(Lightbox.hasCSS() == false){return;}
		var tint = getElementsByClassName(document,"div","tintedBg")[0];
		if(!tint){return;}
		tint.style.height = "0px";
		var pageDimensions = getFullPageDimensions();
		var newWidth = pageDimensions[0];
		var newHeight = pageDimensions[1];
		if(Lightbox.height > newHeight){
			tint.style.height = Lightbox.height +"px";
		}
		else{
			tint.style.height = newHeight +"px";
		}
		tint.style.width = newWidth +"px";
	},
	removeTintBg : function(){
		if(Lightbox.active > 0){return;}
		var tint = getElementsByClassName(document,"div","tintedBg")[0];
		if(!tint){return;}
		tint.style.width = "0px";
		tint.style.height = "0px";
	},
	showLightbox : function(elem_lightbox){
		/*@cc_on @*/
		/*@if (@_jscript_version <= 5.6)
			Lightbox.hideSelects(elem_lightbox); //hide select boxes due to buggy IE6 handling of them
		/*@end @*/
		elem_lightbox.className = elem_lightbox.className.replace(/ hideLightbox/g,"");
		//set the lightbox in the vertical center of the page, account for relatively-positioned parents
		// if the lightbox is bigger than the viewport, set its top to the top of the viewable screen
		var viewPortCenter = Math.round(getViewPortHeight() / 2);
		var scrolledTop = getScrollTop();
		var userViewCenter = parseInt(viewPortCenter) + parseInt(scrolledTop);
		var lightboxCenter = elem_lightbox.offsetHeight / 2;
		var lightboxRelativeTop = 0;
		var parent = elem_lightbox.parentNode;
		while(parent){
			if(parent.nodeName=="#document"){break;}
			if(getStyle(parent,"position") == "relative"){
				lightboxRelativeTop += parent.offsetTop;
			}
			parent = parent.parentNode;
		}
		if(getViewPortHeight() < elem_lightbox.offsetHeight){
			elem_lightbox.style.top = scrolledTop - lightboxRelativeTop + "px";
		}
		else{
			elem_lightbox.style.top = userViewCenter - lightboxCenter - lightboxRelativeTop + "px";
		}
//		Lightbox.active = true;
		Lightbox.active++;
		Lightbox.height = parseInt(elem_lightbox.offsetHeight) + parseInt(elem_lightbox.offsetTop);
		//accessibility aid: send cursor to close button on the lightbox
		var closebtn = document.getElementById("close_" + elem_lightbox.id);
		var hiddenFocusLink = document.getElementById("hiddenFocus_" + elem_lightbox.id);
		if(!hiddenFocusLink){
			var hiddenFocusLink = document.createElement("a");
				hiddenFocusLink.href = "#";
				hiddenFocusLink.onclick = function(){return false;}
				hiddenFocusLink.id = "hiddenFocus_" + elem_lightbox.id;
				hiddenFocusLink.appendChild(document.createTextNode("In page pop-up layer.  Use close links to return to main content."));
			closebtn.parentNode.insertBefore(hiddenFocusLink,closebtn);
		}
		hiddenFocusLink.focus();
		hiddenFocusLink.className = "hidden";
	},
	hideLightbox : function(elem_lightbox){
		/*@cc_on @*/
		/*@if (@_jscript_version <= 5.6)
			Lightbox.showSelects();
		/*@end @*/
		elem_lightbox.className += " hideLightbox";
		var hiddenFocusLink = document.getElementById("hiddenFocus_" + elem_lightbox.id);
		if(hiddenFocusLink){hiddenFocusLink.className = hiddenFocusLink.className.replace("hidden","");}
	},
	rehideLightbox : function(elem_lightbox){
		Lightbox.hideLightbox(elem_lightbox);
		//Lightbox.active = false;
		Lightbox.active--;
	},
	//one for set up, one for show/hide
	//hide select boxes due to buggy IE<7 handling of them
	hideSelects : function(){
		var selects = document.getElementsByTagName("select");
		for(var i=0;i<selects.length;i++){
			selects[i].style.visibility = "hidden";
		}
	},
	showSelects : function(){
		var selects = document.getElementsByTagName("select");
		for(var i=0;i<selects.length;i++){
			selects[i].style.visibility = "visible";
		}
	},
	hasCSS : function(){
		var findHidden = getElementsByClassName(document.body,"*","hidden")[0];
		if(getStyle(findHidden,"position") != "absolute"){
			return false;
		}
		return true;
	}
}

/* PAGINATION 

main 'PE_paginate' function detects elements for pagination and runs the relevant function if found
REQUIRED:
	element with class 'PE_paginate' and arguments specifying how to determine how content should be split up into 'pages'
	UL takes an integer so that the ul is split into several uls with that number of list items in them
*/
function paginate(){
	if(fuzzyClassName("ul","PE_paginate").length > 0){
		paginateUnorderedList();
	}
	
	function paginateUnorderedList(){
		var ul_toPaginate = fuzzyClassName("ul","PE_paginate");
		for(var i=0;i<ul_toPaginate.length;i++){
			var splitAtItem = parseInt(getPEClassInfo(ul_toPaginate[i],"PE_paginate"));
			if(isNaN(splitAtItem)){return false;}
			var listItems = ul_toPaginate[i].getElementsByTagName("li");
			if(listItems.length <= splitAtItem){return false;}
			var uls = [ul_toPaginate[i]];
			var lastUL = "";
			while(listItems.length > splitAtItem){
				var newUL = document.createElement("ul");
					newUL.className = ul_toPaginate[i].className.replace("PE_("+splitAtItem+")","");
					newUL.className += " hidden";
				for(var t=splitAtItem;t<splitAtItem*2;t++){
					if(!listItems[splitAtItem]){break;}
					var moveLi = listItems[splitAtItem].parentNode.removeChild(listItems[splitAtItem]);
					newUL.appendChild(moveLi);
				}
			
				if(!lastUL){lastUL = ul_toPaginate[i];}
				if(lastUL.nextSibling){
					ul_toPaginate[i].parentNode.insertBefore(newUL,lastUL.nextSibling);
				}
				else{
					ul_toPaginate[i].parentNode.appendChild(newUL);
				}
				lastUL = newUL;
				uls.push(newUL);
			}

			function goPrev(){
				this.nextBtn.className = this.nextBtn.className.replace("nodisplay","");
				for(var p=0;p<this.uls.length;p++){
					if(this.uls[p].className.indexOf("hidden") == -1){
						if(p == 0){break;}
						this.uls[p].className += " hidden";
						this.uls[p-1].className = this.uls[p-1].className.replace("hidden","");
						if(p-1 == 0){this.className += " nodisplay";}
						break;
					}
				}
				return false;
			}
			function goNext(){
				this.prevBtn.className = this.prevBtn.className.replace("nodisplay","");
				for(var n=0;n<this.uls.length;n++){
					if(this.uls[n].className.indexOf("hidden") == -1){
						if(n == this.uls.length-1){break;}
						this.uls[n].className += " hidden";
						this.uls[n+1].className = this.uls[n+1].className.replace("hidden","");
						if(n+1 == this.uls.length-1){this.className += " nodisplay";}
						break;
					}
				}
				return false;
			}
			
			var buttonsDiv = document.createElement("div");
			buttonsDiv.className = "paginationButtons clearfix";
			var prevBtn = prevPage(goPrev);
				prevBtn.className += " nodisplay";
			var nextBtn = nextPage(goNext);
			prevBtn.nextBtn = nextBtn;
			nextBtn.prevBtn = prevBtn;
			buttonsDiv.appendChild(prevBtn);
			buttonsDiv.appendChild(nextBtn);
			prevBtn.uls = nextBtn.uls = uls;
			ul_toPaginate[i].parentNode.insertBefore(buttonsDiv,lastUL.nextSibling);
		}
	}
	//returns a DOM object of 'next' image inside a hyperlink
	function nextPage(clickFunc){
		var img = document.createElement("img");
			img.src = "../img/buttons/btn_next_white_off.gif";
			img.className = "PE_swap";
			img.alt = "Next.";
		var a = document.createElement("a");
			a.href="#";
			a.appendChild(img);
			a.onclick = clickFunc;
			return a;
	}
	//returns a DOM object of 'previous' image inside a hyperlink
	function prevPage(clickFunc){
		var img = document.createElement("img");
			img.src = "../img/buttons/btn_previous_white_off.gif";
			img.className = "PE_swap";
			img.alt = "Previous.";
		var a = document.createElement("a");
			a.href="#";
			a.appendChild(img);
			a.onclick = clickFunc;
			return a;
	}
}

/*PASSWORD STRENGTH CHECKER*/
/*
Requirements:
	Wrapper:
				div with class 'checkPasswordStrength' (around strength message and image)
	Fields:		password field with id 'password'
	Message:	div with class 'strengthmessage' to show the message text
	Image:		image with id 'passwordStrengthBar' to show the indicator
				6 image files for the strength indicator:
				bar_pcheckstrength_0.gif, bar_pcheckstrength_1.gif, bar_pcheckstrength_2.gif, bar_pcheckstrength_3.gif, bar_pcheckstrength_4.gif and bar_pcheckstrength_5.gif
*/


function passwordStrengthCheck(){
	var checkStrength = fuzzyClassName("div","checkPasswordStrength");
	if(checkStrength.length == 0){return;}
	BindStrengthCheck();
	PasswordStrengthChecker.setMinLength(6); // set minimum number of characters entered until checker activates
	// put strength checking on keystroke event
	document.getElementById('password').onkeyup = OutputStrengthMessage; //get password input
	
}

function OutputStrengthMessage()
{
	var pwd_el = document.getElementById('password'); //get password input

	var strength = pwd_el.CheckStrength();
	var message = '';
	switch(strength.keyMessage)
	{
	case PasswordStrength.returnValues.levelZero: message='';color='#ffffff';break;
	case PasswordStrength.returnValues.levelOne: message='Vulnerable';color='#fc0303';break;
	case PasswordStrength.returnValues.levelTwo: message='Weak';color='#ff6600';break;
	case PasswordStrength.returnValues.levelThree: message='Try harder';color='#cc9900';break;
	case PasswordStrength.returnValues.levelFour: message='Better';color='#CADB2A';break;
	case PasswordStrength.returnValues.levelFive: message='Excellent';color='#69be28';break;
	}
	
	var strengthmessage = getElementsByClassName(document,'div','strengthmessage')[0]; //get strength message text area

	strengthmessage.innerHTML=message; 
	strengthmessage.style.color=color; 
	
	// change colour;
	var i=new Image();
	i.src='../img/icons/bar_pcheckstrength_'+strength.strength+'.gif';
	var pswImg=document.getElementById('passwordStrengthBar'); //get strength image
	pswImg.src=i.src;
	
}



function BindStrengthCheck()
{
	var pswd=document.getElementById('password'); //get password input
	PasswordStrengthChecker.BindPasswordField(pswd);
		
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// The strength checker stuff
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// return values;

var PasswordStrength = new Object();

PasswordStrength.returnValues = {
	// Return values;
	levelZero: 'levelZero',
	levelOne: 'levelOne', 
	levelTwo: 'levelTwo', 
	levelThree: 'levelThree',
	levelFour: 'levelFour',
	levelFive: 'levelFive'
}

// the global strength checker object:
var PasswordStrengthChecker = {
	min_valid_length: 2,
	input_el_compare: new Array(),
	// default validation checks;
	//regular expressions to determine strength of password based on patterns
	// level 1: user has put in only one case of letters or only numbers or only symbols
	// OR		user has first letter upper case, the rest lower case
	// level 2: user has one case letters and numbers on the end
	// OR		user has mixed case letters but no numbers or symbols
	// OR		user has 2+ upper case letters at the start then lower case, but no numbers or symbols
	// level 3: user has mixed case letters with numbers on the end
	// OR		user has 2+ upper case letters then lower case then numbers on the end
	// OR		user has mixed case letters with symbol on the end
	// OR		user has 2+ upper case letters then lower case then symbol on the end
	// level 4:	user has numbers mixed in the middle of letters
	// OR		user has symbols mixed in the middle of letters
	// level 5: user has both numbers and symbols in the middle of letters
	// OR		user has symbols in the middle of letters and numbers at the end
	default_checks: [
					[/(^[a-z]+$) | (^[A-Z]+$) | (^[0-9]+$) | (^[!%&#~_-]+$) /g, 1, PasswordStrength.returnValues.levelOne],
					[/([a-z]|[A-Z])+[0-9]+/g, 2, PasswordStrength.returnValues.levelTwo],
					[/[a-z]+[A-Z]+/g, 2, PasswordStrength.returnValues.levelTwo],
					[/[A-Z]{2,}[a-z]+/g, 2, PasswordStrength.returnValues.levelTwo],
					[/[a-z]+[A-Z]+[0-9]+$/g, 3, PasswordStrength.returnValues.levelThree],
					[/[A-Z]{2,}[a-z]+[0-9]+$/g, 3, PasswordStrength.returnValues.levelThree],
					[/[a-z]+[A-Z]+[!%&#~_\-]+$/g, 3, PasswordStrength.returnValues.levelThree],	
					[/[A-Z]{2,}[a-z]+[!%&#~_\-]+$/g, 3, PasswordStrength.returnValues.levelThree],	
					[/[a-zA-Z]+[0-9]+[a-zA-Z]+/g, 4, PasswordStrength.returnValues.levelFour],
					[/[a-zA-Z]+[!%&#~_-]+[a-zA-Z]+/g, 4, PasswordStrength.returnValues.levelFour],
					[/[a-zA-Z]+[!%&#~_-]+[a-zA-Z]*[0-9]+[a-zA-Z]+/g, 5, PasswordStrength.returnValues.levelFive],
					[/[a-zA-Z]+[0-9]+[a-zA-Z]*[!%&#~_-]+[a-zA-Z]+/g, 5, PasswordStrength.returnValues.levelFive],
					[/[a-zA-Z]+[!%&#~_-]+[a-zA-Z]*[0-9]+/g, 5, PasswordStrength.returnValues.levelFive]
				],
				//!%&#~_-
	// public methods;
	// adds an input field required for comparison;
	AddCompareInput: function(input_el)
		 {this.input_el_compare.push(input_el);},
	
	// binds events to a password field;
	BindPasswordField: function(input_el) 
	{
		input_el.CheckStrength = function() {return PasswordStrengthChecker._CheckStrength(input_el);};		
	},
	
	// check the strength of a password;
	_CheckStrength: function(input_el) 
	{	
		var input_val = (input_el.value) ? input_el.value : "";
		var ret_val = {strength: 1, keyMessage: PasswordStrength.returnValues.levelOne}; 
		if (input_val.length < this.min_valid_length) 
		{
			ret_val.strength = 0;
			ret_val.keyMessage = PasswordStrength.returnValues.levelZero;
		}
		else 
		{
			this._CheckCompare(input_val, ret_val);
			this._CheckDefault(input_val, ret_val);

		}	
		ret_val.strength = (ret_val.strength<0)?0:ret_val.strength;
		
		return ret_val;
	},
	
	// check against default checks;
	_CheckDefault: function(input_val, ret_val) 
	{
		var def_strength = 0;
		for (var i=0; i < this.default_checks.length; i++) 
		{

			if (input_val.match(this.default_checks[i][0]))
			{
				ret_val.strength = this.default_checks[i][1];
				ret_val.keyMessage = this.default_checks[i][2];
			}
		}
	},
	// check against other input fields to see if there is any crossover; 
	_CheckCompare: function(input_val, ret_val)
	{
		var input_val_lc = input_val.toLowerCase();

		for (var i=this.input_el_compare.length-1; i>=0; i--) 
		{
			var ck_val = this.input_el_compare[i].value.toLowerCase();
			
			if (ck_val.length)
				if (input_val.indexOf(ck_val) > -1)
				{
					ret_val.strength = 1;
					ret_val.keyMessage = PasswordStrength.returnValues.levelOne;
				}
		}
	},

	
	// public: set properties;
	setMinLength: function(min_valid_length) {this.min_valid_length = min_valid_length;}
}


/*END PASSWORD STRENGTH CHECKER*/


/*  SUM

used for calculating the sum of the values of a group of fields and writing them to 1 or more related elements

REQUIRED
	input or select boxes with class 'PE_sum(fooName)'
	area for sum total with class 'PE_sumTotal(name=fooName,format=[float|int|currency])'
	

*/
function sum(){
	var sumTotals = fuzzyClassName("span","PE_sumTotal");
	for(var i=0;i<sumTotals.length;i++){
		sumTotals[i].parentNode.className = sumTotals[i].parentNode.className.replace("nodisplay","");
		getPEHash(sumTotals[i],getPEClassInfo(sumTotals[i],"PE_sumTotal"));
		var sumAddends = fuzzyClassName("input","PE_sum("+sumTotals[i]['name']+")");
		var selectAddends = fuzzyClassName("select","PE_sum("+sumTotals[i]['name']+")");
		for(var c=0;c<selectAddends.length;c++){
			sumAddends.push(selectAddends[c]);
		}
		var tallyTotal = 0;
		for(var s=0;s<sumAddends.length;s++){
			if(!isNaN(parseFloat(sumAddends[s].value))){
				tallyTotal = parseFloat(tallyTotal) + parseFloat(sumAddends[s].value);
			}
			if(!sumAddends[s].outputs){
				sumAddends[s].outputs = new Array();
			}
			sumAddends[s].outputs.push(sumTotals[i]);
			sumAddends[s].addends = sumAddends;
			sumAddends[s].onkeyup = function(){
				var tally = 0;
				for(var a=0;a<this.addends.length;a++){
					if(!isNaN(parseFloat(this.addends[a].value))){
						tally = parseFloat(tally) + parseFloat(this.addends[a].value);
					}
				}
				for(var o=0;o<this.outputs.length;o++){
					tally = parseFloat(tally);
					if(this.outputs[o]['format'] == "currency"){
						tally = "&pound;" + tally.toFixed(2);
					}
					this.outputs[o].innerHTML = tally;
				}
			};
			sumAddends[s].onchange = sumAddends[s].onkeyup;
		}	
		if(sumTotals[i]['format'] == "currency"){
			tallyTotal = "&pound;" + tallyTotal.toFixed(2);
		}
		else if(sumTotals[i]['format'] == "int"){
			tallyTotal = parseInt(tallyTotal);
		}
		sumTotals[i].innerHTML = tallyTotal;
	}
}

/*
BAR CHART
	creates a bar chart from table data and a scale based on the highest value in the data, a given number of markers on the scale and rounds to multiples of 5 (to change, see code below)
REQUIRED:
	table with class 'PE_hasBarChart'
	if a text scale is required, th for the column with class PE_chartScale(x) where x is the number of steps
	numeric values to be used wrapped in a span element with class 'PE_chartValue'
	td for bar to be displayed in to have class 'PE_chartDisplay'
	bar background div with class bar_bg
 */

function barChart(){
	var chartTable = getElementsByClassName(document,"table","PE_hasBarChart");
	for (var i = 0; i < chartTable.length; i++) {
		chartTable[i].className += " barchart";
		var valueSpan = getElementsByClassName(chartTable[i],"span","PE_chartValue");
		if(valueSpan.length === 0){return;}
		var chartDisplay = getElementsByClassName(chartTable[i],"div","bar_bg")[0];
		var chartDisplayTd = getParentByTagName(chartDisplay,"td");
		chartDisplayTd.className += " barchart"
		var chartDisplay_width = parseInt(chartDisplay.offsetWidth,10);
		var maxValue = 1;
		//determine largest item for chart
		for (var v = 0; v < valueSpan.length; v++) {
			if (Math.ceil(valueSpan[v].innerHTML) > maxValue) {
				maxValue = Math.ceil(valueSpan[v].innerHTML);
			}
		}
		var scale = maxValue; //default to largest value

		//calculate values and layout of scale
		var scaleHeader = fuzzyClassNameBlock(chartTable[i],"th","PE_chartScale")[0];
		if(scaleHeader){
			var scaleInner = document.createElement("div");
			scaleInner.className = "chartScale";
			var scaleNoOfSteps = parseInt(getPEClassInfo(scaleHeader,"PE_chartScale"));
			var scaleRound = 5;
			//determine scale maxValue / steps on graphic rounded to highest rounding value
			//rounded so that the scale does not include pence
			var maxValueToNearestRound = Math.ceil(maxValue / scaleRound) * scaleRound;
			var scaleStep = Math.ceil(maxValueToNearestRound / scaleNoOfSteps);
			
			//only round step value to nearest rounding value if maximum is greater than round * steps, otherwise the scale end will be determinded by number of steps multiplied by rounding value
			if(maxValueToNearestRound / scaleRound >= scaleNoOfSteps){
				scaleStep = Math.ceil(scaleStep / scaleRound) * scaleRound;
			}
			scale = scaleStep * scaleNoOfSteps;
			var scaleHeaderWidth = scaleHeader.offsetWidth;
			var paddingHoriz = parseInt(getStyle(scaleHeader,"padding-right")) + parseInt(getStyle(scaleHeader,"padding-left"));
			scaleHeaderWidth -= paddingHoriz;
			for(var s=0;s<=scaleNoOfSteps;s++){
				var newStep = document.createElement("div");
				newStep.className = "chartScaleStep";
				//step value
				var stepNumber = document.createTextNode("\u00A3" + s * scaleStep);
				newStep.appendChild(stepNumber);
				newStep.title = "\u00A3" + s * scaleStep;
				//step dynamic styles. width split equally between divs
				//position set to middle of div on steps equivalent %
				//e.g. 4 steps, 1st step is 25%, 25% of scale width is converted to px and div is positioned so its midpoint is there

				newStep.style.width = Math.floor(scaleHeaderWidth / (scaleNoOfSteps+1)) + "px";
				if(s < scaleNoOfSteps){
					var newStepCentre = parseInt(parseInt(newStep.style.width) / 2);
					var newStepPercent = 100 / scaleNoOfSteps * s;
					var newStepPercentAsPx = scaleHeaderWidth * (newStepPercent / 100);
					var newPos = Math.floor(newStepPercentAsPx - newStepCentre);
					if(newPos == 0 - newStepCentre){ 
						newPos += 5;
					}
					newStep.style.left =  newPos+"px";
					scaleInner.appendChild(newStep);
				}
				if(s == scaleNoOfSteps){
					scaleHeader.appendChild(scaleInner);
					newStep.className = "chartScaleStepLast";
					scaleHeader.appendChild(newStep);
				}

			}
			
		}
		//write out bars
		for(v = 0; v < valueSpan.length; v++){
			var barWidth = parseInt(valueSpan[v].innerHTML / scale * chartDisplay_width);
			var tr = getParentByTagName(valueSpan[v],"tr");
			var chartDisplay = getElementsByClassName(tr,"td","PE_chartDisplay");
			if(chartDisplay.length === 0){continue;}
			chartDisplay[0].className += " barchart";
			var chartBar = document.createElement("div");
			
				chartBar.style.width = barWidth + "px";
				chartBar.className = "bar";
			var chartBarWrapper = document.createElement("div");
				chartBarWrapper.className = "barWrap";
				chartBarWrapper.style.width = chartDisplay_width + "px";
			var chartBarImg = document.createElement("div");
			var chartBarEnd = document.createElement("div");
				chartBarEnd.className = "end";
			chartBarImg.appendChild(chartBarEnd);
			chartBarWrapper.appendChild(chartBarImg);
			chartBar.appendChild(chartBarWrapper);
			chartDisplay[0].lastChild.appendChild(chartBar);

		}
	}
}
/*
toggleView allows specified content to be displayed and hidden depending on one of a group of links being clicked or an input being checked.
the content is hidden using nodisplay, which means it is not available to screenreaders when hidden
the visible content area's link has its link removed so it is just a span, when it is no longer the visible area a link is written around it dynamically
REQUIRED:
	toggling content grouped inside a div with class PE_toggleView
	FOR LINKS
		toggling text in span element with class PE_toggler(uid)
	FOR INPUTS
		input given class PE_toggler(uid)
	FOR HIDING ALL TOGGLE AREAS
		input or span with class PE_toggler()
	block element wrapping the content to be toggled with class PE_toggle(uid)
	the toggler for the starting toggle view has no link around it, all the other togglers do
*/
function toggleView(){
	var toggleViewDiv = getElementsByClassName(document,"div","PE_toggleView");
	for(var i=0,j=toggleViewDiv.length;i<j;i++){
		toggleViewDiv[i].style.display = "block";
		toggleViewDiv[i].style.visibility = "visible";
		var toggles = fuzzyClassNameBlock(toggleViewDiv[i],"span","PE_toggler");
		var toggleInputs = fuzzyClassNameBlock(toggleViewDiv[i],"input","PE_toggler");
		for(var t=0,u=toggleInputs.length;t<u;t++){
			toggles.push(toggleInputs[t]);
		}
		for(var k=0,m=toggles.length;k<m;k++){
			toggles[k].allToggles = toggles;
			var toggleViewMarker = getPEClassInfo(toggles[k],"PE_toggler");
			var toggleViews = fuzzyClassName("*","PE_toggle("+toggleViewMarker+")");
			if(toggleViewMarker == ""){
				toggleViews = null;
			}
			else if(toggleViews.length == 0){
				continue;
			}
			else{
				toggles[k].toggleView = toggleViews[0];
			}
			toggles[k].parentNode.toggle = toggles[k];
			//for spans
			if(toggles[k].parentNode.tagName.toLowerCase() == "a"){
				hideToggleView(toggles[k].toggleView);
				toggles[k].parentNode.onclick = toggle;
			}
			//for inputs
			else if(toggles[k].tagName.toLowerCase() == "input"){
				toggles[k].toggle = toggles[k];
				if(toggles[k].checked == false){
					hideToggleView(toggles[k].toggleView);
				}
				addEvent(toggles[k],"click",toggle);
			}
		}
	}
	function toggle(){
		showToggleView(this.toggle.toggleView);
		for(var n=0,p=this.toggle.allToggles.length;n<p;n++){
			if(this.toggle.allToggles[n] == this.toggle){
				if(this.tagName.toLowerCase() == "a"){
					this.parentNode.insertBefore(this.toggle,this);
					this.parentNode.removeChild(this);
				}
				continue;
			}
			else{
				hideToggleView(this.toggle.allToggles[n].toggleView);
				if(this.toggle.allToggles[n].parentNode.tagName.toLowerCase() != "a" && this.tagName.toLowerCase() != "input" ){
					var toggleLink = document.createElement("a");
					toggleLink.href="#";
								this.toggle.allToggles[n].parentNode.insertBefore(toggleLink,this.toggle.allToggles[n]);
					toggleLink.appendChild(this.toggle.allToggles[n]);
					toggleLink.toggle = this.toggle.allToggles[n];
					toggleLink.onclick = toggle;
				}
			}
		}
		if(this.tagName.toLowerCase() == "a"){return false;}

	}
	function hideToggleView(el){
		if(!el){return false;}
		if(el.className.indexOf("nodisplay") == -1){
			el.className += " nodisplay";
		}
	}
	function showToggleView(el){
		if(!el){return false;}
		 el.className = el.className.replace("nodisplay","");
	}
}

/*
select boxes - hide for IE6

calendar
	brings up a calendar div lying over the page
REQUIRED:
	input field with class "PE_calendar"
	span with class "PE_lastBillDate" containing the date the last bill was sent out in format yyyy/mm/dd
*/

function calendar(){
	var calendarFields = getElementsByClassName(document,"input","PE_calendar");
	for(var i=0,j=calendarFields.length;i<j;i++){
		var calendarDiv = document.createElement("div");
		calendarDiv.id = "div_"+calendarFields[i].id;
		calendarDiv.className = "calendar_wrapper";
		var nextSibling = calendarDiv.nextSibling;
		if(nextSibling){
			calendarFields[i].parentNode.insertBefore(calendarDiv,nextSibling);
			}
		else{
			calendarFields[i].parentNode.appendChild(calendarDiv);
		}
		calendarFields[i].calendarDivID = calendarDiv.id;
		
		var lastBill = getElementsByClassName(document,"span","PE_lastBillDate");
		if(lastBill.length > 0){calendarFields[i].lastBillEnd = new Date(lastBill[0].innerHTML);}
		else{//last bill date defaults to yesterday as a fail-safe
			calendarFields[i].lastBillEnd = new Date();
			calendarFields[i].lastBillEnd.setDate(calendarFields[i].lastBillEnd.getDate()-1);
		}
		
		addEvent(calendarFields[i],'click',newCalendar);
	}
	function newCalendar(){
		var newCal = new CalendarPopup(this.calendarDivID);
		newCal.setWeekStartDay(1);
		newCal.dayHeaders = new Array("S","M","T","W","T","F","S");
		for(var m=0;m<newCal.monthNames.length;m++){
			newCal.monthNames[m] = newCal.monthNames[m].toUpperCase();
		}
		var now = new Date();
		var tomorrow = new Date();
		tomorrow.setDate(now.getDate()+1);
		newCal.offsetX = 0;
		newCal.addDisabledDates(null, formatDate(this.lastBillEnd,"yyyy-MM-dd"));
		
		newCal.addDisabledDates(formatDate(tomorrow,"yyyy-MM-dd"),null);
		
		newCal.lastMonthLink = '<img src="../img/buttons/buttons_ya/btn_calendar_prev.gif" alt="Previous month" />';
		newCal.nextMonthLink = '<img src="../img/buttons/buttons_ya/btn_calendar_next.gif" alt="Next month" />';
		newCal.select(this,this.id,'dd/MM/yyyy');  //call last
	}
}
/*
TODO: what to plug ajax into?  need BT and TechM's help
billSavePrint is an ajax call that retrieves the dynamically-created bill in the user's chosen format and type
REQUIRED:
	div with class "xhr_loading" for loading status
	div with class "xhr_result" for displaying results
	div with class "actionButtons" that hold the input buttons that send the form
	div with class "options" that contains the radio inputs with data in them that goes into making the url to create the file
*/
function billSavePrint(){
	var actionButtonsDivs = getElementsByClassName(document,"div","actionButtons");
	for(var i=0,j=actionButtonsDivs.length;i<j;i++){
		var buttons = actionButtonsDivs[i].getElementsByTagName("input");
		for(var a=0,b=buttons.length;a<b;a++){
			buttons[a].onclick = function(){
				var thisLightBox = getParentByClassName(this,"lightBox");
				if(Lightbox.active < 1){Lightbox.active = 1;}//ensure is true for non-CSS users
				//get which type of bill is selected
				var optionsDiv = getElementsByClassName(thisLightBox,"div","options");
				if(optionsDiv.length === 0){return;}
				var options = optionsDiv[0].getElementsByTagName("input");
				for(var y=0,z=options.length;y<z;y++){
					if(options[y].checked){
						var billID = options[y].value;
						break;
					}
				}
				if(!billID){return;}
				var url = "somefile.jsp?filename=" + billID + "&format=" + this.value;
				url="../img/retail/pdf2.pdf";	
				url="../img/retail/p.pdf";
				billXHR(thisLightBox,url)
				return false;
			}
			var billLinks = getElementsByClassName(document,"a","PE_getBill");
			for(var c=0,d=billLinks.length;c<d;c++){
				billLinks[c].onclick = function(){
					if(Lightbox.active < 1){Lightbox.active = 1;}//ensure is true for non-CSS users
					var thisLightBox = getParentByClassName(this,"lightBox");
					billXHR(thisLightBox,this.href);
					return false;
				}
			}
		}
	}
	
	function billXHR(container,url){
		var xhr_loadings = getElementsByClassName(container,"div","xhr_loading");
		if(xhr_loadings.length === 0){return;}
		var xhr_loading = xhr_loadings[0];
		var xhr_results = getElementsByClassName(container,"div","xhr_results");
		if(xhr_results.length === 0){return;}
		var xhr_result = xhr_results[0];
		show(xhr_loading);
		hide(getElementsByClassName(container,"div","billEmail")[0]);
		hide(xhr_result);

		var xhReq = createXMLHttpRequest();
		xhReq.open("GET", url, true);
		for(var g=0,h=container.closeButtons.length;g<h;g++){
			addEvent(container.closeButtons[g],"click",function(){
				xhReq.abort();
				xhReq.onreadystatechange = function(){return false;}
				hide(xhr_loading);
				hide(xhr_result);
				show(getElementsByClassName(container,"div","billEmail")[0]);
			});
		}
		xhReq.onreadystatechange = function() {
			if(Lightbox.active < 1){return false;}
			if (xhReq.readyState != 4)  { return false; }
			hide(xhr_loading);
			
			if (xhReq.status != 200)  { 
				//HTTP response (e.g. 404 page not found, 500 server error) error handling
				bill_error(xhr_result,"Sorry, there was a problem creating your file.");
				return false;
			}
			var xhrResponse = xhReq.responseText;
			//extract file
			var file = "ddd";
			bill_complete(xhr_result,file);
		}
		xhReq.send(null);
	}

	function show(xhr_area){
		if(!xhr_area){return;}
		xhr_area.className = xhr_area.className.replace("nodisplay","");
	}
	function hide(xhr_area){
		if(!xhr_area){return;}
		if(xhr_area.className.indexOf("nodisplay") == -1){
			xhr_area.className += " nodisplay";
		}
	}
	function bill_error(xhr_result,msg){
		var thisLightbox = getParentByClassName(xhr_result,"lightBox");
		var err = document.createElement("p");
			err.className = "warning";
		var errLink = document.createElement("a");
			errLink.className = "warning";
			errLink.href="#";
			errLink.onclick = function(){Lightbox.rehideLightbox(thisLightbox);Lightbox.removeTintBg();}
		var errMsg = document.createTextNode(msg);
		errLink.appendChild(errMsg);
		err.appendChild(errLink);
		xhr_result.innerHTML = "";
		xhr_result.appendChild(err);
		show(xhr_result);
		updateBuffer();
		errLink.focus();
	}
	function bill_complete(xhr_result,billURL){
		var head = document.createElement("h4");
		var headTxt = document.createTextNode("Your bill is ready to view");
		head.appendChild(headTxt);
		var billLink = document.createElement("a");
			billLink.href=billURL;
		var billLinkImg = document.createElement("img");
			billLinkImg.src = "../img/buttons/buttons_ya/btn_viewbill_blue_off.gif";
			billLinkImg.alt="View bill";
			billLinkImg.className = "PE_swap";
		billLink.appendChild(billLinkImg);
		xhr_result.innerHTML = "";
		xhr_result.appendChild(head);
		xhr_result.appendChild(billLink);
		show(xhr_result);
		updateBuffer();
		billLink.focus();
	}
}




/*
TRHOVERS
	trHovers will detect a mouse over out on a table row and change the background of its tds by adding the class 'hover' to the tr, and remove that class on mouse out.
REQUIRED:
	table with class "PE_trHovers"
*/
function trHovers(){
	var hoverTables = getElementsByClassName(document,"table","PE_trHovers");
	for(var i=0,j=hoverTables.length;i<j;i++){
		var trs = hoverTables[i].getElementsByTagName("tr");
		for(var t=0,u=trs.length;t<u;t++){
			if(trs[t].parentNode.nodeName.toLowerCase() == "thead"){continue;}
			trs[t].onmouseover = function(){
				if(this.className.indexOf("hover") == -1){
					this.className += " hover";
				}
			}
			trs[t].onmouseout = function(){
				this.className = this.className.replace("hover","");
			}
		}
	}
}

/*
PRINT PAGE
	buttons to trigger the print dialog for the user to print the page.  these are hidden with CSS and re-displayed with javascript so that non-js users don't see a button they cannot use.
REQUIRED:
	<a> or <input> with class 'print' on it
*/
function printpage(){
	var printBtns = getElementsByClassName(document,"a","PE_print");
	for(var i=0;i<printBtns.length;i++){
		if(window.print){printBtns[i].style.display = "block";}
		printBtns[i].onclick = function(){
			if(window.print){window.print();}
			return false;
		};
	}
}

/* ROLLOVERS	
Will change an image when the user mouses over or focuses on an image, and change it back to the original when the user mouses out or blurs
REQUIRED:
	2 nearly identically named images, with _off at the end of the file name for normal state and _over at the end of the file name for over state
	
	input or image with 'swap' in the classname
	
	e.g. <img src="myfile_off.gif" class="PE_swap" /> where you also have myfile_over.gif
	
ORIGINS: your account 07/08
*/

function imgSwap(){
var swapImgs=getElementsByClassName(document,"*","PE_swap");
	for(var i=0;i<swapImgs.length;i++){
		swapImgs[i].onmouseover=function(){
				var newImg=this.src.replace("_off.","_over.");
				this.src=newImg;
			};
			swapImgs[i].onmouseout=function(){
				var newImg=this.src.replace("_over.","_off.");
				this.src=newImg;
			};
			swapImgs[i].onfocus=swapImgs[i].onmouseover;
			swapImgs[i].onblur=swapImgs[i].onmouseout;
		}
}

/*truncate text and add an ellipsis
takes a string and an integer for how long to make the string*/
function truncate(str,length){
	if(str.length > length){
		str = str.substr(0,length);
		str += "...";
	}
	return str;
}

/*
totalSelected takes an array of elements and returns a number that relates to how many checkboxes are checked or the total of the numbers selected in the select boxes
*/
function totalSelected(elems){
	if(elems.length < 1){return false;}
	var total = 0;
	for(var i=0,j=elems.length;i<j;i++){
	 if(elems[i].type == "checkbox" && elems[i].checked == true){
	 	total++;
	 }
	 if(elems[i].type == "select-one"){
	 	if(!isNaN(elems[i].value)){
			total = total + elems[i].value;
		}
	 }
	}
	return total;
}

/* XML DATA REQUEST AND MANIPULATION FUNCTIONS */

// returns a cross-browser XMLHTTPRequest object
function createXMLHttpRequest() {
   try { return new XMLHttpRequest(); } catch(e) {}
   try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
   try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
   alert("XMLHttpRequest not supported");
   return null;
 } 


//takes a responseText or xml string argument, strips out any content before the xml declaration (e.g. vignette timestamp) and returns an XML object
function stringToValidXML(xmlString){
	if(xmlString.indexOf("<?xml ")>0){
		xmlString=xmlString.substring(xmlString.indexOf("<?xml "));
	}
	if(window.ActiveXObject){
		doc = new ActiveXObject("Microsoft.XMLDOM");
        doc.async="false";
        doc.loadXML(xmlString);
	}
	else if(document.implementation.createDocument){
		var parser = new DOMParser();
        doc = parser.parseFromString(xmlString, "text/xml");
	}
	return doc;
}

//accessibility hack: updates the value of a hidden field to trigger a buffer refresh. called in the functions that are run when an AJAX call has returned data or error
function updateBuffer(){
	var bufferReset = document.getElementById("bufferReset");
	if(!bufferReset){
		var bufferReset = document.createElement("input");
		bufferReset.type="hidden";
		bufferReset.name="bufferReset";
		bufferReset.value=1;
		bufferReset.id="bufferReset";
		document.body.appendChild(bufferReset);
	}
	bufferReset.value++;
}
/*Generic DOM functions*/
function getElementsByClassName(oElm, strTagName, strClassName){
	var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
	var arrReturnElements = new Array();
	strClassName = strClassName.replace(/\-/g, "\\-");
	var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
	var oElement;
	for(var i=0; i<arrElements.length; i++){
		oElement = arrElements[i];		
		if(oRegExp.test(oElement.className)){
			arrReturnElements.push(oElement);
		}	
	}
	return (arrReturnElements);
}

function getParentByTagName(el,tagName){
	var parent=el.parentNode;
	while(tagName.toLowerCase()!=parent.nodeName.toLowerCase()){
		parent=parent.parentNode;
		if(!parent || parent.nodeName=="#document"){return false;}
	}
	return parent;
}
//returns the first parent node of oElm that has strClassName in its class attribute
//classname is 'fuzzy'
function getParentByClassName(oElm,strClassName){
	var parent=oElm.parentNode;
	while(parent.className.indexOf(strClassName)==-1){
		parent=parent.parentNode;		
		if(!parent || parent.nodeName=="#document"){return false;}

	}
	return parent;
}
//returns the first sibling node it finds with the matching class name in a given direction
//requires element, class name to find and previous or next as direction to search in
function getFirstSiblingByClassName(elem,strClassName,direction){
	var sibling = elem[direction+'Sibling'];
	while(sibling){
		if(sibling.nodeType != 1){sibling = sibling.previousSibling;continue;}
		if(sibling.className.indexOf(strClassName) != -1){
			return sibling;
			break;
		}
		sibling = sibling[direction+'Sibling'];
	}
	return false;
}

//finds the elements in oElm's block that has a specified attribute with a specified value
//CAVEAT: IE6 and below has patchy support for class attribute, don't use for that
function getByAttribute(oElm,strTagName,attribName,attribValue){
	var sEls=oElm.getElementsByTagName(strTagName);
	var aElms=new Array();
	for(var s=0;s<sEls.length;s++){
		if(sEls[s].getAttribute(attribName)==attribValue){
			aElms.push(sEls[s]);
		}
	}
	return aElms;
}
function fuzzyClassName(tag,fClass)	{
	var el = (tag == "*" && document.all) ? document.all : document.getElementsByTagName(tag);
	var o=new Array();
	for (var i=0;i<el.length;i++)	{
		if (el[i].className.indexOf(fClass)!=-1)	{
			o.push(el[i]);
		}
	}
	return o;
}
//as fuzzyClassName, but restriced to self and children of a specific DOM object
function fuzzyClassNameBlock(block,tag,fClass)	{
	var el = (tag == "*" && block.all) ? block.all : block.getElementsByTagName(tag);

	var o=new Array();
	for (var i=0;i<el.length;i++)	{
		if (el[i].className.indexOf(fClass)!=-1)	{
			o.push(el[i]);
		}
	}
	if(block.className.indexOf(fClass)!=-1){
		o.push(block);
	}
	return o;
}
//get the contents between ( and ) marks attached to a classname that starts with a given marker
function getPEClassInfo(el,marker){
	var fullClass = el.className;
	var info = fullClass.substring(fullClass.indexOf(marker+"(")+marker.length+1);
	info = info.substring(0,info.indexOf(")"));
	return info;
}

//takes an object and a string of key-value pairs seperated by commas
//adds the key-value pairs as expandos to the object
function getPEHash(el,str){
	var arr = str.split(",")
	for(var i=0;i<arr.length;i++){
		var keyVal = arr[i].split("=");
		el[keyVal[0]] = keyVal[1];
	}
}

//handle cross-browser reading of CSS-set styles
function getStyle(el,stylename){
	if(el.style[stylename]){
		return el.style[stylename];
	}
	else if(el.currentStyle){
		//handle IE's style-name to styleName convention
		if(stylename.indexOf("-") != -1 && !el.currentStyle[stylename]){
			//get 1st char after -, uppercase, rem -
			var preHyphen = stylename.substr(0,stylename.indexOf("-"));
			var postHyphenFirstLetter = stylename.substr(stylename.indexOf("-")+1,1).toUpperCase();
			var postHyphenRemainder = stylename.substr(stylename.indexOf("-")+2);
			stylename = preHyphen + postHyphenFirstLetter + postHyphenRemainder;
		}
		return el.currentStyle[stylename];
	}
	else if(document.defaultView && document.defaultView.getComputedStyle){
		return document.defaultView.getComputedStyle(el,null).getPropertyValue(stylename);
	}
	else{return false;}
}
//handle cross-browser viewport height
function getViewPortHeight(){
	if(window.innerHeight){
		return window.innerHeight;
	}
	else if(document.documentElement && document.documentElement.clientHeight){
		return document.documentElement.clientHeight;
	}
	else if(document.body){
		return document.body.clientHeight;
	}
	else{return false;}
}
//handle cross-browser viewport width
function getViewPortWidth(){
	if(window.innerWidth){
		return window.innerWidth;
	}
	else if(document.documentElement && document.documentElement.clientWidth){
		return document.documentElement.clientWidth;
	}
	else if(document.body){
		return document.body.clientWidth;
	}
	else{return false;}
}
//find and compare viewport and body heights and widths. return the largest of each dimension in array
function getFullPageDimensions(){
	var newHeight = getViewPortHeight();
	var newWidth  = getViewPortWidth();
	/*compare with body */
	if(newWidth < document.body.offsetWidth){
		newWidth = document.body.offsetWidth;
		newWidth += parseInt(getStyle(document.body,"margin-left")) + parseInt(getStyle(document.body,"margin-right"));
	}
	if(newHeight < document.body.offsetHeight){
		newHeight = document.body.offsetHeight;
		newHeight += parseInt(getStyle(document.body,"margin-top")) + parseInt(getStyle(document.body,"margin-bottom"));
		if(window.innerWidth && window.innerWidth > document.body.scrollWidth){newWidth -= 17;}//handle Firefox bug where it counts the scrollbar too for scrolling pages
	}
	return [newWidth,newHeight];
}
//handle cross-browser retrieval of how far user has scrolled down
function getScrollTop(){
	if(window.pageYOffset){
		return window.pageYOffset;
	}
	else if(document.documentElement && document.documentElement.scrollTop){
		return document.documentElement.scrollTop;
	}
	else if(document.body){
		return document.body.scrollTop;
	}
	else{return false;}
}
//get the first text node of a given element
function getTextNode(elem){
	var children = elem.childNodes;
	for(var i=0;i<children.length;i++){
		if(children[i].nodeType == 3){
			return children[i];
		}
	}
	return false;
}
//handle cross-browser adding events
function addEvent(obj,evt,fn){
	if(document.addEventListener){
		addEvent = function(obj,evt,fn){
			obj.addEventListener(evt,fn,false);
		}
	}
	else if(document.attachEvent){
		addEvent = function(obj,evt,fn){
			obj["e"+evt+fn] = fn;
			obj[evt+fn] = function() { obj["e"+evt+fn]( window.event ); }
			obj.attachEvent( "on"+evt, obj[evt+fn] );
		}
	}
	else{
		return false;
	}
	addEvent(obj,evt,fn);
}

// returns an absolute x position of the given element object 'o'.
function getAbsoluteX(o) {
	oLeft=o.offsetLeft;            
	while(o.offsetParent!=null) { 
		oParent=o.offsetParent;
		oLeft+=oParent.offsetLeft;
		o=oParent;
	}
	return oLeft;
}
// returns an absolute y position of the given element object 'o'.
function getAbsoluteY(o) {
	oTop=o.offsetTop;            
	while(o.offsetParent!=null) { 
		oParent=o.offsetParent;
		oTop+=oParent.offsetTop;
		o=oParent;
	}
	return oTop;
}

/*End Generic DOM functions*/
// -------------------------------- 
// Detect IE5 Mac 
function isIE5Mac() {
	var ua = navigator.userAgent;
	if (ua.indexOf("Mac") != -1 && ua.indexOf("MSIE 5") != -1) {
		return true;
	} else {
		return false;
	}
}

/*IE5.0 ARRAY PROTOTYPE PUSH*/
if(typeof Array.prototype.push=="undefined"){
	Array.prototype.push=function(item){
		return this[this.length] = item;
	};
}


/*PNG handling for inline images
based on supersleight.js
*/
function pngHandling(wrapper){
var supersleight=function(){var root=wrapper;
	var applyPositioning=true;
	var shim='../img/css/blank.gif';
	var shim_pattern=/blank\.gif$/i;
var fnLoadPngs=function(){if(root){root=document.getElementById(root);}else{root=document;}
if(!root){return;}
for(var i=root.all.length-1,obj=null;(obj=root.all[i]);i--){if(obj.currentStyle.backgroundImage.match(/\.png/i)!==null){bg_fnFixPng(obj);}
if(obj.tagName=='IMG'&&obj.src.match(/\.png$/i)!==null){el_fnFixPng(obj);}
if(applyPositioning&&(obj.tagName=='A'||obj.tagName=='INPUT')&&obj.style.position===''){obj.style.position='relative';}}};var bg_fnFixPng=function(obj){var mode='scale';var bg=obj.currentStyle.backgroundImage;var src=bg.substring(5,bg.length-2);if(obj.currentStyle.backgroundRepeat=='no-repeat'){mode='crop';}
obj.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+src+"', sizingMethod='"+mode+"')";obj.style.backgroundImage='url('+shim+')';};var el_fnFixPng=function(img){var src=img.src;img.style.width=img.width+"px";img.style.height=img.height+"px";img.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+src+"', sizingMethod='scale')";img.src=shim;};var addLoadEvent=function(func){var oldonload=window.onload;if(typeof window.onload!='function'){window.onload=func;}else{window.onload=function(){if(oldonload){oldonload();}
func();};}};return{init:function(){addLoadEvent(fnLoadPngs);},limitTo:function(el){root=el;},run:function(){fnLoadPngs();}};}();
//apply with IE6 and below only
/*@cc_on @*/
/*@if (@_jscript_version <= 5.6)
supersleight.init();	
/*@end @*/		

}

