function formValidator(){
	var forms = new Array();			
	var self = this;				
	
	this.addForm = addForm;
	
	function addForm(pFormName){
		if(checkExistance(pFormName)) {
			eval('this.' + pFormName + ' = new form(\'' + pFormName + '\');');
			return true;
		} else {
			alert('The form \"' + pFormName + '\" could not be found.');
			return false;
		}
	}
	function checkExistance(pFormName){
		if( document.forms[pFormName])
			return true;
		else 
			return false;
	}
	function getDOM(){
		var oDOM = document;		
		if(oDOM.all) oDOM = oDOM.all;
		else if(oDOM.getElementById) oDOM = oDOM.getElementById;		
		return(oDOM);
	}
}

function form(pFormName){
	var name = pFormName;
	var self = this;
	this.fields = new Array();
	this.fieldCount = 0;
	
	this.getName = getName;
	this.setName = setName;
	this.validate = validate;
	this.addField = addField;
	
	function getName(){return(name);}
	function setName(pName){name = pName;}
	function validate(){
		var isValid = true;
		for(var currentField in this.fields){

			if(!this.fields[currentField].validate()){
				isValid = false;
				alert(this.fields[currentField].getErrorMessage());
				if(getDOM()[this.fields[currentField].getName()].focus)
					getDOM()[this.fields[currentField].getName()].focus();
				if(getDOM()[this.fields[currentField].getName()].select)
					getDOM()[this.fields[currentField].getName()].select();

				break;
			}
		}		
		return(isValid);
	}
	function addField(pFieldName){
		if(checkExistance(pFieldName)) {
			this.fields[pFieldName] = new Field(pFieldName, name);
			fieldCount = countFields();
			return true;
		} else {
			alert('A field named \"' + pFieldName + '\" could not be found in the form: \"' + name + '\".');
			return false;
		}
	}
	function checkExistance(pFieldName){
		if( document.forms[name].elements[pFieldName] )
			return true;
		else 
			return false;
	}
	function getDOM(){
		var oDOM = document.forms[name].elements;		
		return(oDOM);
	}
	function countFields(){
		var cnt = 0;
		for(var i in self.fields) cnt++;
		return(cnt);
	}
}

function Field (sName, sForm) {
	//private properties
	var self = this;
	var name;
	var dataType = "string";
	var form;
	var displayName;
	var allowNull = new Boolean(0);
	var minLength = 0;
	var maxLength = 0;
	var minSelect = 0;
	var maxSelect = 0;
	var cardType = "visa";
	var errors = new Array();
		errors.code = 0;
		errors.message = "";

	this.init = function (sName, sForm) {
		//set name
		if(sName) {
			this.setName(sName);
			this.setDisplayName(sName);
		}
		//set form
		if(sForm)
			this.setForm(sForm);
	}
	this.validate = function () {
		//get form DOM handler
		var oDOMHandler = getDOMElement(this.getForm());
		
		if(oDOMHandler) {
			//get field's type
			var sElementType = getType(oDOMHandler);

			//get field's current value
			var sElementValue = trim(getValue(oDOMHandler));

			//validate allow null (required value)
			if(!this.getAllowNull() && isNull(sElementValue)) {
				this.setErrors(0, 'The field "'+this.getDisplayName()+'" is a required field.');
				return false;
			}
			
			if(!isNull(sElementValue)) {//validate data type
				var dataTypeCheck = isDataValid(sElementValue);
				if(!dataTypeCheck[0]) {
					this.setErrors(0, 'The field "'+this.getDisplayName()+'" expects a value of type: '+dataTypeCheck[1]+'.\n\nPlease be sure to enter a proper value.');
					return false;
				}
			}
			
			//validate min length
			if(sElementValue.length < this.getMinLength()) {
				this.setErrors(0, 'The field "'+this.getDisplayName()+'" must be at least '+this.getMinLength()+' character(s).');
				return false;
			}

			//validate max length
			if(this.getMaxLength() > 0 && sElementValue.length > this.getMaxLength()) {
				this.setErrors(0, 'The field "'+this.getDisplayName()+'" cannot be more than '+this.getMaxLength()+' character(s).');
				return false;
			}

			if(/^(radio|checkbox|select|select-multiple)$/.test(sElementType)) {
				//validate min select
				if(countSelections(oDOMHandler) < this.getMinSelect()) {
					this.setErrors(0, 'The field "'+this.getDisplayName()+'" requires at least '+this.getMinSelect()+' selection(s).');
					return false;
				}
	
				//validate max select
				if(this.getMaxSelect() > 0 && countSelections(oDOMHandler) > this.getMaxSelect()) {
					this.setErrors(0, 'The field "'+this.getDisplayName()+'" requires no more than '+this.getMaxSelect()+' selection(s) be made.');
					return false;
				}
			}
		} else {
			this.setErrors(0, 'Form cannot be found.');
			return false;
		}
		
		return true;
	}

	this.getName = function () {
		return name;
	}
	this.setName = function (sName) {
		var matchPattern = /^\w[^\s]*$/;
		if(matchPattern.test(sName))
			name = new String(sName);
		else
			return false;
	}
	//////////////////////////////
	this.getName = function () {
		return name;
	}
	this.setName = function (sName) {
		var matchPattern = /^\w[^\s]*$/;
		if(matchPattern.test(sName))
			name = new String(sName);
		else
			return false;
	}
	//////////////////////////////
	this.getDataType = function () {
		return dataType;
	}
	this.setDataType = function (sDataType) {
		var matchPattern = /^(string|int|number|money|date|email|creditcard|usphone|uszip)$/;
		sDataType = sDataType.toLowerCase();
		if(matchPattern.test(sDataType))
			dataType = new String(sDataType);
		else
			return false;
	}
	//////////////////////////////
	this.getCardType = function () {
		return cardType;
	}
	this.setCardType = function (sCardType) {
		var matchPattern = /^(mastercard|visa|amex)$/;
		sCardType = sCardType.toLowerCase();
		if(matchPattern.test(sCardType))
			cardType = new String(sCardType);
		else
			return false;
	}
	//////////////////////////////
	this.getForm = function () {
		return form;
	}
	this.setForm = function (sForm) {
		var matchPattern = /^\w[^\s]*$/;
		if(matchPattern.test(sForm))
			form = new String(sForm);
		else
			return false;
	}
	//////////////////////////////
	this.getDisplayName = function () {
		return displayName;
	}
	this.setDisplayName = function (sDisplayName) {
		if(typeof(sDisplayName) == 'string')
			displayName = new String(sDisplayName);
		else
			return false;
	}
	//////////////////////////////
	this.getAllowNull = function () {
			return allowNull.valueOf();
	}
	this.setAllowNull = function (bAllowNull) {
		if(bAllowNull)
			allowNull = new Boolean(1);
		else
			allowNull = new Boolean(0);
	}
	//////////////////////////////
	this.getMinLength = function () {
		return minLength;
	}
	this.setMinLength = function (iMinLength) {
		var matchPattern = /^[0-9]+$/;
		if(matchPattern.test(iMinLength))
			minLength = iMinLength;
		else
			return false;
	}
	//////////////////////////////
	this.getMaxLength = function () {
		return maxLength;
	}
	this.setMaxLength = function (iMaxLength) {
		var matchPattern = /^[0-9]+$/;
		if(matchPattern.test(iMaxLength))
			maxLength = iMaxLength;
		else
			return false;
	}
	//////////////////////////////
	this.getLengthRange = function () {
		return new Array(minLength, maxLength);
	}
	this.setLengthRange = function (iMinLength, iMaxLength) {
		var matchPattern = /^[0-9]+$/;
		if(matchPattern.test(iMinLength) && matchPattern.test(iMaxLength)) {
			minLength = iMinLength;
			maxLength = iMaxLength;
		} else {
			return false;
		}
	}
	//////////////////////////////
	this.getMinSelect = function () {
		return minSelect;
	}
	this.setMinSelect = function (iMinSelect) {
		var matchPattern = /^[0-9]+$/;
		if(matchPattern.test(iMinSelect))
			minSelect = iMinSelect;
		else
			return false;
	}
	//////////////////////////////
	this.getMaxSelect = function () {
		return maxSelect;
	}
	this.setMaxSelect = function (iMaxSelect) {
		var matchPattern = /^[0-9]+$/;
		if(matchPattern.test(iMaxSelect))
			maxSelect = iMaxSelect;
		else
			return false;
	}
	//////////////////////////////
	this.getSelectRange = function () {
		return new Array(minSelect, maxSelect);
	}
	this.setSelectRange = function (iMinSelect, iMaxSelect) {
		var matchPattern = /^[0-9]+$/;
		if(matchPattern.test(iMinSelect) && matchPattern.test(iMaxSelect)) {
			minSelect = iMinSelect;
			maxSelect = iMaxSelect;
		} else {
			return false;
		}
	}
	//////////////////////////////
	this.getErrors = function () {
		return errors;
	}
	this.getErrorMessage = function () {
		return errors.message;
	}
	this.setErrors = function (sCode, sMessage) {
		errors.code = sCode;
		errors.message = sMessage;
	}
	//////////////////////////////

	
	/***************** Getter/Setter Methods ****************/
	var getDOMElement = function (sElementName) {
		if( document.forms[sElementName] )
			return document.forms[sElementName];
		else 
			return false;
	}
	
	var getValue = function (oDOMHandler) {
		var sElementValue;
		var sElementName = self.getName();
		
		//If type is select, text, textarea, password, hidden, button
		if(oDOMHandler.elements[sElementName].type) {
			if(/^(select|select-multiple)$/i.test(oDOMHandler.elements[sElementName].type)) {
				//cycle through each option
				for(var i=0; i<oDOMHandler.elements[sElementName].options.length; i++) {
					//If this option is checked
					if(oDOMHandler.elements[sElementName].options[i].selected) {
						sElementValue = listAppend(sElementValue, oDOMHandler.elements[sElementName].options[i].value);
					}
				}
			} else {
				sElementValue = oDOMHandler.elements[sElementName].value;
			}
		} else if(oDOMHandler.elements[sElementName].length && oDOMHandler.elements[sElementName].length > 0) {
			//If type is checkbox or radio
			if(/^(checkbox|radio)$/i.test(oDOMHandler.elements[sElementName][0].type)) {
				//cycle through each option
				for(var i=0; i<oDOMHandler.elements[sElementName].length; i++) {
					//If this option is checked
					if(oDOMHandler.elements[sElementName][i].checked) {
						sElementValue = listAppend(sElementValue, oDOMHandler.elements[sElementName][i].value);
					}
				}
			}
		}

		if(!sElementValue) sElementValue = "";
		
		return sElementValue;
	}

	var getType = function (oDOMHandler) {
		var sElementName = self.getName();
		
		//If type is select, text, textarea, password, hidden, button
		if(oDOMHandler.elements[sElementName].type) {
			return oDOMHandler.elements[sElementName].type;
		} else if(oDOMHandler.elements[sElementName][0].type) {
			return oDOMHandler.elements[sElementName][0].type;
		} else {
			return false;
		}
	}

	var countSelections = function (oDOMHandler) {
		var sElementName = self.getName();
		var sSelectionCount = 0;
		
		//If type is select, text, textarea, password, hidden, button
		if(oDOMHandler.elements[sElementName].length) {
			for(var i=0; i<oDOMHandler.elements[sElementName].length; i++) {
				if(oDOMHandler.elements[sElementName][i].checked)
					sSelectionCount++;
			}
		}
		
		return sSelectionCount;
	}


	/***************** Test Methods ****************/
	var isNull = function (sValue) {
		if(sValue == null)
			return true;
		else if(trim(sValue) == "")
			return true;
		else
			return false;
	}
	//////////////////////////////
	var isEmail = function (sValue) {
		var matchPattern = /^([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
		if(matchPattern.test(sValue))
			return true;
		else
			return false;
	}
	//////////////////////////////
	var isCreditCard = function (sValue, sCardType) {
		var matchPattern = /\d+/;
		
		if(matchPattern.test(sValue)) {
			var lengthIsValid = false;
			var prefixIsValid = false;
			var prefixRegExp;
		
			switch(sCardType) {
			  case "mastercard":
				lengthIsValid = (sValue.length == 16);
				prefixRegExp = /^5[1-5]/;
				break;
		
			  case "visa":
				lengthIsValid = (sValue.length == 16 || sValue.length == 13);
				prefixRegExp = /^4/;
				break;
		
			  case "amex":
				lengthIsValid = (sValue.length == 15);
				prefixRegExp = /^3(4|7)/;
				break;
		
			  default:
				return false;
			}
			
			if(prefixRegExp.test(sValue) && lengthIsValid) {
				var numberProduct;
				var numberProductDigitIndex;
				var checkSumTotal = 0;
			
				for (digitCounter = sValue.length - 1; digitCounter >= 0; digitCounter--) {
				  checkSumTotal += parseInt (sValue.charAt(digitCounter));
				  digitCounter--;
				  numberProduct = String((sValue.charAt(digitCounter) * 2));
				  for (var productDigitCounter = 0;	productDigitCounter < numberProduct.length; productDigitCounter++) {
					checkSumTotal += 
					  parseInt(numberProduct.charAt(productDigitCounter));
				  }
				}
			
				return (checkSumTotal % 10 == 0);
			} else {
				return false;
			}
		} else {
			return false;
		}
	}
	//////////////////////////////
	var isInt = function (sValue) {
		var matchPattern = /^-?\d+$/;
		if(matchPattern.test(sValue))
			return true;
		else
			return false;
	}
	//////////////////////////////
	var isNumber = function (sValue) {
		var matchPattern = /^-?\d+\.?\d*$/;
		if(matchPattern.test(sValue))
			if(/\.$/.test(sValue))
				return false;
			else
				return true;
		else
			return false;
	}
	//////////////////////////////
	var isString = function (sValue) {
		if(typeof(sValue) == 'string')
			return true;
		else 
			return false;
	}
	//////////////////////////////
	var isMoney = function (sValue) {
		var matchPattern = /^((\$\-?\d*)|(\$\-?\d*\.\d{2})|(\-?\d*)|(\-?\d*\.\d{2}))$/;
		if(matchPattern.test(sValue))
			return true;
		else
			return false;
	}
	//////////////////////////////
	var isDate = function (sValue) {
		var matchPattern = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{4}$/;
		if(matchPattern.test(sValue))
			return true;
		else
			return false;
	}
	//////////////////////////////
	var isUSPhone = function (sValue) {
		var matchPattern = /(^\([1-9]\d{2}\)\s?\d{3}\-\d{4}$)|(^[1-9]\d{2}\-\d{3}\-\d{4}$)/;
		if(matchPattern.test(sValue))
			return true;
		else
			return false;
	}
	//////////////////////////////
	var isUSZip = function (sValue) {
		var matchPattern = /(^\d{5}$)|(^\d{5}-\d{4}$)/;
		if(matchPattern.test(sValue))
			return true;
		else
			return false;
	}
	//////////////////////////////
	var isDataValid = function (sValue) {
		var sDataType = self.getDataType();

		if(sDataType == "string") {
			return new Array(isString(sValue), "string");
		} else if(sDataType == "int") {
			return new Array(isInt(sValue), "integer");
		} else if(sDataType == "number") {
			return new Array(isNumber(sValue), "number");
		} else if(sDataType == "money") {
			return new Array(isMoney(sValue), "money");
		} else if(sDataType == "date") {
			return new Array(isDate(sValue), "date");
		} else if(sDataType == "email") {
			return new Array(isEmail(sValue), "email address");
		} else if(sDataType == "creditcard") {
			return new Array(isCreditCard(sValue, cardType), cardType + " credit card");
		} else if(sDataType == "usphone") {
			return new Array(isUSPhone(sValue), "US telephone");
		} else if(sDataType == "uszip") {
			return new Array(isUSZip(sValue), "US zip code");
		} else {
			return new Array(true,"");
		}
	}
	//////////////////////////////
	
	
	/***************** Format Methods ****************/
	var trim = function (sValue) {
		//validate input value
		if (typeof(sValue) != "string") { return sTrimmedString; }

		//trim whitespaces
		var sTrimmedString = sValue.replace(/^\s*/, "");	//left side
		sTrimmedString = sTrimmedString.replace(/\s*$/, "");	//right side
		
		return sTrimmedString;
	}
	
	var listAppend = function (sList, sValue, sDelimiter) {
		if(!sDelimiter)	sDelimiter = ",";

		if(sList) {
			var workingArray = sList.split(sDelimiter);
			workingArray.push(sValue);
			return workingArray.join(sDelimiter);
		} else {
			return sValue;
		}
	}
	
	this.init(sName, sForm);
}

function verifyForm(f){
	if(validator.WEB_SURVEY_FORM.validate()) return(true);
	else{
		return(false);
	}
}

