/*********************************************************************
JavaScript 1.2 Validation Script
	version 3.2.0
	by matthew frank

There are no warranties expressed or implied.  This script may be
re-used and distrubted freely provided this header remains intact
and all supporting files are included (unaltered) in the distribution:

		validation.js   - this file
		validation.htm  - example form
		readme.htm      - directions on using this script
		test.htm        - part of an automated test harness
		test.js         - part of an automated test harness

If you are interested in keeping up with the latest releases of this
script or asking questions about its implementation, think about joining
the Yahoo! Groups discussion forum dedicated to validation:

	http://groups.yahoo.com/group/validation

*********************************************************************/
Function.Null = function() {}
Function.create = function(value) {
	if(Object.hasValue(value)) {
		if(!value.instanceOf(Function))
			value = new Function(value);
	} else
		value = Function.Null;
	return value;
}
String.Empty = "";
String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g, String.Empty);
}
String.prototype.startsWith = function(prefix) {
	return new RegExp("^" + prefix).test(this);
}
String.prototype.endsWith = function(suffix) {
	return new RegExp(suffix + "$").test(this);
}
String.prototype.contains = function (substring$) {
	return this.indexOf(substring$) > -1;
}
String.prototype.containsIgnoreCase = function (substring$) {
	return this.toUpperCase().indexOf(substring$.toUpperCase()) > -1;
}
Object.isDefined = function(thing) {
	return typeof thing != "undefined";
}
Object.hasValue = function(thing) {
	return Object.isDefined(thing) && thing != null;
}
Object.extend = function(destination, source) {
	for (var property in source)
		destination[property] = source[property];
}
Object.prototype.instanceOf = function(conztructor) {
	return this.constructor == conztructor;
}
Object.prototype.$get = function(propertyName) {
	var returnValue;
	returnValue = this[propertyName];
	if(!Object.isDefined(returnValue) && this.getAttribute)
		returnValue=this.getAttribute(propertyName);

	if(Object.hasValue(returnValue) && returnValue.instanceOf(String) && returnValue.startsWith("@")) {
		this.$evaluate$ = Function.create("return (" + returnValue.replace(/^@/, String.Empty) + ")");
		returnValue = this.$evaluate$();
		this.$evaluate$ = null;
	}
	return returnValue;
}
Object.prototype.$set = function(propertyName, value) {
	if(this.setAttribute)
		this.setAttribute(propertyName, value);
	else
		this[propertyName] = value;
}
Array.create = function (/* list */) {
	var result = new Array();
	var each;
	for (var i = 0; i < arguments.length; i++) {
		each = arguments[i];
		if (each && each.instanceOf(Array))
			result = result.concat(each);
		else
			result.push(each);
	}
	return result;
}
Array.prototype.compact = function () {
	return this.filter(Object.hasValue);
}
Array.prototype.elementsEqual = function (otherArray) {
	if (otherArray && otherArray.instanceOf(Array)) {
		if (this.length == otherArray.length) {
			for(var i = 0; i < this.length; i++) {
				//doesn't handle embedded arrays
				if (this[i] != otherArray[i])
					return false;
			}
			return true;
		}
	}
	return false;
}
Array.prototype.forEach = function (callback) {
	for (var i = 0; i < this.length; i++)
		callback(this[i]);
}
Array.prototype.map = function (callback) {
	var result = new Array();
	for (var i = 0; i < this.length; i++)
		result.push(callback(this[i]));
	return result;
}
Array.prototype.filter = function (callback) {
	var result = new Array();
	for (var i = 0; i < this.length; i++)
		if (callback(this[i]))
			result.push(this[i]);
	return result;
}
Array.prototype.choose = function () {
	for (var i = 0; i < this.length; i++)
		if (Object.hasValue(this[i])) return this[i];
	return null;
}
Array.prototype.contains = function (item) {
	for (var i = 0; i < this.length; i++)
		if (this[i] == item) return true;
	return false;
}
function $(/* list of ids */) {
	var list = new Array();
	for (var i=0; i < arguments.length; i++)
		list.push(document.getElementById(arguments[i]));
	if (arguments.length == 1) 
		return list[0];
	return list;
}
(function /* fixInternetExplorer */ () {
	if (!window.$get) {
		var everything = document.getElementsByTagName("*");
		for (var i=0; i < everything.length; i++)
			Object.extend(everything[i], Object.prototype);
		window.$get = Object.prototype.$get;
		window.$set = Object.prototype.$set;
		document.$get = Object.prototype.$get;
		document.$set = Object.prototype.$set;
	}
})();

/******************************************/

function Validation(){
	this.markRequiredInterval = 100;
	this.summaryIntroduction = "Please correct the following errors:";
	this.showPopup = true;
	this.showSummary = true;
	this.invalidColor = String.Empty;

	var validationFunctions = new Array();
	var isInitialFocusSet = false;
	
	this.Err = function(){
		this.raise = function(element, message, stem){
			if(typeof event!="object") event = new Object();
			if(event.type!="change" || event.type=="change" && !suppressOnchange(element)){
	 			var displayName = element.$get("DISPLAY-NAME");
				message = [
					(stem ? element.$get(stem.toUpperCase()+"-MESSAGE") : null),
					element.$get("MESSAGE"),
					message].choose();
				var extendedMessage = message.replace(/\.$/, String.Empty)
					+ (!!displayName ? " in the " + displayName + " field." : ".");
				this.registerMessage(element, extendedMessage);
			}
			Validation.markInvalid(element, message);
		}
		this.displayMessages = function (form) {
			if (Validation.showPopup)
				this.displayPopup(form);
			if (Validation.showSummary)
				this.renderSummary(form);
		}
		this.displayPopup = function (form) {
			if (!hasMessages(form)) return;
			var message = Validation.summaryIntroduction;
			var map = getIndexMap(form);
			var fieldMessages = getMessageMap(form);
			for(var i = 0; i < map.length; i++)
				message += "\n  - " + fieldMessages[map[i]];
			window.alert(message);
		}
		this.renderSummary = function (form) {
			var summary = Validation.Err.getValidationSummary(form);
			if (!summary) return;
			var elementId, list, listItem, link;
			var map = getIndexMap(form);
			var fieldMessages;
			if (hasMessages(form)) {
				fieldMessages = getMessageMap(form);
				summary.innerHTML = String.Empty;
				summary.appendChild(document.createTextNode(Validation.summaryIntroduction));
				list = document.createElement("UL");
				summary.appendChild(list);
				for(var i = 0; i < map.length; i++) {
					elementId = map[i];
					listItem = document.createElement("LI");
					link = document.createElement("A");
					link.setAttribute("href", "javascript:$('"+elementId+"').validateFocus();");
					link.appendChild(document.createTextNode(fieldMessages[elementId]));
					listItem.appendChild(link);
					list.appendChild(listItem);
				}
				summary.style.display = String.Empty;
				//TODO scroll to summary
			}
			else 
				summary.style.display = "none";
		}
		var hasMessages = function (form) {
			return getIndexMap(form).length > 0;
		}
		var messageMap = new Array();
		var indexMap = new Array();
		var getMessageMap = function (form) {
			var id = Validation.Err.getElementIdentity(form);
			if (!messageMap[id])
				messageMap[id] = new Array();
			return messageMap[id];
		}
		var getIndexMap = function (form) {
			var id = Validation.Err.getElementIdentity(form);
			if (!indexMap[id])
				indexMap[id] = new Array();
			return indexMap[id]
		}
		this.registerMessage = function (element, message) {
			var id = Validation.Err.getElementIdentity(element);
			getMessageMap(element.form)[id] = message;
			getIndexMap(element.form).push(id);
		}
		this.getValidationSummary = function (form) {
			var divs = form.getElementsByTagName("DIV");
			for (var i = 0; i < divs.length; i++) {
				if (divs[i].$get("className").match(/\bvalidation-summary\b/i))
					return divs[i];
			}
			return null;
		}
		this.unregisterMessage = function (element) {
			var elementId = this.getElementIdentity(element);
			delete getMessageMap(element.form)[elementId];
			delete getIndexMap(element.form)[elementId];
			indexMap = indexMap.compact();
		}
		var identityCounter = 0;
		this.getElementIdentity = function (element) {
			if (!element.id)
				element.id = "validation" + identityCounter++;
			return element.id;
		}
		this.clearMessages = function (form) {
			var id = this.getElementIdentity(form);
			messageMap[id] = new Array();
			indexMap[id] = new Array();
		}
	}
	var propertyOn = function(/* attribute list */){
		var isOn, attribute, length = arguments.length;
		for(var i=0;i<length;i++){
			attribute = arguments[i];
			if(typeof attribute=="string")
				attribute = attribute.toLowerCase();
			isOn = Object.hasValue(attribute) &&
				   attribute!="false" &&
				   attribute!="off" &&
				   attribute!="no" &&
			       attribute!=false &&
			       attribute!=String.Empty;
			if(isOn) break;
		}
		return !!isOn;
	}
	var pad = function(value, width){
		width = width || 2;
		var returnValue=value.toString();
		for(var i=width-returnValue.length;i>0;i--)
			returnValue="0"+returnValue;
		return returnValue;
	}
	var minMaxRange = function(min,max){
		if (propertyOn(min)) min = String.Empty + min;
		if (propertyOn(max)) max = String.Empty + max;
		if (!!min && !!max) return " between " + min + " and " + max;
		else if (!!min) return " greater than or equal to " + min;
		else if (!!max) return " less than or equal to " + max;
		else return String.Empty;
	}
	var dateOrTime = function(format){
		var date = false, time = false;
		date = format.search(/mm?/i)>-1 || format.search(/dd?/i)>-1 || format.search(/yyyy/i)>-1;
		time = format.search(/hh?/i)>-1 || format.search(/nn/i)>-1 || format.search(/ss/i)>-1 || format.search(/ap/i)>-1;
		return (date?"date":String.Empty)+(time?"time":String.Empty);
	}
	var toDateString = function(date, format){
		var i, regex, index=new Array;
		var day, month, year, hour, minute, second, ampm;
		// Determine order of datetime tokens
		with(format){
			index[search(/dd?/i)]="day";
			index[search(/mm?/i)]="month";
			index[search(/yyyy/i)]="year";
			index[search(/hh?/i)]="hour";
			index[search(/nn/i)]="minute";
			index[search(/ss/i)]="second";
			index[search(/ap/i)]="ampm";

			// timing of replaces is quite important!
			regex=format.replace(/(\$|\^|\*|\(|\)|\+|\.|\?|\\|\{|\}|\||\[|\])/g,"\\$1");
			// only allow one pass for day and month
			if(search(/dd/i)>-1)
				regex=regex.replace(/dd/i,"(0[1-9]|[1-2]\\d|3[0-1])");
			else
				regex=regex.replace(/d/i,"(0?[1-9]|[1-2]\\d|3[0-1])");
			if(search(/mm/i)>-1)
				regex=regex.replace(/mm/i,"(0[1-9]|1[0-2])");
			else
				regex=regex.replace(/m/i,"(0?[1-9]|1[0-2])");
			regex=regex.replace(/nn/i,"([0-5]\\d)")
				.replace(/ss/i,"([0-5]\\d)")
				.replace(/yyyy/i,"(\\d{4})")
				.replace(/\s+/g,"\\s*");
			if(search(/hh24/i)>-1)
				regex=regex.replace(/hh24/i,"([0-1]\\d|2[0-3])");
			else if(search(/h24/i)>-1)
				regex=regex.replace(/h24/i,"([0-1]?\\d|2[0-3])");
			else if(search(/hh/i)>-1)
				regex=regex.replace(/hh/i,"(0\\d|1[0-2])").replace(/ap/i,"([ap]m?)");
			else
				regex=regex.replace(/h/i,"(0?\\d|1[0-2])").replace(/ap/i,"([ap]m?)");
		}
		if(!new RegExp("^"+regex+"$","i").test(date))
			return;
		year=month=day=hour=minute=second=0,ampm=String.Empty;
		for(var key=0,i=0;key<index.length;key++)
			if(index[key]) eval(index[key]+"=RegExp.$"+(++i));
		if(hour<12&&/^pm?$/i.test(ampm))
			hour = parseInt(hour)+12;
		else if(hour==12&&/^am?$/i.test(ampm))
			hour=0;
		if(year==0) year=1;
		if(month==0) month=1;
		if(day==0) day=1;
		if(month==2 && day>((year%4==0&&year%100!=0||year%400==0)?29:28) || day>((month-1)%7+1)%2+30)
			return;
		return String.Empty+pad(year,4)+pad(month)+pad(day)+pad(hour)+pad(minute)+pad(second);
	}
	var formatNumber = function(i){
		if (!Object.hasValue(i))
			return null;
		var end = (/\./.test(i = i.toString()))?"\\.":"$";
		var re = new RegExp("(\\d)(\\d{3})(,|" + end + ")");
		if (re.test(i))
			i = formatNumber(i.replace(re, "$1,$2$3"));
		return i;
	}
	var suppressOnchange = function(element){
		return propertyOn(element.$get("SUPPRESS-ONCHANGE-MESSAGE"), element.form.$get("SUPPRESS-ONCHANGE-MESSAGE"))
	}
	var getValueOf = function(element){
		var returnValue=null;
		switch (element.type){
			case "text" : case "textarea" : case "file" : case "password" : case "hidden" :
				returnValue=element.value;
				break;
			case "select-one" :
				if(element.selectedIndex>=0)
					returnValue=element.options[element.selectedIndex].value;
				break;
			case "select-multiple" :
				for(var i=0,iOptions=element.options.length; i<iOptions; i++)
					if(element.options[i].selected && element.options[i].value.toString().trim()){
						returnValue=true;
						break;
					}
				break;
			case "radio" : case "checkbox" :
				returnValue=element.checked;
				break;
			default:
				returnValue = null;
		}
		return returnValue;
	}
	this.markInvalid = function(element){
		if(element.style){
			var backgroundColor = element.$get("INVALID-COLOR") || element.form.$get("INVALID-COLOR");
			if (!!backgroundColor){
				element.$set("OLD-BG-COLOR", element.style.backgroundColor);
				element.style.backgroundColor = backgroundColor;
			}else{
				element.$set("OLD-CLASS-NAME", element.className);
				element.className = (element.className+" invalid").trim();
			}
		}
	}
	var restoreForm = function(form, bReset){
		var elements = form.elements;
		var iElements = elements.length;
		form.isValid = true;
		Validation.Err.clearMessages(form);
		for(var i=0;i<iElements;i++){
			restoreElement(elements[i]);
			if(bReset)elements[i].onreset();
		}
	}
	var restoreElement = function(element){
		element.__validated = null;
		element.isValid = true;
		Validation.Err.unregisterMessage(element);
		if(!element.style) return;
		var backgroundColor = element.$get("OLD-BG-COLOR");
		if (Object.hasValue(backgroundColor)) {
			// Revert to previous background color
			element.style.backgroundColor = backgroundColor;
			element.$set("OLD-BG-COLOR", null);
		}else{
			var oldClass = element.$get("OLD-CLASS-NAME");
			if (Object.hasValue(oldClass)){
				// Revert to previous class
				element.className=oldClass;
				element.$set("OLD-CLASS-NAME",null);
			}
		}
	}
	var isValidForm = function(form,event){
		var i,iElements,orderBy,position;
		var element,elementList = form.elements;
		restoreForm(form);
		if(form.onbeforevalidate()==false)
			return false;
		orderBy = form.$get("ORDERED-VALIDATION");
		if(propertyOn(orderBy)){
			orderBy = /^tabindex$/i.test(orderBy)?"tabIndex":"VALIDATION-ORDER";
			elementList = new Array();
			for(i=0,iElements=form.elements.length;i<iElements;i++){
				element=form.elements[i];
				position = parseInt(element.$get(orderBy));
				if(propertyOn(position) && !isNaN(position))
					elementList=elementList.slice(0,position).concat(element,elementList.slice(position));
				else
					elementList[elementList.length]=element;
			}
		}
		for(i=0,iElements=elementList.length;i<iElements;i++)
			if (!elementList[i].validate(event)) form.isValid = false;
		if(form.onaftervalidate()==false)
			return false;
		return form.isValid;
	}
	var isRequired = function(element){
		return propertyOn(element.$get("REQUIRED"));
	}
	var isFloat = function(value, signed){
		return new RegExp("^"+(propertyOn(signed)?"-?":String.Empty)+"(\\d*(,?\\d{3})*\\.?\\d+|\\d+(,?\\d{3})*\\.?\\d*)$").test(value);
	}
	var isInteger = function(value, signed){
		return new RegExp("^"+(propertyOn(signed)?"-?":String.Empty)+"(\\d{1,3})(,?\\d{3})*$").test(value);
	}
	var isCurrency = function(value, signed){
		var reMain = "((\\d{1,3})(,?\\d{3})*(\\.\\d{2})?|((\\d{1,3})(,?\\d{3})*)?\\.\\d{2})";
		return new RegExp("^("+
			              "("+ (propertyOn(signed)?"(\\$?\\-?|\\-?\\$?)":"\\$?")+reMain +")"+
			              (propertyOn(signed)?"|(\\(\\$?"+reMain+"\\))":String.Empty)
			              +")$").test(value)	
	}
	var isValidElement = function(element, event){
		// Do not validate label or fieldset elements
		if(!Object.isDefined(element.type)||element.__validated||propertyOn(element.$get("disabled"))||propertyOn(element.$get("readOnly")))
			return true;
		element.__validated = true;
		if(element.onbeforevalidate()==false)
			return false;
		var iLength,vAnd,or,oRegexp,iMin,iMax,format,sMask,date,minDate,maxDate,bFirst;
		iMin = element.$get("MIN");
		iMax = element.$get("MAX");
		var pass=true;
		if(element.value && element.type != "file")
			element.value = element.value.trim();
		var sValue = getValueOf(element);
		var bSigned = element.$get("SIGNED");
		
		// REQUIRED
		if(isRequired(element) && !sValue){
			Validation.Err.raise(element, "Please enter a value", "REQUIRED",event);
	 		return false;
		}
		// FLOAT, NUMBER
		if(((bFirst=propertyOn(element.$get("FLOAT")))||propertyOn(element.$get("NUMBER"))) && sValue){
			if(!isFloat(sValue, bSigned))
				pass=false;
			else if(iMin==parseFloat(iMin) && parseFloat(sValue.replace(/,/g, String.Empty)) < parseFloat(iMin))
				pass=false;
			else if(iMax==parseFloat(iMax) && parseFloat(sValue.replace(/,/g, String.Empty)) > parseFloat(iMax))
				pass=false;
			if(!pass){
				Validation.Err.raise(element, "Please enter a "+(bSigned?String.Empty:"positive ")+"number"+minMaxRange(formatNumber(iMin),formatNumber(iMax)),bFirst?"FLOAT":"NUMBER",event);
				return false;
			}
		}
		// AMOUNT, CURRENCY
		else if (((bFirst=propertyOn(element.$get("AMOUNT")))||propertyOn(element.$get("CURRENCY"))) && sValue){
			if(!isCurrency(sValue, bSigned))
				pass=false;
			else if(iMin==parseFloat(iMin) && parseFloat(sValue.replace(/[\$,]/g,String.Empty).replace(/^\(\$(.*)\)$/,"-$1"))<parseFloat(iMin))
				pass=false;
			else if(iMax==parseFloat(iMax) && parseFloat(sValue.replace(/[\$,]/g,String.Empty).replace(/^\(\$(.*)\)$/,"-$1"))>parseFloat(iMax))
				pass=false;
			if(!pass){
				Validation.Err.raise(element, "Please enter a "+(bSigned?String.Empty:"positive ")+"dollar amount"+minMaxRange(formatNumber(iMin),formatNumber(iMax)),bFirst?"AMOUNT":"CURRENCY",event);
				return false;
			}
		}
		// INTEGER
		else if (propertyOn(element.$get("INTEGER")) && sValue){
			if (!isInteger(sValue, bSigned))
				pass=false;
			else if(iMin==parseInt(iMin) && parseInt(sValue.replace(/,/g,String.Empty))<parseInt(iMin))
				pass=false;
			else if(iMax==parseInt(iMax) && parseInt(sValue.replace(/,/g,String.Empty))>parseInt(iMax))
				pass=false;
			if(!pass){
				Validation.Err.raise(element, "Please enter a "+(bSigned?"n ":"positive ")+"integer"+minMaxRange(formatNumber(iMin),formatNumber(iMax)),"INTEGER",event);
				return false;
			}
		}
		// DATE, DATETIME
		else if(((bFirst=propertyOn((format=element.$get("DATE"))))||propertyOn((format=element.$get("DATETIME"))))&&sValue){
			// Set default date format
			if(format==String.Empty||typeof format!="string") format="M/D/YYYY";
			minDate = toDateString(iMin, format);
			maxDate = toDateString(iMax, format);
			date = toDateString(sValue, format);
			if(!Object.isDefined(date))
				pass = false;
			else if(propertyOn(iMin) && Object.isDefined(minDate) && date < minDate)
				pass = false;
			else if(propertyOn(iMax) && Object.isDefined(maxDate) && date > maxDate)
				pass = false;
			if(!pass){
				Validation.Err.raise(element,"Please enter a "+dateOrTime(format)+" value"+minMaxRange(iMin,iMax)+" in the proper format:\n\t"+format.replace(/ap/i,"AM/PM").toUpperCase(),bFirst?"DATE":"DATETIME",event);
				return false;
			}
		}
		// PHONE
		else if(propertyOn(element.$get("PHONE"))&&sValue){
			var sPhone=sValue.replace(/\D/g,String.Empty);
			var iDigits=sPhone.length;
			if(!(iDigits==10||iDigits==11&&/^1/.test(sPhone))){
				Validation.Err.raise(element,"Please enter a valid phone number","PHONE",event);
				return false;
			}
		}
		// EMAIL
		else if(propertyOn(element.$get("EMAIL"))&&sValue){
			if(!/^[\w_-]+(\.[\w_-]+)*@[\w_-]+(\.[\w_-]+)*\.[a-z]{2,4}$/i.test(sValue)){
				Validation.Err.raise(element,"Please enter a valid email address", "EMAIL", event);
				return false;
			}
		}
		// ZIP Code
		else if (propertyOn(element.$get("ZIP")) && sValue){
			if (!/^\d{5}(-?\d{4})?$/.test(sValue)){
				Validation.Err.raise(element, "Please enter a valid ZIP code", "ZIP", event);
				return false;
			}
		}
		// REGEXP
		if (propertyOn(oRegexp=element.$get("REGEXP")) && sValue){
			if (!oRegexp.instanceOf(RegExp))
				oRegexp = new RegExp(oRegexp, "i");
			if (!oRegexp.test(sValue)){
				Validation.Err.raise(element, "Please enter a valid value", "REGEXP", event);
				return false;
			}
		}
		// MAXLENGTH
		if (sValue&&(iLength=element.$get("maxLength"))&&!/\D/.test(iLength)&&sValue.length>iLength){
			Validation.Err.raise(element,"Please enter a value having no more than " + formatNumber(iLength) + " characters", "MAXLENGTH",event);
			return false;
		}
		for(var i=0;validationFunctions[i];i++){
			if(validationFunctions[i](element, sValue)==false)
				return false;
		}
		// AND
		if (propertyOn(vAnd = element.$get("AND")) && !sValue){
			if(typeof vAnd == "string") vAnd = vAnd.toString().split(/,/);
			for(var oNewElement,i=0,iFields=vAnd.length; i<iFields; i++){
				if((oNewElement=(typeof vAnd[i].form=="object")?vAnd[i]:element.form.elements[vAnd[i].trim()])){
					if(!!getValueOf(oNewElement)){
						Validation.Err.raise(element, "Please enter a value", "AND",event);
						return false;
					}
				}
			}
		}
		// OR
		if ((or = element.$get("OR")) && !sValue){
			var fields,message;
			if(or.constructor==Array||or.constructor==String)
				fields = or;
			else
				fields = or["fields"];
			if(!!fields){
				if(fields.constructor!=Array)
					fields=fields.toString().split(/,/);
				for(var oNewElement,i=0,iFields=fields.length,bValue; !bValue&&i<iFields; i++){
					oNewElement=(fields[i].form)?fields[i]:element.form.elements[fields[i].trim()];
					if(oNewElement) bValue = !!getValueOf(oNewElement);
				}
				if(!bValue){
					Validation.Err.raise(element, message ? message : "Please enter a value", "OR", event);
					return false;
				}
			}
		}
		if(element.onvalidate()==false)
			return false;
		if(element.onaftervalidate()==false)
			return false;

		return true;
	}

	this.add = function(code) {
		if(code.instanceOf(Function)) {
			//evaluate the function in private scope to Validation; enables use of private functions
			eval("code="+code.toString());
			validationFunctions.push(code);
		}
	}
	this.setup = function(){
		var initialFocus;
		for(var i=0,oForm; i < document.forms.length; i++){
			oForm=document.forms[i];
			if(!oForm._setup){
				oForm.isValid = true;
				oForm._onsubmit_ = Function.create(oForm.$get("onsubmit"));
				oForm._onreset_ = Function.create(oForm.$get("onreset"));
				oForm.validate = function(oEvent){
					this.isValid = isValidForm(this, oEvent);
					Validation.Err.displayMessages(this);
					return this.isValid;
				}
				oForm.onbeforevalidate = Function.create(oForm.$get("onbeforevalidate"));
				oForm.onaftervalidate = Function.create(oForm.$get("onaftervalidate"));
				oForm.onautosubmit = Function.create(oForm.$get("onautosubmit"));
				oForm.onsubmit = function(oEvent){ //NN passed event
					if(!this.validate(oEvent || window.event)) return false;
					if (this._onsubmit_ && this._onsubmit_(oEvent)==false)
						return false;
					return true;
				}
				oForm.onreset=function(oEvent){
					restoreForm(this, true);
					Validation.Err.renderSummary(this);
					if (this._onreset_ && this._onreset_(oEvent)==false)
						return false;
				}
				oForm.markRequired=function(){
					var oldClassName, element;
					for(var k=0;(element=this.elements[k]);k++){
						oldClassName = element.$get("OLD-CLASS-NAME");
						if(propertyOn(element.$get("REQUIRED"))){
							if(!/\brequired\b/i.test(element.className)){
								if(propertyOn(oldClassName))
									element.$set("OLD-CLASS-NAME", (oldClassName+" required").trim());
								else
									element.className = (element.className+" required").trim();
							}
						}else{
							if(propertyOn(oldClassName))
								element.$set("OLD-CLASS-NAME", oldClassName.replace(/\brequired\b/gi, String.Empty).trim());
							else if(element.className)
								element.className = element.className.replace(/\brequired\b/gi, String.Empty).trim();
						}
					}
				}
				oForm._setup = true;
			}
			for(var j=0,element; j < oForm.elements.length; j++){
				element = oForm.elements[j];
				if(!element._setup){
					if(!isInitialFocusSet && propertyOn((initialFocus=element.$get("INITIAL-FOCUS")))){
						element.focus();
						if(/^select$/i.test(initialFocus)) element.select();
						isInitialFocusSet = true;
					}
					element._onkeypress_ = Function.create(element.$get("onkeypress"));
					element._onchange_ = Function.create(element.$get("onchange"));
					element._onpropertychange_ = Function.create(element.$get("onpropertychange"));
					element.validate = function(oEvent){
						this.isValid = isValidElement(this,oEvent);
						return this.isValid;
					}
					element.validateFocus = function() {
						Function.create(element.$get("onvalidatefocus"))();
						if (this.focus) this.focus();
						if (this.select) this.select();
					}
					element.isValid = true;
					element.onbeforevalidate = Function.create(element.$get("onbeforevalidate"));
					element.onvalidate = Function.create(element.$get("onvalidate"));
					element.onaftervalidate = Function.create(element.$get("onaftervalidate"));
					element.onautosubmit = Function.create(element.$get("onautosubmit"));
					element.onreset = Function.create(element.$get("onreset"));
					if(Object.isDefined(element.type)){
						element.onkeypress = function(oEvent){ //NN passes event object
var keyEnter = 13, keyNewLine = 10, keyTab = 9, keyBackspace = 8, keyNull = 0, keyDelete = 46, keyEscape = 27;
							if(this._onkeypress_ && this._onkeypress_(oEvent)==false) return false;
							var filter = this.$get("FILTER");
							if (propertyOn(filter)){
								if (!filter.instanceOf(RegExp))
									filter = new RegExp(filter);
								oEvent = oEvent || window.event;
								var keyCode = oEvent.which || oEvent.keyCode;
								if(![keyNull, keyTab, keyEnter, keyNewLine, keyBackspace, keyDelete, keyEscape].contains(keyCode)
										&& !filter.test(String.fromCharCode(keyCode)))
									return false;
							}
							return true;
						}
					}
					if(element.type!="radio" && element.type!="checkbox"){
						element.onchange = function(oEvent){
							oEvent = oEvent || window.event;
							restoreElement(this);
							if(propertyOn(this.$get("VALIDATE-ONCHANGE"), this.form.$get("VALIDATE-ONCHANGE")))
								if(!this.validate(oEvent) && !suppressOnchange(this))
									return false;
							if(this._onchange_ && this._onchange_()==false)
								return false;
							var autoSubmit = this.$get("AUTO-SUBMIT");
							if(propertyOn(autoSubmit) && this.onautosubmit()!=false && this.form.onautosubmit()!=false)
								this.form.submit();
						}
					}
					element._setup = true;
				}
			}
		}
	}
	this.Err=new this.Err();
	this.setup();
	window.setInterval(function() {
		for(var i = 0; i < document.forms.length; i++)
			document.forms[i].markRequired();
	}, this.markRequiredInterval);
}
(function /* limitToValidEnvironments */ () {
if(!!window.RegExp 
	&& !!String.Empty.replace 
	&& "ab".replace(/a/, String.Empty)=="b" 
	&& !!document.forms 
	&& !( (navigator.appVersion.indexOf("Mac")!=-1) && (navigator.appVersion.indexOf("MSIE")!=-1) )
	&& !!window.$get
  )
	Validation = new Validation();
})();