function isblank(s){
	for(var i = 0; i < s.length; i++) {
		var c = s.charAt(i);
		if ((c != ' ') && ( c != '\n') && (c != '\t')) return false;
	}
	return true;
}

function configDateTimes(fieldList,frm) {
	var fldsArray,dateVal,hrs,mins;
	fldsArray = fieldList.split(',');
	for (i=0; i < fldsArray.length; i++) {
		dateVal = eval('frm.'+fldsArray[i]).value.split(' ');
		dateVal = dateVal[0];
		hrs = eval('frm.'+fldsArray[i]+'_hour_.selectedIndex');
		mins = eval('frm.'+fldsArray[i]+'_min_.selectedIndex');
		if ((dateVal != '') && (dateVal != 'NULL')) {
			eval('frm.'+fldsArray[i]).value = dateVal + ' '+hrs+':'+mins+':0';
		}
	}
}

function buildHiddenDate(fieldList,frm) {
	var fldsArray,dateTimeVal,outputDate;
	fldsArray = fieldList.split(',');
	for (i=0; i < fldsArray.length; i++) {
		dateTimeVal = eval('frm.'+fldsArray[i]+'_').value;
		if (isblank(dateTimeVal)) {
			eval('frm.'+fldsArray[i]).value='NULL';
		} else {
			eval('frm.'+fldsArray[i]).value=dateTimeVal;
		}
	}
}

function buildDropDownDate(fieldList,frm) {
	var fldsArray,dateVal,dayVal,monthVal,yearVal,dayCtrl,monthCtrl,yearCtrl,outputDate;
	fldsArray = fieldList.split(',');
	for (i=0; i < fldsArray.length; i++) {
		dayCtrl = eval('frm.'+fldsArray[i]+'_day_');
		monthCtrl = eval('frm.'+fldsArray[i]+'_month_');
		yearCtrl = eval('frm.'+fldsArray[i]+'_year_');
		dayVal = dayCtrl.options[dayCtrl.selectedIndex].value;
		monthVal = monthCtrl.options[monthCtrl.selectedIndex].value
		yearVal = yearCtrl.options[yearCtrl.selectedIndex].value;
		dateVal = eval('frm.'+fldsArray[i]).value.split(' ');
		if (dateVal.length>1) {
			timeVal = dateVal[1];
		} else {
			timeVal='';
		}
		outputDate = '';
		if ((dayVal !='') && (monthVal != '') && (yearVal != '')) {
			outputDate = yearVal + '/' +monthVal+ '/' + dayVal
		}
		if (isblank(outputDate)) {
			eval('frm.'+fldsArray[i]).value='NULL'
		} else {
			eval('frm.'+fldsArray[i]).value=outputDate
		}
		//hrs = eval('frm.'+fldsArray[i]+'_hour_.selectedIndex');
		//mins = eval('frm.'+fldsArray[i]+'_min_.selectedIndex');
		//if ((dateVal != '') && (dateVal != 'NULL')) {
			//eval('frm.'+fldsArray[i]).value = dateVal + ' '+hrs+':'+mins+':0';
		//}
	}
}

function defaultMsg(){
	var msg;
	msg =  "_______________________________________________\n\n"
	msg += "The form was not submitted because of the following error(s).\n";
	msg += "Please correct the error(s) and re-submit.\n";
	msg +=  "_______________________________________________\n\n"
	return msg;
}

//This code prevents dotted lines when clicking on links
function ExplorerFix() {
		for (a in document.links) document.links[a].onfocus = document.links[a].blur;
}

if (document.all)	document.onmousedown = ExplorerFix;

var isNS4 = (document.layers) ? true : false;
var isIE4 = (document.all && !document.getElementById) ? true : false;
var isIE5 = (document.all && document.getElementById) ? true : false;
var isNS6 = (!document.all && document.getElementById) ? true : false;

// The code below has been in tools.js right from the very beginning
// It doesn't make any sense and certainly does not belong here as it certainly does not apply to every site.
// It has never caused a problem until now as if current is never set to anything other than false then showHide will do nothing
// Unfortunately IATC had some script of it's own using a veriable called current which then through up errors on every page click.

/*var current = false;
function showHide(layerName){
	if ((current) && (layerName != current)) {
		setVis(current, 'hidden');
	} 
	if (layerName) {
		setVis(layerName, 'visible');
	} 
	current = layerName;
}
				
if (isNS4) document.captureEvents(Event.MOUSEUP);
document["onmouseup"] = function(event) {showHide(false);}*/

function checkSearch(frm) {
	var ok = true;
	if (isblank(frm.search.value)) {
		ok = false;
		alert('Please enter a search string');
	} else {
		frm.submit();
	}
	return false;
}

function confirmDelete(ref,tableName,mode,rootPath) {
	msg = 'WARNING!!!!!\nThis will permanently delete this item.\n';
	msg += 'Are you sure?...Click OK to continue or CANCEL to stop.';
	if (window.confirm(msg)) window.open(rootPath+'library/asp/del.asp?table=' + tableName + '&ref='+ref+'&mode='+mode, 'del', 'width=100,height=100,resizable');
	return false
}

//Takes a name and value and creates a cookie of the given name with the given value
function setCookie(name, val){
	document.cookie = name+"="+val+"; path=/";
	return true;
}

/*Takes a cookie name and returns its value as a string for numeric values 
	you need to do a parse on the result*/
function getCookie(name){
	var val, cookiename;
	var cookieArray = document.cookie.split(";");
	for(var i = 0; i < cookieArray.length; i++){
		cookiename = cookieArray[i].split("=")[0];
		if (cookiename.substr(0,1) == ' ') {
			cookiename = cookiename.substr(1);
		}
		if(cookiename.toLowerCase() == name.toLowerCase()) {
			val = cookieArray[i].split("=")[1];
		}
	}
	return unescape(val);
}

// Adds an item to a comma separated list 
// if it does not exist or removes it if it does

function addRemoveListItem (item, list){
	//make an array from the list
	var list2Array = list.split(",");
	//create an array in which we will build the list after the insertyion/deletion
	var newList = new Array();
	var count = 0, i = 0, foundItem;
	//if (!((list2Array.length == 1) && (list2Array[0]=='undefined'))) {
	// if the list is not empty
	if (list.length > 0) {
		//loop around the existing items to see if we can find the item in the array
		for (i = 0; i < list2Array.length; i++) {
			foundItem = foundItem || (list2Array[i] == item)
			//if this existing item is not the item in question and it sin't blank or undefined then add it to the new list
			if ((list2Array[i] != item) && (list2Array[i] !='undefined') && !isblank(list2Array[i])) {
				newList[count] = list2Array[i];
				count++;
			}
		}
	}
	// if the item in question was not found in the existing list then add it in
	if (!foundItem){
		newList[count] = item;
	}
	//alert(newList.join(','));
	return newList.join(',');
}

function addRemoveCodeListItem (item, list,inheritance){
	//make an array from the list
	var list2Array = list.split(",");
	//create an array in which we will build the list after the insertyion/deletion
	var newList = new Array();
	var count = 0, i = 0, foundItem;
	var inputArray,inputElement,inputId,thisItemControlId,parentCode,foundChildItem,k,j,i,parentIconId;
	// if the list is not empty
	if (list.length > 0) {
		if (inheritance=='none') {
			for (i = 0; i < list2Array.length; i++) {
				foundItem = foundItem || (list2Array[i] == item)
				//if this existing item is not the item in question and it sin't blank or undefined then add it to the new list
				if ((list2Array[i] != item) && (list2Array[i] !='undefined') && !isblank(list2Array[i])) {
					newList[count] = list2Array[i];
					count++;
				}
			}
		} else {
			inputArray = document.getElementsByTagName('input');
			thisItemControlId = 'profileCheckbox'+item;
			//loop around the existing items to see if we can find the item in the array
			for (i = 0; i < list2Array.length; i++) {
				foundItem = foundItem || (list2Array[i] == item)
			}
			for (i = 0; i < list2Array.length; i++) {
				//if this existing item is not the item in question and it isn't blank or undefined then add it to the new list
				if (foundItem) {
					//removing an item so ok to add all items except the one being removed
					if ((list2Array[i] != item) && (list2Array[i] !='undefined') && !isblank(list2Array[i])) {
						newList[count] = list2Array[i];
						count++;
					}
				} else {
					//we are adding an item so we want to remove all child items from the list too
					if ((list2Array[i].substring(0,item.length) != item) && (list2Array[i] !='undefined') && !isblank(list2Array[i])) {
						newList[count] = list2Array[i];
						count++;
					}
				}
			}
			if (foundItem) {
				// removing item so need to enable all child items and re-evaluate hasChildren for parent items
				for (j=0;j<inputArray.length;j++) {
					inputElement=inputArray[j];
					inputId = inputElement.getAttribute('id');
					if (inputId != null) {
						if (inputId.substring(0,thisItemControlId.length)==thisItemControlId) {
							document.getElementById(inputId.replace('profileCheckbox','profileCheckboxContainer')).style.display = 'inline';
							document.getElementById(inputId.replace('profileCheckbox','parentCheckIconContainer')).style.display = 'none';
						}
					}
				}
				//loop through the parents of this item to see if hasChildrenChecked still applies
				if (item.length>4) {
					parentCode = item.substring(0,item.length-4);
					while (parentCode.length>0) {
						foundChildItem = false;
						for (k = 0; k < newList.length; k++) {
							foundChildItem = foundChildItem || (newList[k].substring(0,parentCode.length) == parentCode)
						}
						if (!foundChildItem) {
							parentIconId = 'childCheckIconContainer'+parentCode;
							if (document.getElementById(parentIconId)) {
								document.getElementById(parentIconId).style.display = 'none';
							}
						}
						parentCode = parentCode.substring(0,parentCode.length-4)
					}
				}
			} else {
				// adding item so need to disable all child items mark them as parent selected ensure all parents are flagged as hasChildrenSelected
				document.getElementById('childCheckIconContainer'+item).style.display = 'none';
				for (var j=0;j<inputArray.length;j++) {
					inputElement=inputArray[j];
					inputId = inputElement.getAttribute('id');
					if (inputId != null) {
						if ((inputId.substring(0,thisItemControlId.length)==thisItemControlId) && (inputId!=thisItemControlId)) {
							inputElement.checked = false;
							document.getElementById(inputId.replace('profileCheckbox','profileCheckboxContainer')).style.display = 'none';
							document.getElementById(inputId.replace('profileCheckbox','parentCheckIconContainer')).style.display = 'inline';
							document.getElementById(inputId.replace('profileCheckbox','childCheckIconContainer')).style.display = 'none';
						}
						if ((thisItemControlId.substring(0,inputId.length)==inputId) && (inputId!=thisItemControlId) && (inputId!='')) {
							document.getElementById(inputId.replace('profileCheckbox','childCheckIconContainer')).style.display = 'inline';
						}
					}
				}
			}
		}
	}
	// if the item in question was not found in the existing list then add it in
	if (!foundItem){
		newList[count] = item;
	}
	return newList.join(',');
}



function autoFormatText(input,encodespecialCharacters,convertLineBreaks,autoLink) {
	var formattedText=input;
	var existingLinks=formattedText.match(/<a [^>]*>.*(<\/a>)?/ig);
	//hide existing links
	formattedText=formattedText.replace(/<a [^>]*>.*(<\/a>)?/ig,'[~link~]');
	var existingTags=formattedText.match(/<[^>]*>/ig);
	//hide existing tags
	formattedText=formattedText.replace(/<[^>]*>/ig,'[~tag~]');
	if (encodespecialCharacters) {
		// to be written
	}
	if (convertLineBreaks) {
		formattedText=formattedText.replace(/(\r)*\n/g,'<br>\n');
		formattedText=formattedText.replace(/\r/g,'<br>\r');
	} 
	if (autoLink) {
		formattedText=formattedText.replace(/((\w|-)+(\.(\w|-)+)*@(\w|-)+(\.(\w|-)+)+)/g,'<a href="mailto:$1">$1</a>');
		//formattedText=formattedText.replace(/(http:\/\/(\w|-)+(\.(\w|-)+)*)/g,'<a href="$1" target="_blank">$1</a>');
		formattedText=formattedText.replace(/(http:\/\/(\w|-|\.|\/)+)/g,'<a href="$1" target="_blank">$1</a>');
	} 
	//unhide existing links
	if (existingLinks!=null) {
		for (var i=0;i<existingLinks.length;i++) {
			formattedText=formattedText.replace(/\[~link~\]/,existingLinks[i]);
		}
	}
	if (existingTags!=null) {
		for (var i=0;i<existingTags.length;i++) {
			formattedText=formattedText.replace(/\[~tag~\]/,existingTags[i]);
		}
	} 
	return formattedText;
}

function autoFormat(formObj,fieldname) {
	var formattedText=eval('formObj.'+fieldname).value;
	eval('formObj.'+fieldname).value=autoFormatText(formattedText,true,true,true);
}

function clearHTML(formObj,fieldname) {
	eval('formObj.'+fieldname).value=stripHTML(eval('formObj.'+fieldname).value);
}

function stripHTML(inputString) {
	return inputString.replace(/<([^\n>])+>/g,'');
}

function htmlEncode(inputString) {
		var outputString = inputString;
		var matches;
		// All characters above 128 to be replaced by their ascii equivalents
		//var regExpObj = /[^\x00-\x80]/gi;
		var regExpObj = new RegExp("[^\\x00-\\x80]", "gi");
		while ((matches = regExpObj.exec(outputString)) != null) {
			outputString = outputString.replace(matches[0],'&#'+matches[0].charCodeAt(0)+';');
		}
		// replace other known illegal characters with defined entities
		outputString = outputString.replace(/"/ig,'&quot;');
		outputString = outputString.replace(/</ig,'&lt;');
		outputString = outputString.replace(/>/ig,'&gt;');
		// replace all ampersands with &amp; that are not part of currently encoded entities
		//var regExpObj = /(&)([\w#]{1,7};)?/gi;
		var regExpObj = new RegExp("(&)([\\w#]{1,7};)?", "gi");
		while (((matches = regExpObj.exec(outputString)) != null )) {
			if (matches[0]==matches[1]) {
				outputString = outputString.substring(0,matches.index) + '[~amp~]amp;' + outputString.substring(matches.index+1);
			} else {
				outputString = outputString.substring(0,matches.index) + '[~amp~]' + outputString.substring(matches.index+1);    
			}
		}
		outputString = outputString.replace(/\[~amp~\]/ig,'&');
		return outputString
	}
	
function htmlEncodeTag(inputString) {
		var attributeValue,attributeName,rebuildAttribute;
		var attributeQuote,encodedAttributeValue;
		var outputString = inputString;
		var undefined;
		var debugMessage
		// Not quoting attribute values
		// Not lowercasing attribute names
		//var regExpObj = new RegExp('=\\s*"([^"]+)"+', 'gi');
		var regExpObj = new RegExp('([^\\s=]+\\s*)=\\s*(((\')([^\']+)(\'))|((")([^"]+)("))|([^\'"\\s>]+))','gi');
		var encodeTest = new RegExp('([^\x00-\x80]|<|>|&|")','gi');
		while ((matches = regExpObj.exec(outputString)) != null) {
			debugMessage = 'Found: '+matches[0]+'\n';
			for (var i=1;i<matches.length;i++) {
				debugMessage += (i+': '+matches[i]+'\n');
			}
			//alert(debugMessage);
			rebuildAttribute = false;
			attributeValue = '';
			//matchLength = aMatch.length
			attributeName = matches[1];
			rebuildAttribute = (attributeName!=attributeName.toLowerCase());
			//document.write('{checking: ' + attributeName + '}<br>\n');
			if ((matches[11]!=undefined) && (matches[11]!='')) {
				attributeQuote = '"';
				attributeValue = matches[11];
				rebuildAttribute = true;
			} else if ((matches[3]!=undefined) && (matches[3]!='')) {
				attributeQuote = '\'';
				attributeValue = matches[5];
			} else if ((matches[7]!=undefined) && (matches[7]!='')) { 
				attributeQuote = '"';
				attributeValue = matches[9];
			} 
			//alert(attributeName + ':' + attributeValue)
			if (encodeTest.test(attributeValue)) {
				encodedAttributeValue = htmlEncode(attributeValue);
				rebuildAttribute = true;
			} else {
				encodedAttributeValue = attributeValue;
			}
			if (rebuildAttribute) {
				//document.write('{rebuilding: ' + attributeName + ':' + attributeValue + '}<br>\n');
				outputString = outputString.substring(0,matches.index) + attributeName.toLowerCase() + '[~eq~]' + attributeQuote + encodedAttributeValue.replace(/=/ig,'[~eq~]') + attributeQuote + outputString.substring(matches.index+matches[0].length);
			}
			//document.write('<hr>');
		}
		outputString = outputString.replace(/\[~eq~\]/ig,'=');
		return outputString;
	}

	function fixCharacterEncoding(inputString) {
		var outputString = inputString;
		var matches,regExpObj,rxComment;
		var scriptBlocks = new Array;
		// Protect the contents of all script tags
		var regExpObj = new RegExp("(<script[^<>]*>)((.|\n)*?)(</script>)", "gi");
		while ((matches = regExpObj.exec(outputString)) != null) {
			scriptBlocks[scriptBlocks.length] = matches[2];
		}
		outputString = outputString.replace(regExpObj,"$1[~script~]$4");
		// strip all html comments as these interfere with the encoding process and make it very difficult to identify the start and end of tags
		rxComment = new RegExp("<!--(.|\n)*?-->", "gi");
		outputString = outputString.replace(rxComment,""); // This needs to be done using a proper regExp object to avoid errors in IE 5 on page load. Using the object means the pattern gets passed as a string and only gets parsed if the function is called.
		// find all text not part of an html tag and HMTL Encode
		//var regExpObj = /(^|>)((.|\n)*?)((<([a-zA-Z]|\/|\?|!))|$)/gi;
		regExpObj = new RegExp("(^|>)((.|\\n)*?)((<([a-zA-Z]|\\/|\\?|!))|$)", "gi");
		while ((matches = regExpObj.exec(outputString)) != null) {
			//outputString = outputString.replace(matches[2],htmlEncode(matches[2]));
			outputString = outputString.substring(0,matches.index) + matches[1] + htmlEncodeTag(matches[2]) + matches[4] + outputString.substring(matches.index+matches[0].length);
		}
		// find all HTML Tags and encode
		//var regExpObj = /<([a-zA-Z]|\/|\?|!)[^<>]+=[^<>]+>/gi; //="?((.|\n)+)"|\s(.|\n)+?
		regExpObj = new RegExp("<([a-zA-Z]|\\/|\\?|!)[^<>]+=[^<>]+>", "gi");
		while ((matches = regExpObj.exec(outputString)) != null) {
			//alert('Matches[0]: '+ matches[0] + '\n\nHTML encode bit: ' + htmlEncodeTag(matches[0]).replace(/=/,'[~eq~]'));
			outputString = outputString.substring(0,matches.index) + htmlEncodeTag(matches[0]).replace(/=/,'[~eq~]') + outputString.substring(matches.index+matches[0].length);
		}
		// Reinstate the contents of all script tags
		for (var i=0;i<scriptBlocks.length;i++) {
			outputString = outputString.replace(/\[~script~\]/i,scriptBlocks[i]);
		}
		
		outputString = outputString.replace(/\[~eq~\]/ig,'=');
		return outputString
	}
	
	function preLoadImages() {
		var imageList;
		imageList = new Array();
		for (var i=0;i<arguments.length;i++) {
			imageList[i] = new Image;
			imageList[i].src=arguments[i];
		}
	}
	
	function swapTag(inputString, oldTag, newTag) {
		var outputStr, regExpObj;
		outputStr = inputString;
		if (outputStr.length>0) {
			var regExpObj = new RegExp('(<\/?)'+oldTag+'((\s[^>]*)*>)','gi');
			outputStr = outputStr.replace(regExpObj, '$1'+newTag+'$2');
		}
		return outputStr;
	}
	
	function tagsToLowerCase(inputString) {
		var outputString = inputString;
		var matches,done;
		done='|';
		var matchReplaceRX = new RegExp;
		var regExpObj = new RegExp('(<\\/?[a-z0-9\\-:_]*[A-Z]+[a-z0-9\\-:_]*)[\\s>]','gi');
		while ((matches = regExpObj.exec(outputString)) != null) {
			if (done.indexOf('|' + matches[0] + '|')<0) {
				// We have to use a regular expression here to make sure the replace is global
				matchReplaceRX.compile(matches[0],"ig")
				outputString = outputString.replace(matchReplaceRX,matches[0].toLowerCase());
				done = done + matches[0] + '|';
			}
		}
		return outputString;
	} 
	
	function regExpMatchArray(inputString,regExpObj,unique,includeSubMatches) {
		var matches,done,allMatches;
		done='';
		allMatches = new Array;
		while ((matches=regExpObj.exec(inputString)) != null) {
			if ((!unique) || (done.indexOf('|' + matches[0] + '|')<0)) {
				if (includeSubMatches) {
					allMatches[allMatches.length]=matches;
				} else {
					allMatches[allMatches.length]=matches[0];
				} 
				done = done + matches[0] + '|';
			}
		}
		return allMatches;
	}
	
	function cleanHTML(strHTML,validTagList,validAttributeList,validStyleParameters) {
		//Takes in comlex HTML and returns a clean simple format
		var objRegExp, strOutput, validTags, validAttributes, validStartTags, validEndTags;
		var validTagAttributes, tagHeader, validatedTag, validatedStartTags, styleAttributes;
		var validatedStyleParameters, validatedStyleParameter, validatedStyleAttribute;
		var startTagCount, startTag, endTagCount, endTag, validTagCount, thisTag, validatedStartTag;
		var validTagAttribute,styleAttribute,styleRegExp,styleQuote;
		var validTagAttributeParts, arrayIndex, validAttributeParam, objRegExp2, equals, stylesOk;
		var allStyleRegExp, colons,i,j,k;
		objRegExp = new RegExp;
		objRegExp2 = new RegExp;
		if (validTagList=='') {
			// use the default tag list
			validTagList='a,p,br,b,strong,i,u,table,th,td,tr,img,span,div,ul,li,ol,hr,h1,h2,h3,em,blockquote,param';
		} else if (validTagList.toLowerCase()=='none') {
			validTagList='';
		}
		if (validAttributeList=='') {
			// use the default attribute list
			validAttributeList='class,colspan,rowspan,align,valign,src,height,width,border,cellpadding,cellspacing,type,summary,alt,href,target,bgcolor,hspace,vspace,style,clear,name';
		} else if (validAttributeList.toLowerCase()=='none') {
			validAttributeList='';
		}
		if (validStyleParameters=='') {
			// use the default attribute list
				validStyleParameters = 'background,background-color,color';
				validStyleParameters = validStyleParameters + ',margin,margin-left,margin-right,margin-top,margin-bottom';
				validStyleParameters = validStyleParameters + ',padding,padding-left,padding-right,padding-top,padding-bottom';
				validStyleParameters = validStyleParameters + ',border-top,border-bottom,border-left,border-right';
				validStyleParameters = validStyleParameters + ',font-weight,font-size,font-family';
		} else if (validStyleParameters.toLowerCase()=='none') {
			validStyleParameters='';
		}
		// we need to process style tags seperately to allow us to select individual params
		// so exlude it from the validAttributesList


		if ((','+validAttributeList+',').indexOf(',style,')>=0) {
			validAttributeList=validAttributeList.replace(/style,/ig,'');
			validAttributeList=validAttributeList.replace(/,style/ig,'');
			stylesOk = true;
		} else {
			stylesOk = false;
		}

		//Replace all HTML comments with the empty string
		objRegExp.compile('<!--(.|\n)+?-->','ig');
		strOutput = strHTML.replace(objRegExp,'');

		//objRegExp.Pattern = "<!(--)?\[if(.|\n)+?\[endif\](--)?>"
		//Replace all conditional code with the empty string, this is to combat wierd vml code inserted by word
		//strOutput = objRegExp.Replace(strOutput, "")
		if ((validTagList.toLowerCase()!='all') || (validAttributeList.toLowerCase()!='all')) {
			//Remove all invalid tags this involves:
			// - hide all valid tags
			// - remove all remaining tags
			// - unhide the valid tags
			if (validTagList.toLowerCase()=='all') {
				objRegExp.compile('<((.|\n)+?)>','ig');
				strOutput=strOutput.replace(objRegExp,'[~lt~]$1[~gt~]');
			} else {
				objRegExp.compile('<((' + validTagList.replace(/,/ig,'|') + ')(\\s[^>]*)*)?>','ig');
				strOutput=strOutput.replace(objRegExp,'[~lt~]$1[~gt~]');
				objRegExp.compile('<(\\/(' + validTagList.replace(/,/ig,'|') +')\\s*)?>','ig');
				strOutput=strOutput.replace(objRegExp,'[~lt~]$1[~gt~]');
				objRegExp.compile('<(.|\\n)+?>','ig');
				//Replace all HTML tag matches with the empty string
				strOutput = strOutput.replace(objRegExp,'');
			}

			strOutput=strOutput.replace(/\[~lt~\]/ig,'<');
			strOutput=strOutput.replace(/\[~gt~\]/ig,'>');

			validatedStartTags = '';
			//We are only interested in tags that have attributes as we have already established that the tag itself is ok.
			objRegExp.compile('<[^\\/][^>]*?=[^>]*?>','ig');
			validStartTags=regExpMatchArray(strOutput,objRegExp,true,false);      
			objRegExp2.compile('\\s\\S+\\s*=\\s*((\'[^\']*\')|("[^"]*")|([^\'\\s">]*))','ig');
			//clean up attributes within acceptable start tags here
			for (i=0;i<validStartTags.length;i++) {
				startTag=validStartTags[i];
				thisTag=startTag;
				// extract valid tag attributes
				objRegExp.compile('\\s(' + validAttributeList.replace(/,/ig,'|') + ')\\s*=\\s*((\'[^\']*\')|("[^"]*")|([^\'\\s">]*))','ig');
				validTagAttributes = regExpMatchArray(thisTag,objRegExp,false,false);
				equals = regExpMatchArray(thisTag,objRegExp2,false,false);
				if (validTagAttributes.length!=equals.length) {
					objRegExp.compile('<[^\\s>]+','ig');
					tagHeader=objRegExp.exec(thisTag);
					validatedStartTag=tagHeader[0];
					for (j=0;j<validTagAttributes.length;j++) {
						validatedStartTag=validatedStartTag + validTagAttributes[j];
					}
					if ((validStyleParameters!='') && stylesOk) {
						// extract style attributes
						objRegExp.compile('\\sstyle\\s*=\\s*((\'[^\']*\')|("[^"]*")|([^\\s\'">]*))','ig');
						styleAttributes = regExpMatchArray(thisTag,objRegExp,false,false);
						for (j=0;j<styleAttributes.length;j++) {
							styleAttribute=styleAttributes[j];
							if (validStyleParameters.toLowerCase()=='all') {
								validatedStartTag=validatedStartTag + styleAttribute;
							} else {
								objRegExp.compile('\\sstyle\\s*=\\s*(\'.*\')','ig');
								if (objRegExp.test(styleAttribute)) {
									styleQuote='\'';
									styleRegExp = '(' + validStyleParameters.replace(/,/ig,'|') + ')\\s*:\\s*[^\';]*';
									allStyleRegExp = '[^;\\s\']+\\s*:\\s*[^\';]*';
								} else {
									objRegExp.compile('\\sstyle\\s*=\\s*(".*")','ig');
									if (objRegExp.test(styleAttribute)) {
										styleQuote='"';
										styleRegExp = '(' + validStyleParameters.replace(/,/ig,'|') + ')\\s*:\\s*[^";]*';
										allStyleRegExp = '[^;\\s"]+\\s*:\\s*[^";]*';
									} else {
										styleQuote = ''
										styleRegExp = '(' + validStyleParameters.replace(/,/ig,'|') + '):[^\\s;]*';
										allStyleRegExp = '[^;\\s]+:[^\\s;]*';
									}
								}
								objRegExp.compile(styleRegExp,'ig');
								validatedStyleParameters = regExpMatchArray(styleAttribute,objRegExp,false,false);
								validatedStyleAttribute=' style='+styleQuote;
								for (k=0;k<validatedStyleParameters.length;k++) {
									validatedStyleParameter=validatedStyleParameters[k];
									validatedStyleAttribute=validatedStyleAttribute + validatedStyleParameter + ';';
								}
								validatedStyleAttribute=validatedStyleAttribute + styleQuote;
								if (validatedStyleAttribute.replace(/\s/,'')=='style='+styleQuote+styleQuote) {
									validatedStyleAttribute=''
								}
								validatedStartTag=validatedStartTag+validatedStyleAttribute
							}
						}
					}
					validatedStartTag=validatedStartTag + '>';
					//replace dirty tag with clean tag
					if (validatedStartTags.indexOf(thisTag)<0) {
						objRegExp.compile(thisTag,'ig');
						strOutput = strOutput.replace(objRegExp,validatedStartTag);
						validatedStartTags = validatedStartTags + thisTag;
					}
				}
			}
		}
		return strOutput;//Return the value of strOutput
	}
	
	function cleanWordHTML(strHTML) {
		// for now call cleanHTML with the default valid lists
		var validTags,validAttributes,validStyleParameters;
		validTags = 'a,p,br,b,strong,i,u,table,th,td,tr,img,span,div,ul,li,ol,hr,h1,h2,h3,em,blockquote,param';
		validAttributes = 'colspan,rowspan,align,valign,src,height,width,border,cellpadding,cellspacing,type,summary,alt,href,target,bgcolor,hspace,vspace,style,clear,name';
		validStyleParameters = 'background,background-color,color';
		validStyleParameters = validStyleParameters + ',margin,margin-left,margin-right,margin-top,margin-bottom';
		validStyleParameters = validStyleParameters + ',padding,padding-left,padding-right,padding-top,padding-bottom';
		validStyleParameters = validStyleParameters + ',border-top,border-bottom,border-left,border-right';
		//validStyleParameters = validStyleParameters + ',font-weight,font-size,font-family';
		return cleanHTML(strHTML,validTags,validAttributes,validStyleParameters);
		
		//Issues
		//======
		
		//1) word sets the margins on paragraphs using style to nothing and uses dummy paragraphs containing &nbsp; to create line spacing
		//   this means that when these margin attributes are stripped the test becomes double spaced
		//   it would be nice to strip these dummy paragraphs
		//2) At the moment we have only addresses cut and paste from word which immediately looses all the header information
		//   This works well for us at the mo but if we wanted to allow people to cut word html and paste into the source of the editor
		//   we would have a lot more work to do interpreting the header.
		//3) we end up with a lot of empty span tags when most style attributes are stripped
		//4) In many cases it would be nice to move the style definition out of the span into the parent <p> tag.
		//5) The current means of defining what is and isn't allowed is too crude we need to be able to say what is valid under what circumstances
		//6) We also need to be able to configure alternatives for some things <strong> instead of <b> for example in a very simple case
		//7) Also some units are ok and some aren't so table widths in % may be classed as ok but not in pixels
		
	}
	
	function buildCompliantHTML(inputString) {
		var outputStr
		//Make local copy to avoid odd occurances of editing actual var passed in
		outputStr = inputString;
		if (outputStr.length>0) {
			outputStr = outputStr.replace(/\starget=""/ig,'');
			outputStr = swapTag(outputStr, "b", "strong");
			outputStr = swapTag(outputStr, "i", "em");
		// outputStr = cleanHTML(outputStr,"","","")
			outputStr = tagsToLowerCase(outputStr);
			outputStr = fixCharacterEncoding(outputStr);
			//When it exists we should also call the function to quote attributes
		}
		return outputStr;
	}
	
	function ensureTagClosure(inputString) {
		var outputHTML = inputString;
		var regExpObj;
		regExpObj = new RegExp("<(img|meta|br|hr|param)([^>]*[^/])?>", "gi");
		outputHTML = outputHTML.replace(regExpObj,"<$1$2 />");
		regExpObj.compile("<(img|meta|br|hr|param)([^>]*[^\\s])?/>", "gi");
		outputHTML = outputHTML.replace(regExpObj,"<$1$2 />");
		return outputHTML;
	}

	function fixListNesting(inputString) {
		var outputHTML = inputString;
		var regExpObj;
		regExpObj = new RegExp("(<(ul|ol)[^>]*>[^<]*)(<(ul|ol)[^>]*>)", "gi");
		while (regExpObj.test(outputHTML)) {
			outputHTML = outputHTML.replace(regExpObj,"$1<li>$3");
		}
		regExpObj.compile("(</li>\\s*)(<(ul|ol)[^>]*>)", "gi");
		outputHTML = outputHTML.replace(regExpObj,"$2");
		regExpObj.compile("(</(ul|ol)>\\s*)(<(li|/ul|/ol)[^>]*>)", "gi");
		while (regExpObj.test(outputHTML)) {
			outputHTML = outputHTML.replace(regExpObj,"$1</li>$3");
		}
		return outputHTML;
	}
	
	function expandMinimalisedAttributes(inputString) {
		var outputHTML = inputString;
		var regExpObj;
		regExpObj = new RegExp("(<[^>]*?\\s)(compact|checked|declare|readonly|disabled|selected|defer|ismap|nohref|noshade|nowrap|multiple|noresize)(\\s*(>|([^=\\s][^>]*>)))", "gi");
		while (regExpObj.test(outputHTML)) {
			outputHTML = outputHTML.replace(regExpObj,'$1$2="$2"$3');
		}
		return outputHTML;
	}
	
	function htmlEncodeAll(inputString) {
		var outputHTML = '';
		// All characters be replaced by their ascii equivalents - used to disguise email adresses and other sensitvei content from harvesting programs
		for (var i=0;i<inputString.length;i++) {
			outputHTML += '&#'+inputString.charCodeAt(i)+';'
		}
		return outputHTML;
	}
	
	function encodeEmailAddresses(inputString) {
		var outputHTML = inputString;
		var matches;
		var regExpObj;
		regExpObj = new RegExp('(<a\\s[^>]*?href\\s*?=\\s*?["\']?mailto:)(([^"\'\\s]+)?@([^"\'\\s]+)?)(["\']?[^>]*?>)([^<]*)?', 'gi');
		matches = regExpObj.exec(inputString);
		while (matches != null) {
			outputHTML = outputHTML.replace(matches[0],matches[1]+htmlEncodeAll(matches[2])+matches[5]+htmlEncodeAll(matches[6]));
			matches = regExpObj.exec(inputString);
		}
		return outputHTML;
	}
	
	function stringReplace(source,findStr,replaceStr) {
		var inStr;
		inStr = source;
		if (findStr != replaceStr) {
			while (inStr.indexOf(findStr)>=0) {
				inStr=inStr.replace(findStr,replaceStr)
			}
		}
		return inStr;
	}
	
	function correctEditorPaths(text,baseURL) {
		var outputStr=text;
		outputStr = stringReplace(outputStr,'href="'+baseURL+'/#','href="#');
		outputStr = stringReplace(outputStr,'href="'+baseURL+'/','href="/');
		outputStr = stringReplace(outputStr,'src="'+baseURL+'/#','src="#');
		outputStr = stringReplace(outputStr,'src="'+baseURL+'/','src="/');
		return outputStr;
	}

	function trim(inputString) {
		var outputString = inputString
		while ((outputString.length>0) && (outputString.substring(0,1)==' ')) {
			outputString = outputString.substring(1)
		}
		while ((outputString.length>0) && (outputString.substring(outputString.length-1,outputString.length)==' ')) {
			outputString = outputString.substring(0,outputString.length-1)
		}
		return outputString
	}
	
	function consolidateMultiSelectControl(fieldname,frm) {
		var selectCtrl,realCtrl,returnValue;
		selectCtrl = frm[fieldname +'_'];
		realCtrl = frm[fieldname];
		returnValue = '';
		for (var i=0;i<selectCtrl.options.length;i++) {
			if (selectCtrl.options[i].selected) {
				if (returnValue!='') {
					returnValue+=',';
				}
				returnValue+=selectCtrl.options[i].value=='' ? selectCtrl.options[i].text : selectCtrl.options[i].value;
			}
		}
		realCtrl.value=returnValue;
	}

	