if (! Validator) {
var Validator = Class.create();

Validator.prototype = {
	initialize: function (formId) {
		//console.log('initialize');
		//console.log('- formid=' + formId);
		this.formId = formId;
		this.handlers = {};
		this.rules = new Array();
		this.ruleName = {};
	},

	addValidationHandlers : function (library) {
		//console.log('addValidationHandlers');
		var newHandlers = library.getHandlers();
		for (var i in newHandlers) {
			this.handlers[i] = { library : library, method : newHandlers[i] };
		}
	},

	addRule : function (fieldId, isRequired, checkType, errorMessage, name, enabled, options) {
		//console.log(this.formId + ' addRule');
		this.rules.push({
			fieldId : fieldId,
			isRequired : isRequired,
			checkFunction : (this.handlers[checkType]) ? this.handlers[checkType].library[this.handlers[checkType].method] : null,
			errorMessage : errorMessage,
			enabled : ((enabled == null) || enabled) ? true : false,
			options : (options) ? options : {}
		});
		if (name && (name != "")) {
			this.ruleName[name] = this.rules[this.rules.length - 1];
		}
	},

	/**
	 * Clears all validation rules so no validation is performed.
	 */
	clearRules : function () {
		//console.log(this.formId + ' clearRules');
		this.rules = new Array();
	},

	setRuleEnabled : function (ruleNameList, enabled) {
		//console.log(this.formId + ' setRuleEnabled');
		for (var i = 0; i < ruleNameList.length; i++) {
			this.ruleName[ruleNameList[i]].enabled = enabled;
			if (! enabled) {
				Element.removeClassName(this.ruleName[ruleNameList[i]].fieldId, "exceptionField");
			}
		}
	},

	/**
	 * Validates all rules for this validation object and calls the appropriate
	 * callback function depending on success or failure.
	 * Returns: True if validation succeeds, false otherwise.
	 */
	validate : function () {
		//console.log(this.formId + ' validate');
		var errors = new Array();
		var success = new Array();
		var currRule = null;
		var currValue = null;
		var result = false;
		//console.log('- serializing');
		var formValues = Form.serialize(this.formId, {hash:true, submit:true});
		//console.info(formValues);

		for (var i = 0; i < this.rules.length; i++) {
			currRule = this.rules[i];

			if (formValues[currRule.fieldId]) {
				currValue = formValues[currRule.fieldId];
			} else {
				currValue = "";
			}

			if (currRule.enabled) {
			
				if (this.isEmpty(currValue)) {
					if(currRule.isRequired){
						errors.push({fieldId: currRule.fieldId, errorMessage: currRule.errorMessage});
					} else {
						success.push({fieldId: currRule.fieldId});
					}
				} else if (currRule.checkFunction) {
					// Test using the appropriate check function
					if (currRule.checkFunction(currValue, currRule.options)) {					
						success.push({fieldId: currRule.fieldId});
					}
					else {
						errors.push({fieldId: currRule.fieldId, errorMessage: currRule.errorMessage});
					}
				} else {
					success.push({fieldId: currRule.fieldId});
				}
			}
		}

		if (errors.length == 0) {
			this.onValidateSuccess();
			this.doSuccessDisplay(success);

			result = true;
		} else {
			this.onValidateFailure(errors);
			this.doSuccessDisplay(success);
			this.doFailureDisplay(errors);
		}

		return result;
	},

	onFormSubmit : function (event) {
		//console.log(this.formId + ' onFormSubmit');
		var isValid = this.validate();
		if (! isValid) {
			Event.stop(event);
		}
	},

	onValidateSuccess : function () {
		//console.log(this.formId + ' onValidateSuccess');
		var formValElement = $("FormValidation-" + this.formId);
		formValElement.innerHTML = "";
		Element.hide(formValElement);
	},

	doSuccessDisplay : function (fieldArray) {
		fieldArray.each(function(currField) {
			Element.removeClassName(currField.fieldId, "exceptionField");
		});
	},

	onValidateFailure : function (errors) {
		//console.log(this.formId + ' onValidateFailure');
		var formValElement = $("FormValidation-" + this.formId);
		var valHtml = '<div class="formValidationHeader">Submission Problems</div>';
		valHtml += '<p>The following problems were found when processing your submission:</p>';
		valHtml += '<ul>';

		for (var i = 0; i < errors.length; i++) {
			valHtml = valHtml + '<li>' + errors[i].errorMessage + '</li>';
		}

		valHtml += '</ul>';
		valHtml += '<p>Please scroll down to correct the problems and submit the form again.</p>';

		formValElement.innerHTML = valHtml;
		Element.show(formValElement);
		new Effect.ScrollTo(formValElement);
	},

	doFailureDisplay : function (fieldArray) {
		//console.log(this.formId + ' doFailureDisplay');
		fieldArray.each(function(currField) {
			var field = $(currField.fieldId);
			if (field) {
				Element.addClassName(field, "exceptionField");
				field.title = currField.errorMessage;
			}
		});
	},

	/**
	 * Returns true if a value has been entered into the given field,
	 * false otherwise.
	 */
	isEmpty : function (value) {
		return (value.replace(/^\s+/,'').replace(/\s+$/,'').length == 0) ? true : false;
	}
}
}