var FormValidator = {
	validate : function(form) {
		Log.clear();
		Log.setFormName(form.name);
		Log.setPageName(document.title);
		var finalResult = true;
		var i = 0;
		
		if (FormValidator.masterErrorMessenger === null && form.getAttribute("fv_error_label") !== null) {
			FormValidator.masterErrorMessenger = new HiddenBlock_ErrorMessenger(document.getElementById(form.getAttribute("fv_error_label")));
		}

		// Initialize all fields if not already done
		if (FormValidator.fields.length === 0) {
			var formFields = form.elements;
			
			for (i=0; i<formFields.length; i++) {
				var newFormField = FormFieldFactory.getFormField(formFields[i]);
				
				if (newFormField !== null) {
					FormValidator.fields[FormValidator.fields.length] = newFormField;
				}
			}
		}

		// Undisplay all errorss
		for (i=0; i<FormValidator.fields.length; i++) {
			FormValidator.fields[i].undisplayError();
		}		

		var isFocused = false;
		var errorMessage = "";

		// Check to make sure required fields are filled
		for (i=0; i<FormValidator.fields.length; i++) {
			if (!FormValidator.fields[i].verifyRequired()) {
				finalResult = false;
				
				if (!isFocused) {
					FormValidator.fields[i].focus();
					isFocused = true;
					
					errorMessage = "";
					
					if (FormValidator.masterErrorMessenger !== null) {
						if (form.getAttribute("fv_gen_error_msg") !== null) {
							FormValidator.masterErrorMessenger.setMessage(form.getAttribute("fv_gen_error_msg"));
						}
						
						FormValidator.masterErrorMessenger.displayError();
					}
				}

				Log.info("Displaying Error due to Required Field - " + FormValidator.fields[i].element.name);
				Log.omnitureError("Required Field - " + FormValidator.fields[i].element.name);
				FormValidator.fields[i].displayError();
			}
		}
		
		// Validate all fields
		for (i=0; i<FormValidator.fields.length; i++) {
			if (!FormValidator.fields[i].validate()) {
				finalResult = false;
				
				if (!isFocused) {
					FormValidator.fields[i].focus();
					isFocused = true;
					
					errorMessage = "";
					
					if (FormValidator.masterErrorMessenger !== null) {
						if (FormValidator.fields[i].getErrorMessage() !== null) {
							FormValidator.masterErrorMessenger.setMessage(FormValidator.fields[i].getErrorMessage());
						}
						else if (form.getAttribute("fv_gen_error_msg") !== null) {
							FormValidator.masterErrorMessenger.setMessage(form.getAttribute("fv_gen_error_msg"));
						}

						FormValidator.masterErrorMessenger.displayError();
					}
				}
				
				Log.info("Displaying Error due to Invalid Field - " + FormValidator.fields[i].element.name);
				Log.omnitureError("Invalid Field - " + FormValidator.fields[i].element.name);
				FormValidator.fields[i].displayError();
			}
		}

		// If all required fields are filled and have all been validated
		if (finalResult) {
			FormValidator.masterErrorMessenger.undisplayError();
			s.sendFormEvent('s',Log.pageName,Log.formName);
		}
		
		return finalResult;
	},
	
	fields : new Array(),
	masterErrorMessenger : null
};

var FormFieldFactory = {
	getFormField : function(field) {
		var fieldType = "";
		
		if (field.getAttribute("fv_validator") !== null) {
			fieldType = field.getAttribute("fv_validator");
		}
		
		var formField = null;
		
		switch (fieldType) {
			case "email":
				formField = new Email_FormField();
				
				break;
				
			case "zip":
			case "postal":
				formField = new Zip_FormField();
				
				break;
				
			case "phone":
				formField = new Phone_FormField();
				
				break;
						
			default:
				formField = new FormField();

				break;
		}
		
		if (formField !== null) {
			formField.setElement(field);
		}
		
		return formField;
	}
};

// FormField object
FormField = function() {
	this.init();
};

FormField.prototype.init = function() {
	this.element = null;
	this.isRequired = false;
	this.errorMessengers = new Array();	
};

FormField.prototype.verifyRequired = function() {
	Log.info("Checking Requirement (" + this.element.name + ")");
	
	if (this.element === null) {
		Log.error("No element to check");
	
		return true;
	}
	
	if (this.isRequired && this.element.value.trim() === "") {
		Log.info("Check Invalid: Required Field not filled out");
		Log.omnitureError(this.element.name + ": required field not filled out");
		return false;
	}
	
	Log.info("Check Valid");
	
	return true;
};

FormField.prototype.validate = function() {
	return true; // Nothing to validate
};

FormField.prototype.setElement = function(element) {
	if (element === null) {
		return;
	}

	this.element = element;

	this.setIsRequired(element.getAttribute("fv_is_required"));
	this.setErrorMessengers(element.getAttribute("fv_on_error"));
};

FormField.prototype.setIsRequired = function(isRequired) {
	if (isRequired !== null) {
		isRequired = isRequired.trim();
	
		if (isRequired === "true" || isRequired === "yes" || isRequired === "1") {
			this.isRequired = true;
		}
	}
};

FormField.prototype.addErrorMessenger = function(errorMessenger) {
	var paramName = "";
	var paramValue = "";
	var param = errorMessenger.split(":");
	
	paramName = param[0];
	if (param.length > 1) {
		paramValue = param[1];
	}
	
	Log.info("Adding Error Messenger - " + this.element.name + " (" + paramName + ", " + paramValue + ")");

	if (errorMessenger !== null) {
		switch (paramName.toLowerCase()) {
			case "labeltoerror":
				this.errorMessengers[this.errorMessengers.length] = new Label_ErrorMessenger(document.getElementById(paramValue));
				
				break;
				
			case "inputclassname":
				this.errorMessengers[this.errorMessengers.length] = new Field_ErrorMessenger(this.element, paramValue);
				
				break;
				
			case "inputbackgroundcolor":
				this.errorMessengers[this.errorMessengers.length] = new BackgroundColor_ErrorMessenger(this.element, paramValue);
				
				break;
		
			default:
				break;
		}
	}
};

FormField.prototype.setErrorMessengers = function(errorMessengers) {
	if (errorMessengers !== null) {
		var errorMessengersArray = errorMessengers.split(";");
		
		for (var i=0; i<errorMessengersArray.length; i++) {
			this.addErrorMessenger(errorMessengersArray[i]);
		}
	}
};

FormField.prototype.displayError = function() {
	Log.info("Displaying Error - " + this.element.name + " (" + this.errorMessengers.length + ")");

	for (var i=0; i<this.errorMessengers.length; i++) {
		this.errorMessengers[i].displayError();
	}
};

FormField.prototype.undisplayError = function() {
	for (var i=0; i<this.errorMessengers.length; i++) {
		this.errorMessengers[i].undisplayError();
	}
};

FormField.prototype.focus = function() {
	if (this.element !== null) {
		this.element.focus();
	}
};

FormField.prototype.getErrorMessage = function() {
	if (this.element !== null) {
		return this.element.getAttribute("fv_error_msg");
	}
	
	return null;
};
// End FormField object

// Email_FormField object
Email_FormField = function() {
	FormField.prototype.init.call(this);
};

Email_FormField.prototype = new FormField();
Email_FormField.parent = FormField.prototype;

Email_FormField.prototype.validate = function() {
	if (this.element === null) {
		return true;
	}
	
	Log.info("Validating (Email) - " + this.element.name);
	
	var value = this.element.value.trim();
	
	if (!this.isRequired && value === "") {
		Log.info("Valid Email (Not required and not filled out)");
		return true;
	}
	
	var atIndex = value.indexOf("@");
	var dotIndex = value.indexOf(".", atIndex);
	
	if (atIndex < 0 || atIndex === 0 || atIndex > value.length - 4) {
		Log.info("Invalid Email (atIndex)");
		Log.omnitureError("Invalid Email (atIndex)");
		return false;
	}
	
	if (dotIndex <= atIndex + 1 || dotIndex == value.length - 1) {
		Log.info("Invalid Email (dotIndex)");
		Log.omnitureError("Invalid Email (dotIndex)");
		return false;
	}
	
	Log.info("Valid Email");
	return true;
};
// End Email_FormField object

// Zip_FormField object
Zip_FormField = function() { 
	FormField.prototype.init.call(this);
};

Zip_FormField.prototype = new FormField();
Zip_FormField.parent = FormField.prototype;

Zip_FormField.prototype.validate = function() {
	if (this.element === null) {
		return true;
	}
	
	Log.info("Validating (Zip) - " + this.element.name);

	var value = this.element.value.trim();
	
	if (!this.isRequired && value === "") {
		Log.info("Valid Zip (Not required and not filled out");
		return true;
	}

	var result = (this.validateAmericanZip(value) || this.validateCanadianZip(value));
	Log.info("Zip Result: " + result);
		
	return result;
};
// End Zip_FormField object

Zip_FormField.prototype.validateAmericanZip = function(zip) {
	Log.info("Checking American Zip Format");

	var i = 0;
	
	if (zip.length == 5 || zip.length == 9) {
		for (i=0; i<zip.length; i++) {
			if (isNaN(zip.charAt(i)) || zip.charAt(i) === ' ') {
				Log.info("Invalid Zip (1)");
				Log.omnitureError("Invalid Zip (1)");
				return false;
			}
		}
		
		Log.info("Valid Zip (1)");
		return true;
	}
	
	if (zip.length == 10) {
		for (i=0; i<5; i++) {
			if (isNaN(zip.charAt(i)) || zip.charAt(i) === ' ') {
				Log.info("Invalid Zip (2)");
				Log.omnitureError("Invalid Zip (2)");
				return false;
			}
		}
		
		if (zip.charAt(5) !== '-' && zip.charAt(5) !== ' ' && zip.charAt(5) !== '.') {
			Log.info("Invalid Zip (3)");
			Log.omnitureError("Invalid Zip (3)");
			return false;
		}
		
		for (i=6; i<10; i++) {
			if (isNaN(zip.charAt(i)) || zip.charAt(i) === ' ') {
				Log.info("Invalid Zip (4)");
				Log.omnitureError("Invalid Zip (4)");
				return false;
			}
		}

		Log.info("Valid Zip (2)");		
		return true;
	}
	
	Log.info("Invalid Zip (5)");
	Log.omnitureError("Invalid Zip (5)");
	return false;
};

Zip_FormField.prototype.validateCanadianZip = function(zip) {
	Log.info("Checking Canadian Zip Format");
	
	if (zip.length == 6) {
		if (isNaL(zip.charAt(0))) {
			Log.omnitureError("Invalid Canadian Zip (1)");
			return false;
		}
		if (isNaN(zip.charAt(1)) || zip.charAt(1) === ' ') {
			Log.omnitureError("Invalid Canadian Zip (2)");
			return false;
		}
		if (isNaL(zip.charAt(2))) {
			Log.omnitureError("Invalid Canadian Zip (3)");
			return false;
		}
		if (isNaN(zip.charAt(3)) || zip.charAt(3) === ' ') {
			Log.omnitureError("Invalid Canadian Zip (4)");
			return false;
		}
		if (isNaL(zip.charAt(4))) {
			Log.omnitureError("Invalid Canadian Zip (5)");
			return false;
		}
		if (isNaN(zip.charAt(5)) || zip.charAt(5) === ' ') {
			Log.omnitureError("Invalid Canadian Zip (6)");
			return false;
		}
		
		Log.info("Valid Zip");
		return true;
	}
	
	if (zip.length == 7) {
		if (isNaL(zip.charAt(0))) {
			Log.omnitureError("Invalid Canadian Zip (7)");
			return false;
		}
		if (isNaN(zip.charAt(1)) || zip.charAt(1) === ' ') {
			Log.omnitureError("Invalid Canadian Zip (8)");
			return false;
		}
		if (isNaL(zip.charAt(2))) {
			Log.omnitureError("Invalid Canadian Zip (9)");
			return false;
		}
		
		if (zip.charAt(3) !== ' ' && zip.charAt(3) !== '.' && zip.charAt(3) !== '-') {
			Log.omnitureError("Invalid Canadian Zip (10)");
			return false;
		}
		
		if (isNaN(zip.charAt(4)) || zip.charAt(4) === ' ') {
			Log.omnitureError("Invalid Canadian Zip (11)");
			return false;
		}
		if (isNaL(zip.charAt(5))) {
			Log.omnitureError("Invalid Canadian Zip (12)");
			return false;
		}
		if (isNaN(zip.charAt(6)) || zip.charAt(6) === ' ') {
			Log.omnitureError("Invalid Canadian Zip (13)");
			return false;
		}
		
		Log.info("Valid Zip");
		return true;
	}
	
	Log.info("Invalid Zip");
	Log.omnitureError("Invalid Canadian Zip (14)");
	return false;
};

// Phone_FormField object
Phone_FormField = function() {
	FormField.prototype.init.call(this);
	this.areaCodeRequired = false; 
};

Phone_FormField.prototype = new FormField();
Phone_FormField.parent = FormField.prototype;

Phone_FormField.prototype.validate = function() {
	if (this.element === null) {
		return true;
	}
	
	var value = this.element.value.trim();
	
	Log.info("Validating Phone Number (" + this.element.name + ")");
	
	if (!this.isRequired && value === "") {
		Log.info("Valid Phone Number (Not required and not filled out)");
		return true;
	}
	
	var numCount = 0;
	
	for (var i=0; i<value.length; i++) {
		if (!isNaN(value.charAt(i)) && value.charAt(i) !== ' ') {
			numCount++;
		}
	}
	
	if (this.areaCodeRequired && numCount == 7) {
		Log.info("Invalid Phone Number (Requires area code)");
		Log.omnitureError("Invalid Phone Number (Requires area code)");
		return false;
	}
	
	if (numCount == 7 || numCount >= 10) {
		Log.info("Valid Phone Number");
		return true;
	}
	
	Log.info("Invalid Phone Number");
	Log.omnitureError("Invalid Phone Number");
	return false;
};
// End Phone_FormField object

// ErrorMessenger object
ErrorMessenger = function() {
	this.errorDisplayed = false;
};

ErrorMessenger.prototype.displayError = function() {
	Log.info("Displaying Error");

	this.errorDisplayed = true;
};

ErrorMessenger.prototype.undisplayError = function() {
	this.errorDisplayed = false;
};

ErrorMessenger.prototype.isDisplayed = function() {
	return this.errorDisplayed;
};
// End ErrorMessenger object

// Label_ErrorMessenger object
//   Inherits ErrorMessenger
Label_ErrorMessenger = function(label) {
	this.label = null;
	this.originalClass = "";
	this.errorClass = "";
	
	if (label !== null) {
		this.label = label;
		this.originalClass = label.className;
		
		if (label.getAttribute("fv_error_class") !== null) {
			this.errorClass = label.getAttribute("fv_error_class");
		}
	}
};

Label_ErrorMessenger.prototype = new ErrorMessenger();
Label_ErrorMessenger.parent = ErrorMessenger.prototype;

Label_ErrorMessenger.prototype.displayError = function() {
	ErrorMessenger.prototype.displayError.call(this);

	if (this.label !== null) {
		this.label.className = this.errorClass;
	}
};

Label_ErrorMessenger.prototype.undisplayError = function() {
	ErrorMessenger.prototype.undisplayError.call(this);

	if (this.label !== null) {
		this.label.className = this.originalClass;
	}
};
// End Label_ErrorMessenger

// Field_ErrorMessenger Object
Field_ErrorMessenger = function(field, errorClass) {
	this.field = null;
	this.originalClass = "";
	this.errorClass = "";
	
	if (field !== null) {
		this.field = field;
		this.originalClass = field.className;
		this.errorClass = errorClass;
	}
};

Field_ErrorMessenger.prototype = new ErrorMessenger();
Field_ErrorMessenger.parent = ErrorMessenger.prototype;

Field_ErrorMessenger.prototype.displayError = function() {
	ErrorMessenger.prototype.displayError.call(this);

	if (this.field !== null) {
		this.field.className = this.errorClass;
	}
};

Field_ErrorMessenger.prototype.undisplayError = function() {
	ErrorMessenger.prototype.undisplayError.call(this);

	if (this.field !== null) {
		this.field.className = this.originalClass;
	}
};
// End Field_ErrorMessenger Object

// BackgroundColor_ErrorMessenger Object
BackgroundColor_ErrorMessenger = function(field, errorColor) {
	this.field = null;
	this.originalColor = "";
	this.errorColor = "";
	
	if (field !== null) {
		this.field = field;
		this.originalColor = field.style.backgroundColor;
		this.errorColor = errorColor;
	}
};

BackgroundColor_ErrorMessenger.prototype = new ErrorMessenger();
BackgroundColor_ErrorMessenger.parent = ErrorMessenger.prototype;

BackgroundColor_ErrorMessenger.prototype.displayError = function() {
	ErrorMessenger.prototype.displayError.call(this);

	if (this.field !== null) {
		this.field.style.backgroundColor = this.errorColor;
	}
};

BackgroundColor_ErrorMessenger.prototype.undisplayError = function() {
	ErrorMessenger.prototype.undisplayError.call(this);

	if (this.field !== null) {
		this.field.style.backgroundColor = this.originalColor;
	}
};
// End BackgroundColor_ErrorMessenger Object

// HiddenBlock_ErrorMessenger Object
HiddenBlock_ErrorMessenger = function(element) {
	this.element = null;
	this.msg = "";
	
	if (element !== null) {
		this.element = element;
	}
};

HiddenBlock_ErrorMessenger.prototype = new ErrorMessenger();
HiddenBlock_ErrorMessenger.parent = ErrorMessenger.prototype;

HiddenBlock_ErrorMessenger.prototype.displayError = function() {
	ErrorMessenger.prototype.displayError.call(this);

	if (this.element !== null && this.msg !== "") {
		this.element.style.display = "block";
	}
};

HiddenBlock_ErrorMessenger.prototype.undisplayError = function() {
	ErrorMessenger.prototype.undisplayError.call(this);

	if (this.element !== null) {
		this.element.style.display = "none";
	}
};

HiddenBlock_ErrorMessenger.prototype.setMessage = function(msg) {
	if (this.element !== null) {
		this.element.innerHTML = msg;
		this.msg = msg;
	}
};
// End Field_ErrorMessenger Object

String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g, "");
};

// is Not A Letter, true only for A-Z and a-z
var isNaL = function(character) {
	character = character.toUpperCase();
	
	if (character.length < 1 || character.length > 1) {
		return false;
	}
	
	var result = (character < 'A' || character > 'Z');
	
	return result;
};

var Log = {
	printLogToScreen : true,
	errorField : null,

	init : function() {
		if (Log.printLogToScreen && Log.errorField === null) {
			if (document.getElementById("ErrorField") !== null) {
				Log.errorField = document.getElementById("ErrorField");
			}
		}
	},
	
	pageName : null,
	formName : null,
	
	setPageName : function(pName){
		Log.pageName = pName;
	},
	
	setFormName : function(fName){
		Log.formName = fName;
	},
	
	omnitureError : function(errorString){
		s.sendFormEvent('e',Log.pageName,Log.formName,errorString);
	},
	
	error : function(msg) {
		Log.addMsg("<span style=\"color:#FF0000\">*" + msg + "</span>");
	},
		
	info : function(msg) {
		Log.addMsg("<span style=\"color:#0000FF\">*" + msg + "</span>");
	},
	
	addMsg : function(msg) {
		Log.init();
	
		if (Log.printLogToScreen && Log.errorField !== null) {
			Log.errorField.innerHTML = Log.errorField.innerHTML + "<br />" + msg;
		}
	},
	
	clear : function() {
		Log.init();
	
		if (Log.printLogToScreen && Log.errorField !== null && Log.errorField !== undefined) {
			Log.errorField.innerHTML = "";
		}	
	}
};