/**
 * Form constructor
 * @constructor
 * @extends EventDispatcher
 * @param object formElement The form element for validation
 * @throws {Invalid Argument} When formElement is missing, not a DOM node or not a form element
 */
function Form(formElement)
{
	// Call the prototype's constructor
	EventDispatcher.call(this);

	// Check arguments
	if (formElement == undefined || formElement.nodeType == undefined || formElement.nodeType != 1 || formElement.nodeName.toLowerCase() != "form")
	{
		throw new Error("Invalid argument: formElement is missing, not a DOM node or not a form element");
	}


	// Public
	this.addField				= addField;
	this.removeField			= removeField;
	this.getValidationTrigger	= getValidationTrigger;
	this.getAllNotices			= getAllNotices;
	this.getGenericNotices		= getGenericNotices;
	this.getPassedNotices		= getPassedNotices;
	this.getFailedNotices		= getFailedNotices;
	this.validate				= validate;

	// Private
	var self					= this,
		valid					= false,
		validationTrigger		= null,
		fields					= {},
		allNotices				= null,
		genericNotices			= null,
		passedNotices			= null,
		failedNotices			= null;


	// On instantation
	formElement.onsubmit = function()
	{
		return validate(Form.VALIDATION_TRIGGER_SUBMIT);
	}


	/**
	 * Add a field instance to the form.
	 * @param {Object} field A field instance.
	 * @method
	 */
	function addField(field)
	{
		if (field instanceof Field == false)
		{
			throw new Error("Invalid argument: field is not an instance of Field");
		}

		if (field in fields == false)
		{
			fields[field] = field;

			self.dispatchEvent(new CustomEvent(Form.FIELD_ADDED, field));
		}
	}


	/**
	 * Remove a field instance from the form.
	 * @param {Object} field A field instance.
	 * @method
	 */
	function removeField(field)
	{
		if (field instanceof Field == false)
		{
			throw new Error("Invalid argument: field is not an instance of Field");
		}

		if (field in fields)
		{
			delete fields[field];

			self.dispatchEvent(new CustomEvent(Form.FIELD_REMOVED, field));
		}
	}


	/**
	 * Get the reason validation was triggered.
	 * @return {String} The reason validation was triggered.
	 * @method
	 */
	function getValidationTrigger()
	{
		return validationTrigger;
	}


	/**
	 * Get all notices generated by the validation process.
	 * @return {Array} An array containing all notices generated by the validation process.
	 * @method
	 */
	function getAllNotices()
	{
		return allNotices;
	}


	/**
	 * Get all generic notices generated by the validation process.
	 * @return {Array} An array containing all generic notices generated by the validation process.
	 * @method
	 */
	function getGenericNotices()
	{
		return genericNotices;
	}


	/**
	 * Get all passed notices generated by the validation process.
	 * @return {Array} An array containing all passed notices generated by the validation process.
	 * @method
	 */
	function getPassedNotices()
	{
		return passedNotices;
	}


	/**
	 * Get all failed notices generated by the validation process.
	 * @return {Array} An array containing all failed notices generated by the validation process.
	 * @method
	 */
	function getFailedNotices()
	{
		return failedNotices;
	}


	/**
	 * Validate all field instances added to the form.
	 * @param {String} trigger The trigger
	 * @method
	 * @return {Boolean} formValidationResult
	 */
	function validate(trigger)
	{
		self.dispatchEvent(new CustomEvent(Form.VALIDATION_STARTED, self));

		var formValidationResult = true;

		// Update the trigger
		if (typeof trigger != "string")
		{
			validationTrigger = null;
		}
		else
		{
			validationTrigger = trigger;
		}

		// Reset the notices
		allNotices		= [];
		genericNotices	= [];
		passedNotices	= [];
		failedNotices	= [];

		for (var field in fields)
		{
			var fieldValidationResult = fields[field].validate(Field.VALIDATION_TRIGGER_SUBMIT);

			// No validation performed on field (returned null)
			if (fieldValidationResult == null)
			{
				if (fields[field].getGenericNotice() != null)
				{
					allNotices.push(fields[field].getGenericNotice());
					genericNotices.push(fields[field].getGenericNotice());
				}
			}

			// Field was validated (either passed or failed)
			else
			{
				// Update the form validation result
				formValidationResult = Boolean(formValidationResult & fieldValidationResult);

				if (fieldValidationResult == false && fields[field].getFailedNotice() != null)
				{
					allNotices.push(fields[field].getFailedNotice());
					failedNotices.push(fields[field].getFailedNotice());
				}
				else if (fieldValidationResult == true && fields[field].getPassedNotice() != null)
				{
					allNotices.push(fields[field].getPassedNotice());
					passedNotices.push(fields[field].getPassedNotice());
				}
			}
		}

		valid = formValidationResult;

		self.dispatchEvent(new CustomEvent(Form.VALIDATION_COMPLETE, self));

		return formValidationResult;
	}
}

// Inheritance
Form.prototype = new EventDispatcher();

// Static
Form.FIELD_ADDED				= "field_added";
Form.FIELD_REMOVED				= "field_removed";
Form.VALIDATION_STARTED			= "validation_started";
Form.VALIDATION_COMPLETE		= "validation_complete";
Form.VALIDATION_TRIGGER_SUBMIT	= "trigger_submit";
Form.VALIDATION_TRIGGER_EDIT	= "trigger_edit";




/**
 * Field
 * @extends EventDispatcher
 * @constructor
 */
function Field()
{
	// Call the prototype's constructor
	EventDispatcher.call(this);


	// Private
	var self				= this,
		optional			= false,
		disabled			= false,
		requirements		= {},
		dependencies		= {},
		dependenciesMet		= true;

	// Public
	this.addRequirement			= addRequirement;
	this.removeRequirement		= removeRequirement;
	this.getRequirements		= getRequirements;
	this.addDependency			= addDependency;
	this.removeDependency		= removeDependency;
	this.checkDependencies		= checkDependencies;
	this.setOptional			= setOptional;
	this.getOptional			= getOptional;
	this.setDisabled			= setDisabled;
	this.getDisabled			= getDisabled;
	this.getGenericNotice		= getGenericNotice;
	this.getFailedNotice		= getFailedNotice;
	this.getPassedNotice		= getPassedNotice;


	/**
	 * Add a requirement instance to the field.
	 * @param {Object} requirement A requirement instance.
	 * @throws  {Invalid Argument} When requirement is not an instance of Requirement
	 * @method
	 */
	function addRequirement(requirement)
	{
		if (requirement instanceof Requirement == false)
		{
			throw new Error("Invalid argument: requirement is not an instance of Requirement");
		}

		if (requirement in requirements == false)
		{
			requirements[requirement] = requirement;

			self.dispatchEvent(new CustomEvent(Field.REQUIREMENT_ADDED, requirement));
		}
	}


	/**
	 * Remove a requirement instance from the field.
	 * @param {Object} requirement A requirement instance.
	 * @throws  {Invalid Argument} When requirement is not an instance of Requirement
	 * @method
	 */
	function removeRequirement(requirement)
	{
		if (requirement instanceof Requirement == false)
		{
			throw new Error("Invalid argument: requirement is not an instance of Requirement");
		}

		if (requirement in requirements)
		{
			delete requirements[requirement];

			self.dispatchEvent(new CustomEvent(Field.REQUIREMENT_REMOVED, requirement));
		}
	}


	/**
	 * Get all requirements assigned to the field.
	 * @return {Object} All requirements assigned to the field.
	 * @method
	 */
	function getRequirements()
	{
		return requirements;
	}


	/**
	 * Add a dependency instance to the field.
	 * @param {Object} dependency A dependency instance.
	 * @throws  {Invalid Argument} When dependency is not an instance of Dependency
	 * @method
	 */
	function addDependency(dependency)
	{
		if (dependency instanceof Dependency == false)
		{
			throw new Error("Invalid argument: dependency is not an instance of Dependency");
		}

		if (dependency in dependencies == false)
		{
			dependencies[dependency] = dependency;

			self.dispatchEvent(new CustomEvent(Field.DEPENDENCY_ADDED, dependency));
		}
	}


	/**
	 * Remove a dependency instance from the field.
	 * @param {Object} dependency A dependency instance.
	 * @throws  {Invalid Argument} When dependency is not an instance of Dependency
	 * @method
	 */
	function removeDependency(dependency)
	{
		if (dependency instanceof Dependency == false)
		{
			throw new Error("Invalid argument: dependency is not an instance of Dependency");
		}

		if (dependency in dependencies)
		{
			delete dependencies[dependency];

			self.dispatchEvent(new CustomEvent(Field.DEPENDENCY_REMOVED, dependency));
		}
	}


	/**
	 * Check all assigned dependencies
	 * @return {Boolean} Whether the dependencies were met
	 * @method
	 */
	function checkDependencies()
	{
		for (var dependency in dependencies)
		{
			if (dependencies[dependency].check() == false)
			{
				dependenciesMet = false;

				break;
			}
			else
			{
				dependenciesMet = true;
			}
		}

		return dependenciesMet;
	}


	/**
	 * Enable or disable the 'optional' property of the field. When optional, the field is validated, but the result won't change the users ability to submit the form.
	 * @param {Boolean} value A boolean value.
	 * @throws  {Invalid Argument} When value is missing or not a boolean
	 * @method
	 */
	function setOptional(value)
	{
		if (typeof value != "boolean")
		{
			throw new Error("Invalid argument: value is missing or not a boolean");
		}

		optional = value;
	}


	/**
	 * Get the value of the 'optional' property.
	 * @return {Boolean} The value of the 'optional' property.
	 * @method
	 */
	function getOptional()
	{
		return optional;
	}


	/**
	 * Enable or disable the field. When disabled, the field is not validated and returns null when the 'validate' method is called.
	 * @param {Boolean} value A boolean value.
	 * @throws  {Invalid Argument} When value is missing or not a boolean
	 * @method
	 */
	function setDisabled(value)
	{
		if (typeof value != "boolean")
		{
			throw new Error("Invalid argument: value is missing or not a boolean");
		}

		disabled = value;
	}


	/**
	 * Get the value of the 'disabled' property.
	 * @return {Boolean} The value of the 'disabled' property.
	 * @method
	 */
	function getDisabled()
	{
		return disabled;
	}


	/**
	 * Get a generic notice shown when no validation has occurred.
	 * @return {String} A generic notice shown when no validation has occurred.
	 * @method
	 */
	function getGenericNotice()
	{
		return null;
	}


	/**
	 * Get the notice shown when the validation has failed.
	 * @return {Object} The notice shown when validation has failed.
	 * @method
	 */
	function getFailedNotice()
	{
		return null;
	}


	/**
	 * Get the notice shown when the validation has passed.
	 * @return {Object} The notice shown when validation has passed.
	 * @method
	 */
	function getPassedNotice()
	{
		return null;
	}
}

// Inheritance
Field.prototype = new EventDispatcher();

/** @constant */ 
Field.REQUIREMENT_ADDED					= "requirement_added";
/** @constant */ 
Field.REQUIREMENT_REMOVED				= "requirement_removed";
/** @constant */ 
Field.DEPENDENCY_ADDED					= "dependency_added";
/** @constant */ 
Field.DEPENDENCY_REMOVED				= "dependency_removed";
/** @constant */ 
Field.VALIDATION_STARTED				= "validation_started";
/** @constant */ 
Field.VALIDATION_COMPLETE				= "validation_complete";
/** @constant */ 
Field.VALIDATION_TRIGGER_FIELD_ADDED	= "trigger_field_added";
/** @constant */ 
Field.VALIDATION_TRIGGER_BLUR			= "trigger_blur";
/** @constant */ 
Field.VALIDATION_TRIGGER_CHANGE			= "trigger_change";
/** @constant */ 
Field.VALIDATION_TRIGGER_KEYUP			= "trigger_keyup";
/** @constant */ 
Field.VALIDATION_TRIGGER_SUBMIT			= "trigger_submit";




/**
 * InputField
 * @param {Object} inputElement An input or textarea element.
 * @param {Object} genericNotice An instance of Notice used for generic messages regarding the field.
 * @throws {Invalid Argument} When inputElement is missing, not a DOM node or not an input or textarea element
 * @throws {Invalid Argument} When given inputElement does not have an 'id' attribute
 * @throws {Invalid Argument} When genericNotice is not an instance of Notice
 * @extends Field
 * @constructor
 */
function InputField(inputElement, genericNotice)
{
	// Call the prototype's constructor
	Field.call(this);


	// Check arguments
	if (inputElement == undefined || inputElement.nodeType == undefined || inputElement.nodeType != 1 || (inputElement.nodeName.toLowerCase() != "input" && inputElement.nodeName.toLowerCase() != "textarea"))
	{
		throw new Error("Invalid argument: inputElement is missing, not a DOM node or not an input or textarea element");
	}

	if (inputElement.id == undefined)
	{
		throw new Error("Invalid argument: given inputElement does not have an 'id' attribute");
	}

	if (genericNotice == undefined)
	{
		genericNotice = null;
	}
	else if (genericNotice instanceof Notice == false)
	{
		throw new Error("Invalid argument: genericNotice is not an instance of Notice");
	}


	// Public
	this.getValid				= getValid;
	this.getElement				= getElement;
	this.getGenericNotice		= getGenericNotice;
	this.getFailedNotice		= getFailedNotice;
	this.getPassedNotice		= getPassedNotice;
	this.getValidationTrigger	= getValidationTrigger;
	this.focus					= focus;
	this.validate				= validate;
	this.resetValidation		= resetValidation;
	this.toString				= toString;

	// Private
	var self				= this,
		valid				= null,
		keyUpListenerSet	= false,
		currentValue		= "",
		validationTrigger	= null,
		passedNotice		= null,
		failedNotice		= null;


	// On instantation
	dom = new DOM();
	dom.extend(inputElement);

	inputElement.addEventListener("blur", function()
	{
		if (inputElement.value != currentValue || (valid == true && inputElement.value == ""))
		{
			setKeyUpListener();

			currentValue = inputElement.value;

			validate(Field.VALIDATION_TRIGGER_BLUR);
		}
	}, false);


	/**
	 * Set the DOM event listeners which will trigger validation
	 */
	function setKeyUpListener()
	{
		if (keyUpListenerSet == false)
		{
			inputElement.addEventListener("keyup", keyUpListener, false);

			keyUpListenerSet = true;
		}
	}


	/**
	 * Remove the DOM event listeners which will trigger validation
	 */
	function removeKeyUpListener()
	{
		if (keyUpListenerSet == true)
		{
			inputElement.removeEventListener("keyup", keyUpListener, false);

			keyUpListenerSet = false;
		}
	}


	/**
	 * Validate while typing
	 */
	function keyUpListener()
	{
		if (inputElement.value != currentValue)
		{
			currentValue = inputElement.value;

			validate(Field.VALIDATION_TRIGGER_KEYUP);
		}
	}

	/**
	 * Get the value of the 'valid' property. The value contains the result of the 'validate' method.
	 * @return {Boolean} The result of the 'validate' method.
	 * @method
	 */
	function getValid()
	{
		return valid;
	}


	/**
	 * Get the input element to which the field instance is bound.
	 * @return {Object} The input element to which the field instance is bound.
	 * @method
	 */
	function getElement()
	{
		return inputElement;
	}


	/**
	 * Get a generic notice shown when no validation has occurred.
	 * @return {String} A generic notice shown when no validation has occurred.
	 * @method
	 */
	function getGenericNotice()
	{
		return genericNotice;
	}


	/**
	 * Get the notice shown when the validation has failed.
	 * @return {Object} The notice shown when validation has failed.
	 * @method
	 */
	function getFailedNotice()
	{
		return failedNotice;
	}


	/**
	 * Get the notice shown when the validation has passed.
	 * @return {Object} The notice shown when validation has passed.
	 * @method
	 */
	function getPassedNotice()
	{
		return passedNotice;
	}


	/**
	 * Get the reason validation was triggered.
	 * @return {String} The reason validation was triggered.
	 * @method
	 */
	function getValidationTrigger()
	{
		return validationTrigger;
	}


	/**
	 * Set focus to the input element.
	 * @return {Boolean} Always returns false in order to cancel a DOM event this function may be bound to.
	 * @method
	 */
	function focus()
	{
		inputElement.focus();

		return false;
	}


	/**
	 * Validate all requirement instances added to the field.
	 * @param {String} trigger The function or event that triggered the validation.
	 * @return {Boolean} Whether the requirements were met.
	 * @method
	 */
	function validate(trigger)
	{
		self.dispatchEvent(new CustomEvent(Field.VALIDATION_STARTED, self));

		// Update the trigger
		if (typeof trigger != "string")
		{
			validationTrigger = null;
		}
		else
		{
			validationTrigger = trigger;
		}

		// Reset the notices
		passedNotice = null;
		failedNotice = null;

		// If the validate method is called before the event listeners are set, correcting a mistake wouldn't cause immediate re-validation. So call setKeyUpListener just to be sure (won't cause listeners to be set multiple times).
		setKeyUpListener();

		// Check dependencies
		self.checkDependencies();

		// If the field is optional, it's allowed to be empty. No further validation occurs
		if (self.getOptional() == true && inputElement.value == "")
		{
			valid = null;

			self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));

			return null;
		}

		// If the field is disabled, no validation occurs
		if (self.getDisabled() == true)
		{
			valid = null;

			self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));

			return null;
		}

		// Otherwise, check each requirement
		var requirements		= self.getRequirements(),
			validationResult	= null;

		for (var requirement in requirements)
		{
			if (requirements[requirement].getDisabled() == false)
			{
				validationResult = requirements[requirement].validate(self);

				if (validationResult == true)
				{
					valid = true;

					if (requirements[requirement].getPassedNotice() != null)
					{
						passedNotice = requirements[requirement].getPassedNotice();
					}
				}
				else if (validationResult == false)
				{
					valid = false;

					if (requirements[requirement].getFailedNotice() != null)
					{
						failedNotice = requirements[requirement].getFailedNotice();
					}

					break;
				}
			}
		}

		self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));

		return valid;
	}


	/**
	 * Reset all validation
	 */
	function resetValidation()
	{
		removeKeyUpListener();

		valid = null;

		self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[InputField " + inputElement.id + "]";
	}
}

// Inheritance
InputField.prototype = new Field();




/**
 * SelectField
 * @param {Object} selectElement A select element.
 * @param {Object} genericNotice An instance of Notice used for generic messages regarding the field.
 * @throws {Invalid Argument} When selectElement is missing, not a DOM node or not a select element
 * @throws {Invalid Argument} When given selectElement does not have an 'id' attribute
 * @throws {Invalid Argument} When genericNotice is not an instance of Notice
 * @extends Field
 * @constructor
 */
function SelectField(selectElement, genericNotice)
{
	// Call the prototype's constructor
	Field.call(this);


	// Check arguments
	if (selectElement == undefined || selectElement.nodeType == undefined || selectElement.nodeType != 1 || selectElement.nodeName.toLowerCase() != "select")
	{
		throw new Error("Invalid argument: selectElement is missing, not a DOM node or not a select element");
	}

	if (selectElement.id == undefined)
	{
		throw new Error("Invalid argument: given selectElement does not have an 'id' attribute");
	}

	if (genericNotice == undefined)
	{
		genericNotice = null;
	}
	else if (genericNotice instanceof Notice == false)
	{
		throw new Error("Invalid argument: genericNotice is not an instance of Notice");
	}


	// Public
	this.getValid				= getValid;
	this.getElement				= getElement;
	this.getGenericNotice		= getGenericNotice;
	this.getFailedNotice		= getFailedNotice;
	this.getPassedNotice		= getPassedNotice;
	this.getValidationTrigger	= getValidationTrigger;
	this.resetValidation		= resetValidation;
	this.focus					= focus;
	this.validate				= validate;
	this.toString				= toString;

	// Private
	var self				= this,
		valid				= null,
		currentValue		= "",
		validationTrigger	= null,
		passedNotice		= null,
		failedNotice		= null;


	// On instantation
	dom.extend(selectElement);

	selectElement.addEventListener("change", function()
	{
		validate(Field.VALIDATION_TRIGGER_CHANGE);
	}, false);


	/**
	 * Get the value of the 'valid' property. The value contains the result of the 'validate' method.
	 * @return {Boolean} The result of the 'validate' method.
	 * @method
	 */
	function getValid()
	{
		return valid;
	}


	/**
	 * Get the input element to which the field instance is bound.
	 * @return {Object} The input element to which the field instance is bound.
	 * @method
	 */
	function getElement()
	{
		return selectElement;
	}


	/**
	 * Get a generic notice shown when no validation has occurred.
	 * @return {String} A generic notice shown when no validation has occurred.
	 * @method
	 */
	function getGenericNotice()
	{
		return genericNotice;
	}


	/**
	 * Get the notice shown when the validation has failed.
	 * @return {Object} The notice shown when validation has failed.
	 * @method
	 */
	function getFailedNotice()
	{
		return failedNotice;
	}


	/**
	 * Get the notice shown when the validation has passed.
	 * @return {Object} The notice shown when validation has passed.
	 * @method
	 */
	function getPassedNotice()
	{
		return passedNotice;
	}


	/**
	 * Get the reason validation was triggered.
	 * @return {String} The reason validation was triggered.
	 * @method
	 */
	function getValidationTrigger()
	{
		return validationTrigger;
	}


	/**
	 * Set focus to the select element.
	 * @return {Boolean} Always returns false in order to cancel a DOM event this function may be bound to.
	 * @method
	 */
	function focus()
	{
		selectElement.focus();

		return false;
	}



	/**
    * Reset all validation
    */
    function resetValidation() {
        valid = null;

        self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));
    }



	/**
	 * Validate all requirement instances added to the field.
	 * @param {String} trigger The function or event that triggered the validation.
	 * @return {Boolean} Whether the requirements were met.
	 * @method
	 */
	function validate(trigger)
	{
		self.dispatchEvent(new CustomEvent(Field.VALIDATION_STARTED, self));

		// Update the trigger
		if (typeof trigger != "string")
		{
			validationTrigger = null;
		}
		else
		{
			validationTrigger = trigger;
		}

		// Reset the notices
		passedNotice = null;
		failedNotice = null;

		// Check dependencies
		self.checkDependencies();

		// If the field is optional, it's allowed to be empty. No further validation occurs
		if (self.getOptional() == true && selectElement.value == "")
		{
			valid = null;

			self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));

			return null;
		}

		// If the field is disabled, no validation occurs
		if (self.getDisabled() == true)
		{
			valid = null;

			self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));

			return null;
		}

		// Otherwise, check each requirement
		var requirements		= self.getRequirements(),
			validationResult	= null;

		for (var requirement in requirements)
		{
			if (requirements[requirement].getDisabled() == false)
			{
				validationResult = requirements[requirement].validate(self);

				if (validationResult == true)
				{
					valid = true;

					passedNotice = requirements[requirement].getPassedNotice();
				}
				else if (validationResult == false)
				{
					valid = false;

					failedNotice = requirements[requirement].getFailedNotice();

					break;
				}
			}
		}

		self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));

		return valid;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[SelectField " + selectElement.id + "]";
	}
}

// Inheritance
SelectField.prototype = new Field();




/**
 * DateField
 * @param {Object} dayElement A select element for the date's day.
 * @param {Object} monthElement A select element for the date's month.
 * @param {Object} yearElement A select element for the date's year.
 * @param {Object} genericNotice An instance of Notice used for generic messages regarding the field.
 * @throws {Invalid Argument} When dayElement != null and dayElement is missing, not a DOM node or not a select element
 * @throws {Invalid Argument} When monthElement is missing, not a DOM node or not a select element
 * @throws {Invalid Argument} When yearElement is missing, not a DOM node or not a select element
 * @throws {Invalid Argument} When genericNotice is not an instance of Notice
 * @extends Field
 * @constructor
 */
function DateField(dayElement, monthElement, yearElement, genericNotice)
{
	// Call the prototype's constructor
	Field.call(this);

	if (dayElement !== null)
	{
		// Check arguments
		if (dayElement == undefined || dayElement.nodeType == undefined || dayElement.nodeType != 1 || dayElement.nodeName.toLowerCase() != "select")
		{
			throw new Error("Invalid argument: dayElement is missing, not a DOM node or not a select element");
		}
	}

	if (monthElement == undefined || monthElement.nodeType == undefined || monthElement.nodeType != 1 || monthElement.nodeName.toLowerCase() != "select")
	{
		throw new Error("Invalid argument: monthElement is missing, not a DOM node or not a select element");
	}

	if (yearElement == undefined || yearElement.nodeType == undefined || yearElement.nodeType != 1 || yearElement.nodeName.toLowerCase() != "select")
	{
		throw new Error("Invalid argument: yearElement is missing, not a DOM node or not a select element");
	}

	if (yearElement.id == undefined)
	{
		throw new Error("Invalid argument: given yearElement does not have an 'id' attribute");
	}

	if (genericNotice == undefined)
	{
		genericNotice = null;
	}
	else if (genericNotice instanceof Notice == false)
	{
		throw new Error("Invalid argument: genericNotice is not an instance of Notice");
	}


	// Public
	this.getValid				= getValid;
	this.getElement				= getElement;
	this.getGenericNotice		= getGenericNotice;
	this.getFailedNotice		= getFailedNotice;
	this.getPassedNotice		= getPassedNotice;
	this.getValidationTrigger	= getValidationTrigger;
	this.resetValidation		= resetValidation;
	this.focus					= focus;
	this.validate				= validate;
	this.toString				= toString;

	// Private
	var self				= this,
		valid				= null,
		currentValue		= "",
		validationTrigger	= null,
		passedNotice		= null,
		failedNotice		= null,
		date				= null;


	// On instantation
	if (dayElement !== null)
	{
		dom.extend(dayElement);
	}

	dom.extend(monthElement);
	dom.extend(yearElement);

	if (dayElement !== null)
	{
		dayElement.addEventListener("change", function()
		{
			validate(Field.VALIDATION_TRIGGER_CHANGE);
		}, false);
	}

	monthElement.addEventListener("change", function()
	{
		validate(Field.VALIDATION_TRIGGER_CHANGE);
	}, false);

	yearElement.addEventListener("change", function()
	{
		validate(Field.VALIDATION_TRIGGER_CHANGE);
	}, false);


	/**
	 * Get the value of the 'valid' property. The value contains the result of the 'validate' method.
	 * @return {Boolean} The result of the 'validate' method.
	 * @method
	 */
	function getValid()
	{
		return valid;
	}


	/**
	 * Get the input element to which the field instance is bound.
	 * @return {Object} The input element to which the field instance is bound.
	 * @method
	 */
	function getElement()
	{
		return yearElement;
	}


	/**
	 * Get the date as selected.
	 * @return {Array} The date as selected.
	 * @method
	 */
	function getDate()
	{
		return date;
	}


	/**
	 * Get a generic notice shown when no validation has occurred.
	 * @return {String} A generic notice shown when no validation has occurred.
	 * @method
	 */
	function getGenericNotice()
	{
		return genericNotice;
	}


	/**
	 * Get the notice shown when the validation has failed.
	 * @return {Object} The notice shown when validation has failed.
	 * @method
	 */
	function getFailedNotice()
	{
		return failedNotice;
	}


	/**
	 * Get the notice shown when the validation has passed.
	 * @return {Object} The notice shown when validation has passed.
	 * @method
	 */
	function getPassedNotice()
	{
		return passedNotice;
	}


	/**
	 * Get the reason validation was triggered.
	 * @return {String} The reason validation was triggered.
	 * @method
	 */
	function getValidationTrigger()
	{
		return validationTrigger;
	}



 	/**
    * Reset all validation
    */
    function resetValidation() {
        valid = null;

        self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));
    }



	/**
	 * Set focus to the select element.
	 * @return {Boolean} Always returns false in order to cancel a DOM event this function may be bound to.
	 * @method
	 */
	function focus()
	{
		if (dayElement !== null)
		{
			dayElement.focus();
		}
		else
		{
			monthElement.focus();
		}

		return false;
	}


	/**
	 * Validate all requirement instances added to the field.
	 * @param {String} trigger The function or event that triggered the validation.
	 * @return {Boolean} Whether the requirements were met.
	 * @method
	 */
	function validate(trigger)
	{
		self.dispatchEvent(new CustomEvent(Field.VALIDATION_STARTED, self));

		// Update the trigger
		if (typeof trigger != "string")
		{
			validationTrigger = null;
		}
		else
		{
			validationTrigger = trigger;
		}

		// Reset the notices
		passedNotice = null;
		failedNotice = null;

		// Check dependencies
		self.checkDependencies();

		// Update date
		if (dayElement !== null)
		{
			// Update date
			if (dayElement.value == "" || monthElement.value == "" || yearElement.value == "")
			{
				date = null;
	
				// The onblur validation should only occur when all fields have a value
				if (trigger == Field.VALIDATION_TRIGGER_BLUR || trigger == Field.VALIDATION_TRIGGER_CHANGE)
				{
					return null;
				}
			}
			else
			{
				date = [monthElement.value, dayElement.value, yearElement.value];
			}
		}
		else
		{
			// Update date
			if (monthElement.value == "" || yearElement.value == "")
			{
				date = null;
	
				// The onblur validation should only occur when all fields have a value
				if (trigger == Field.VALIDATION_TRIGGER_BLUR || trigger == Field.VALIDATION_TRIGGER_CHANGE)
				{
					return null;
				}
			}
			else
			{
				date = [monthElement.value, 1, yearElement.value];
			}
		}
		
		// If the field is optional, it's allowed to be empty. No further validation occurs
		if (self.getOptional() == true && dayElement.value == "" && monthElement.value == "" && yearElement.value == "")
		{
			valid = null;

			self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));

			return null;
		}

		// If the field is disabled, no validation occurs
		if (self.getDisabled() == true)
		{
			valid = null;

			self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));

			return null;
		}

		// Otherwise, check each requirement
		var requirements		= self.getRequirements(),
			validationResult	= null;

		valid = false;

		for (var requirement in requirements)
		{
			if (requirements[requirement].getDisabled() == false)
			{
				validationResult = requirements[requirement].validate(date);

				if (validationResult == true)
				{
					valid = true;

					passedNotice = requirements[requirement].getPassedNotice();
				}
				else if (validationResult == false)
				{
					valid = false;

					failedNotice = requirements[requirement].getFailedNotice();

					break;
				}
			}
		}

		self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));

		return valid;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[DateField " + yearElement.id + "]";
	}
}

// Inheritance
DateField.prototype = new Field();

/** @constant */
DateField.RESULT_INDEX_MONTH	= 0;
/** @constant */
DateField.RESULT_INDEX_DAY		= 1;
/** @constant */
DateField.RESULT_INDEX_YEAR		= 2;




/**
 * Notice
 * @param {string} plainText The notice as plain text.
 * @param {Array} html The notice as an array of DOM nodes.
 * @constructor
 */
function Notice(plainText, html)
{
	// Check arguments
	if (typeof plainText != "string")
	{
		throw new Error("Invalid argument: plainText is missing or not a string");
	}

	if (html == undefined)
	{
		html = [plainText];
	}
	else if (html instanceof Array == false)
	{
		throw new Error("Invalid argument: html is not an array");
	}


	// Public
	this.getPlainText	= getPlainText;
	this.getHTML		= getHTML;
	this.toString		= toString;
	
	// Private
	var self = this;


	/**
	 * Get the notice as plain text.
	 * @return {String} The notice as plain text.
	 */
	function getPlainText()
	{
		return plainText;
	}


	/**
	 * Get the notice as HTML.
	 * @return {String} The notice as HTML.
	 */
	function getHTML()
	{
		return html;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[Notice " + plainText + "]";
	}
}




/**
 * Dependency
 * @constructor
 */
function Dependency()
{
	// Public
	this.check = check;


	/**
	 * Check if the dependency was met.
	 * @return {Boolean} Whether the dependency was met.
	 * @method
	 */
	function check()
	{
		return false;
	}
}




/**
 * ValidFieldDependency
 * @param {Object} field The field which needs to be valid for this dependency to be met.
 * @param {Function} callback The function called when the dependency is checked.
 * @extends Dependency
 * @constructor
 */
function ValidFieldDependency(field, callback)
{
	// Call the prototype's constructor
	Dependency.call(this);


	// Check arguments
	if (field instanceof Field == false)
	{
		throw new Error("Invalid argument: field is not an instance of Field");
	}

	if (callback != undefined && typeof callback != "function")
	{
		throw new Error("Invalid argument: callback is not a function");
	}


	// Public
	this.check		= check;
	this.toString	= toString;


	/**
	 * Check if the dependency was met.
	 * @return {Boolean} Whether the dependency was met.
	 * @method
	 */
	function check()
	{
		// We don't just return the value of the getValid method, as it might be null. Instead, we default to false, unless it's... well, true
		var returnValue = false;

		if (field.getValid() == true)
		{
			returnValue = true;
		}

		if (callback != undefined)
		{
			callback(returnValue); // The return value is reversed, as we don't want a field or requirement to be optional or disabled when it's valid, we want it the other way around
		}

		return returnValue;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[ValidFieldDependency " + field.getElement().id + "]";
	}
}

// Inheritance
ValidFieldDependency.prototype = new Dependency();




/**
 * VisibilityDependency
 * @param {Object} optionalFields The optional fields which need to be visible for this dependency to be met.
 * @param {Object} dependee The field which depends on it's visiblity for validation.
 * @extends Dependency
 * @constructor
 */
function VisibilityDependency(optionalFields, dependee)
{
	// Call the prototype's constructor
	Dependency.call(this);


	// Check arguments
	if (optionalFields instanceof OptionalFields == false)
	{
		throw new Error("Invalid argument: optionalFields is not an instance of OptionalFields");
	}

	if (dependee instanceof Field == false)
	{
		throw new Error("Invalid argument: dependee is not an instance of Field");
	}
	else if (dependee == undefined)
	{
		dependee = null;
	}


	// Public
	this.check		= check;
	this.toString	= toString;

	if (dependee != null)
	{
		optionalFields.addEventListener(OptionalFields.FIELD_HIDDEN, function(event)
		{
			dependee.setDisabled(true);
			dependee.resetValidation();
		});

		optionalFields.addEventListener(OptionalFields.FIELD_VISIBLE, function(event)
		{
			if (dependee.getElement().value != "")
			{
				dependee.setDisabled(false);
				dependee.validate();
			}
		});
	}


	/**
	 * Check if the dependency was met.
	 * @return {Boolean} Whether the dependency was met.
	 * @method
	 */
	function check()
	{
		// We don't just return the value of the getVisible method, as it might be null. Instead, we default to false, unless it's... well, true
		var returnValue = false;

		if (optionalFields.getVisible() == true)
		{
			returnValue = true;
		}

		if (dependee != null)
		{
			dependee.setDisabled(!returnValue);
		}

		return returnValue;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[VisibilityDependency " + optionalFields.getTargetElement().id + "]";
	}
}

// Inheritance
VisibilityDependency.prototype = new Dependency();



/**
 * VisibilitySelectDependency
 * @param {Object} optionalSelectFields The optional select fields which need to be visible for this dependency to be met.
 * @param {Object} dependee The field which depends on it's visiblity for validation.
 * @extends Dependency
 * @constructor
 */
function VisibilitySelectDependency(optionalSelectFields, dependee)
{
	// Call the prototype's constructor
	Dependency.call(this);

	// Check arguments
	if (optionalSelectFields instanceof OptionalSelectFields == false)
	{
		throw new Error("Invalid argument: optionalSelectFields is not an instance of OptionalSelectFields");
	}

	if (dependee instanceof Field == false)
	{
		throw new Error("Invalid argument: dependee is not an instance of Field");
	}
	else if (dependee == undefined)
	{
		dependee = null;
	}


	// Public
	this.check		= check;
	this.toString	= toString;

	/* uses all the triggerelements to enable or disable the dependee */
	if (dependee != null)
	{
		var triggerElements = optionalSelectFields.getTriggerElements();
		optionalSelectFields.addEventListener(OptionalSelectFields.FIELD_HIDDEN, function(event)
		{
			for (var i = 0; triggerElements[i] !== undefined; i++)
			{
				dependee.setDisabled(true);
				
				if (dependee.getElement().value == triggerElements[i].field.value && triggerElements[i].field.selected)
				{
					dependee.setDisabled(false);
					dependee.validate();
				}
			}
		});

		optionalSelectFields.addEventListener(OptionalSelectFields.FIELD_VISIBLE, function(event)
		{
			for (var i = 0; triggerElements[i] !== undefined; i++)
			{
				dependee.setDisabled(true);
				
				if (dependee.getElement().value == triggerElements[i].field.value && triggerElements[i].field.selected)
				{
					dependee.setDisabled(false);
					dependee.validate();
				}
			}
		});
	}


	/**
	 * Check if the dependency was met.
	 * @return {Boolean} Whether the dependency was met.
	 * @method
	 */
	function check()
	{
		// We don't just return the value of the getVisible method, as it might be null. Instead, we default to false, unless it's... well, true
		var returnValue = false;

		if (optionalSelectFields.getVisible() == true)
		{
			returnValue = true;
		}

		if (dependee != null)
		{
			dependee.setDisabled(!returnValue);
		}

		return returnValue;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[VisibilitySelectDependency " + optionalSelectFields.getTargetElement().id + "]";
	}
}

// Inheritance
VisibilitySelectDependency.prototype = new Dependency();



/**
 * CheckedDependency
 * @param {Object} checkbox The checkbox which must be checked for this dependency to be met.
 * @param {Function} callback The function called when the dependency is checked.
 * @extends Dependency
 * @constructor
 */
function CheckedDependency(checkBoxElement, callback)
{
	// Call the prototype's constructor
	Dependency.call(this);


	// Check arguments
	if (checkBoxElement == undefined || checkBoxElement.nodeType == undefined || checkBoxElement.nodeType != 1 || checkBoxElement.nodeName.toLowerCase() != "input" || (checkBoxElement.type != "radio" && checkBoxElement.type != "checkbox"))
	{
		throw new Error("Invalid argument: checkBoxElement is missing, not a DOM node, not an input element, not a radio button or not a checkbox");
	}

	if (typeof callback != "function")
	{
		throw new Error("Invalid argument: callback is not a function");
	}


	// Public
	this.check		= check;
	this.toString	= toString;


	/**
	 * Check if the dependency was met.
	 * @return {Boolean} Whether the dependency was met.
	 * @method
	 */
	function check()
	{
		// We don't just return the value of the checked property, as it might be null (or undefined). Instead, we default to false, unless it's... well, true
		var returnValue = false;

		if (checkBoxElement.checked == true)
		{
			returnValue = true;
		}

		callback(!returnValue); // The return value is reversed, as we don't want a field or requirement to be optional or disabled when it's visible, we want it the other way around

		return returnValue;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[CheckedDependency " + checkBoxElement.id + "]";
	}
}

// Inheritance
CheckedDependency.prototype = new Dependency();




/**
 * Requirement
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @param {Object} passedNotice An instance of Notice used when the requirement was met.
 * @extends EventDispatcher
 * @constructor
 */
function Requirement(failedNotice, passedNotice)
{
	// Call the prototype's constructor
	EventDispatcher.call(this);


	// Check arguments
	if (failedNotice == undefined)
	{
		failedNotice = null;
	}
	else if (failedNotice instanceof Notice == false)
	{
		throw new Error("Invalid argument: failedNotice is not an instance of Notice");
	}

	if (passedNotice == undefined)
	{
		passedNotice = null;
	}
	else if (passedNotice instanceof Notice == false)
	{
		throw new Error("Invalid argument: passedNotice is not an instance of Notice");
	}


	// Public
	this.addDependency		= addDependency;
	this.removeDependency	= removeDependency;
	this.checkDependencies	= checkDependencies;
	this.setDisabled		= setDisabled;
	this.getDisabled		= getDisabled;
	this.getFailedNotice	= getFailedNotice;
	this.getPassedNotice	= getPassedNotice;
	this.validate			= validate;

	// Private
	var self			= this,
		disabled		= false,
		dependencies	= {},
		dependenciesMet	= true;


	/**
	 * Add a dependency instance to the requirement.
	 * @param {Object} dependency A dependency instance.
	 * @method
	 */
	function addDependency(dependency)
	{
		if (dependency instanceof Dependency == false)
		{
			throw new Error("Invalid argument: dependency is not an instance of Dependency");
		}

		if (dependency in dependencies == false)
		{
			dependencies[dependency] = dependency;

			self.dispatchEvent(new CustomEvent(Field.DEPENDENCY_ADDED, dependency));
		}
	}


	/**
	 * Remove a dependency instance from the requirement.
	 * @param {Object} dependency A dependency instance.
	 * @method
	 */
	function removeDependency(dependency)
	{
		if (dependency instanceof Dependency == false)
		{
			throw new Error("Invalid argument: dependency is not an instance of Dependency");
		}

		if (dependency in dependencies)
		{
			delete dependencies[dependency];

			self.dispatchEvent(new CustomEvent(Field.DEPENDENCY_REMOVED, dependency));
		}
	}


	/**
	 * Check all assigned dependencies
	 * @return {Boolean} Whether the dependencies were met
	 * @method
	 */
	function checkDependencies()
	{
		for (var dependency in dependencies)
		{
			if (dependencies[dependency].check() == false)
			{
				dependenciesMet = false;

				break;
			}
			else
			{
				dependenciesMet = true;
			}
		}

		return dependenciesMet;
	}


	/**
	 * Enable or disable the requirement. When disabled, the requirement is not tested and returns null when the 'validate' method is called.
	 * @param {Boolean} value A boolean value.
	 * @method
	 */
	function setDisabled(value)
	{
		if (typeof value != "boolean")
		{
			throw new Error("Invalid argument: value is missing or not a boolean");
		}

		disabled = value;
	}


	/**
	 * Get the value of the 'disabled' property.
	 * @return {Boolean} The value of the 'disabled' property.
	 * @method
	 */
	function getDisabled()
	{
		return disabled;
	}


	/**
	 * Get the notice shown when the requirement was not met.
	 * @return {Object} The notice shown when the requirement was not met.
	 * @method
	 */
	function getFailedNotice()
	{
		return failedNotice;
	}


	/**
	 * Get the notice shown when the requirement was met.
	 * @return {Object} The notice shown when the requirement was met.
	 * @method
	 */
	function getPassedNotice()
	{
		return passedNotice;
	}


	/**
	 * Test the conditions of the requirement.
	 * @method
	 */
	function validate()
	{
		return null;
	}
}

// Inheritance
Requirement.prototype = new EventDispatcher();




/**
 * FormatRequirement
 * @param {Object} format A regular expression which the value of the field must match.
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @param {Object} passedNotice An instance of Notice used when the requirement was met.
 * @extends Requirement
 * @constructor
 */
function FormatRequirement(format, failedNotice, passedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice, passedNotice);


	// Check arguments
	if (format instanceof RegExp == false)
	{
		throw new Error("Invalid argument: format is missing or not a regular expression");
	}


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self = this;


	/**
	 * Test the value of the input element against the given format.
	 * @param {Object} field A reference to a field of which the value is tested.
	 * @return {Boolean} Whether or not the value matches the format.
	 */
	function validate(field)
	{
		if (self.checkDependencies() == true)
		{
			return format.test(field.getElement().value);
		}

		return false;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[FormatRequirement " + format.toString() + "]";
	}
}

// Inheritance
FormatRequirement.prototype = new Requirement();

/** @constant */
FormatRequirement.NOT_EMPTY						= /[^ |\t].*/;
/** @constant */
FormatRequirement.NUMERIC						= /^([0-9]+)?$/;
/** @constant */
FormatRequirement.EMAIL_ADDRESS					= /^[a-z0-9_\.\-\+]+\@([a-z0-9][a-z0-9-]*[a-z0-9]\.)+[a-z]{2,4}$/i;
/** @constant */
FormatRequirement.POSTALCODE_NL					= /^([0-9]{4} ?[a-z]{2})?$/i;
/** @constant */
FormatRequirement.POSTALCODE_BE					= /^((be?)?(-| )?[0-9]{4})?$/i;
/** @constant */
FormatRequirement.BANKACCOUNT_NL                = /^(:?[a-z0\s\.]+)?[0-9]\.?(:?(:?(:?\s?\.?)+[0-9]\.?){8}|(:?(:?\s?\.?)+[0-9]\.?){2,6})$/i;
/** @constant */
FormatRequirement.BANKACCOUNT_NL_NOTEMPTY       = /^[0-9]*[1-9]+[0-9]*$/i;
/** @constant */
FormatRequirement.BANKACCOUNT_BE                = /^(:?[0-9](:?\-?\s?\.?)+){12}$/i;
/** @constant */
FormatRequirement.NOT_ZERO                		= /^([1-9])$/;
/** @constant */
FormatRequirement.EMAIL_ADDRESS_OR_EMPTY		= /^$|^[a-z0-9_\.\-\+]+\@([a-z0-9][a-z0-9-]*[a-z0-9]\.)+[a-z]{2,4}$/i;
/** @constant */
FormatRequirement.TELEPHONENUMBER_OR_EMPTY_NL   = /^31[0-9]{9}$/;
/** @constant */
FormatRequirement.TIME							= /^[0-2]?[0-9](?:\.|:)[0-5][0-9]$/;


/**
 * MaxLengthRequirement
 * @param {Object} maxLength The maximum number of characters allowed.
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @param {Object} passedNotice An instance of Notice used when the requirement was met.
 * @extends Requirement
 * @constructor
 */
function MaxLengthRequirement(maxLength, failedNotice, passedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice, passedNotice);


	// Check arguments
	if (typeof maxLength != "number")
	{
		throw new Error("Invalid argument: maxLength is missing or not a number");
	}


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self = this;


	/**
	 * Check if the value of the field does not exceed the maximum number of characters.
	 * @param {Object} field A reference to a field of which the value is tested.
	 * @return {Boolean} Whether or not the value matches exceeds the maximum length.
	 */
	function validate(field)
	{
		if (self.checkDependencies() == true)
		{
			return (field.getElement().value.length <= maxLength);
		}

		return false;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[MaxLengthRequirement]";
	}
}

// Inheritance
MaxLengthRequirement.prototype = new Requirement();



/**
 * MinLengthRequirement
 * @param {Object} minLength The minimum number of words allowed.
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @param {Object} passedNotice An instance of Notice used when the requirement was met.
 * @extends Requirement
 * @constructor
 */
function MinLengthRequirement(wordCounter, minLength, failedNotice, passedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice, passedNotice);


	// Check arguments
	if (typeof minLength != "number")
	{
		throw new Error("Invalid argument: minLength is missing or not a number");
	}


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self = this;



	/**
	 * Check if the value of the field does not exceed the maximum number of characters.
	 * @param {Object} field A reference to a field of which the value is tested.
	 * @return {Boolean} Whether or not the value matches exceeds the maximum length.
	 */
	function validate()
	{
		
		if (self.checkDependencies() == true)
		{
			return wordCounter.getCount() >= minLength;
		}

		return false;
	}



	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[MinLengthRequirement]";
	}
}

// Inheritance
MinLengthRequirement.prototype = new Requirement();



/**
 * validateTelephoneNumber
 * @param {string} telephoneNumber A telephone number.
 * @param {string} defaultCountryCode The country code used when the given telephone number does not have one.
 * @param {Boolean} onlyMobile True or false Defaults to false if undefined.
  */
function validateTelephoneNumber(telephoneNumber, defaultCountryCode, onlyMobile)
{
	// If onlyMobile is undefined, default to false
	if (onlyMobile == undefined)
	{
		onlyMobile = false;
	}

	if(telephoneNumber == "")
	{
		return null;
	}

	// Clean up the telephone number (remove any characters that don't belong)
	telephoneNumber = telephoneNumber.replace(/\(0\)|\(|\)| |\t|\.|,|-|_|\/|\\/gi, "")
	
	// Check if phonenumber is bigger then only zero's
	if(telephoneNumber <=0 ) {
		return false;
	}

	// Check it's an international number for The Netherlands or Belgium
	if (/^(\+|00)(31|32)/.test(telephoneNumber) == true)
	{
		// Remove any's plusses (as they're not covered by the initial replacement)
		telephoneNumber = telephoneNumber.replace(/\+/g, "");

		// Remove the prefixed zero's
		telephoneNumber = telephoneNumber.replace(/^00/, "");
	}
	else
	{
		// Remove any's plusses (as they're not covered by the initial replacement)
		telephoneNumber = telephoneNumber.replace(/\+/g, "");

		telephoneNumber = telephoneNumber.replace(/^0/, defaultCountryCode);
	}

	// Check Dutch numbers (start with 31, 11 characters)
	if (/^31[0-9]{9}$/.test(telephoneNumber) == true && telephoneNumber.length == 11)
	{
		if (onlyMobile == true)
		{
			return /^316[0-9]{8}$/.test(telephoneNumber);
		}
		else
		{
			return true;
		}
	}

	// Check Belgian numbers (start with 32, 10 characters)
	if (/^32[0-9]{8}$/.test(telephoneNumber) == true && telephoneNumber.length == 10 && onlyMobile == false)
	{
		return true;
	}

	// Check Belgian mobile numbers (start with 32 plus one of the prefices, 11 characters)
	if (/^32(47[0-9])|(48[0-9])|(49[0-9])[0-9]{6}$/.test(telephoneNumber) == true && telephoneNumber.length == 11)
	{
		return true;
	}

	return false;
}




/**
 * ValidNLTelephoneNumberRequirement
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function ValidNLTelephoneNumberRequirement(failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self = this;


	/**
	 * Check if the value of the input element is a valid telephonenumber.
	 * @param {Object} field A reference to a field of which the value is tested.
	 * @return {Boolean} Whether or not the value matches the format.
	 */
	function validate(field)
	{
		if (self.checkDependencies() == false)
		{
			return false;
		}

		return validateTelephoneNumber(field.getElement().value, "31");
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[ValidNLTelephoneNumberRequirement]";
	}
}

// Inheritance
ValidNLTelephoneNumberRequirement.prototype = new Requirement();




/**
 * ValidBETelephoneNumberRequirement
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function ValidBETelephoneNumberRequirement(failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self = this;


	/**
	 * Check if the value of the input element is a valid telephonenumber.
	 * @param {Object} field A reference to a field of which the value is tested.
	 * @return {Boolean} Whether or not the value matches the format.
	 */
	function validate(field)
	{
		if (self.checkDependencies() == false)
		{
			return false;
		}

		return validateTelephoneNumber(field.getElement().value, "32");
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[ValidBETelephoneNumberRequirement]";
	}
}

// Inheritance
ValidBETelephoneNumberRequirement.prototype = new Requirement();




/**
 * ValidNLMobileTelephoneNumberRequirement
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function ValidNLMobileTelephoneNumberRequirement(failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self = this;


	/**
	 * Check if the value of the input element is a valid telephonenumber.
	 * @param {Object} field A reference to a field of which the value is tested.
	 * @return {Boolean} Whether or not the value matches the format.
	 */
	function validate(field)
	{
		if (self.checkDependencies() == false)
		{
			return false;
		}

		return validateTelephoneNumber(field.getElement().value, "31", true);
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[ValidNLTelephoneNumberRequirement]";
	}
}

// Inheritance
ValidNLMobileTelephoneNumberRequirement.prototype = new Requirement();




/**
 * ValidBEMobileTelephoneNumberRequirement
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function ValidBEMobileTelephoneNumberRequirement(failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self = this;


	/**
	 * Check if the value of the input element is a valid telephonenumber.
	 * @param {Object} field A reference to a field of which the value is tested.
	 * @return {Boolean} Whether or not the value matches the format.
	 */
	function validate(field)
	{
		if (self.checkDependencies() == false)
		{
			return false;
		}

		return validateTelephoneNumber(field.getElement().value, "32", true);
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[ValidBETelephoneNumberRequirement]";
	}
}

// Inheritance
ValidBEMobileTelephoneNumberRequirement.prototype = new Requirement();




/**
 * ValidDateRequirement
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function ValidDateRequirement(failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self	= this;


	/**
	 * Check if the value of the input element is a valid telephonenumber.
	 * @param {Array} date An array containing the contents of the DateField to be checked (M,D,Y).
	 * @return {Boolean} Whether or not the value matches the format.
	 */
	function validate(date)
	{
		if (self.checkDependencies() == true)
		{
			if (date == null)
			{
				return false;
			}

			var leapYear	= ((date[DateField.RESULT_INDEX_YEAR] % 4 == 0 && date[DateField.RESULT_INDEX_YEAR] % 100 != 0) || date[DateField.RESULT_INDEX_YEAR] % 400 == 0),
				months		= [31, (leapYear == true ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

			if (date[DateField.RESULT_INDEX_DAY] <= months[date[DateField.RESULT_INDEX_MONTH] - 1])
			{
				return true;
			}

			return false;
		}

		return false;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[ValidDateRequirement]";
	}
}

// ValidDateRequirement
ValidDateRequirement.prototype = new Requirement();


/**
 * ValidBankaccount_NLRequirement
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function ValidBankaccount_NLRequirement(failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self	= this;


	/**
	 * Check if the value of the input element is a valid telephonenumber.
	 * @param 
	 * @return {Boolean} Whether or not the value matches the format.
	 */
	function validate(bankaccount)
	{
		if (self.checkDependencies() == true)
		{
			bankaccountvalue = bankaccount.getElement().value;			
			bankaccountnumber = bankaccountvalue.replace(/\D/g,'')
			
			if(FormatRequirement.BANKACCOUNT_NL.test(bankaccountvalue) && bankaccountnumber > 0) {				
				return true;
			}
		}

		return false;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[ValidBankaccount_NLRequirement]";
	}
}

// ValidDateRequirement
ValidBankaccount_NLRequirement.prototype = new Requirement();




/**
 * ExistingPostalCodeRequirement
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function ExistingPostalCodeRequirement(failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self	= this,
		socket	= new Socket();


	/**
	 * Check if the value of the field is an existing postal code.
	 * @param {Object} field A reference to a field of which the value is tested.
	 * @return {Boolean} Whether or not the validation was passed.
	 */
	function validate(field)
	{
		if (self.checkDependencies() == true)
		{
			var postalCode	= null,
				areaCode	= null,
				letterCombo	= null,
				request		= null,
				result		= null;

			postalCode	= field.getElement().value.replace(" ", "");
			areaCode	= postalCode.substr(0, 4);
			letterCombo	= postalCode.substr(4);

			// The validator requires the request to be synchronous, as other requirements may depend on the result of the request
			socket.request(
				"/addresslookup.php",
				{
					areacode	: areaCode,
					lettercombo	: letterCombo
				},
				false,
				true
			);


			if (socket.getStatus() == 200)
			{
				if (socket.getResponseText() != "")
				{
					result = json.parse(socket.getResponseText());

					if (result.success == undefined)
					{
						return false;
					}
					else if (result.success == false)
					{
						return false;
					}

					return true;
				}

				return true;
			}

			return true;
		}

		return false;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[ExistingPostalCodeRequirement]";
	}
}

// Inheritance
ExistingPostalCodeRequirement.prototype = new Requirement();




/**
 * ExistingAddressRequirement
 * @param {Object} postalCodeField An instance of Field representing an input field used for a postal code.
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function ExistingAddressRequirement(postalCodeField, failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Check arguments
	if (postalCodeField instanceof Field == false)
	{
		throw new Error("Invalid argument: postalCodeField is not an instance of Field");
	}


	// Public
	this.getPassedNotice	= getPassedNotice;
	this.getPOBox			= getPOBox;
	this.validate			= validate;
	this.toString			= toString;

	// Private
	var self	= this,
		socket	= new Socket(),
		address	= "",
		poBox	= false;


	/**
	 * Get the notice shown when the requirement was met.
	 * @return {Object} The notice shown when the requirement was met.
	 * @method
	 */
	function getPassedNotice()
	{
		return new Notice(address);
	}


	/**
	 * Whether the lookup resulted in a P.O. box.
	 * @return {Boolean} Whether the lookup resulted in a P.O. box.
	 */
	function getPOBox()
	{
		return poBox;
	}


	/**
	 * Check if the given housenumber combined with the postal code results in an existing address.
	 * @param {Object} field A reference to a field of which the value is tested.
	 * @return {Boolean} Whether or not the validation was passed.
	 */
	function validate(field)
	{
		address = "";

		if (self.checkDependencies() == true)
		{
			var postalCode	= null,
				areaCode	= null,
				letterCombo	= null,
				request		= null,
				result		= null;

			postalCode	= postalCodeField.getElement().value.replace(" ", "");
			areaCode	= postalCode.substr(0, 4);
			letterCombo	= postalCode.substr(4);

			// The validator requires the request to be synchronous, as other requirements may depend on the result of the request
			socket.request(
				"/addresslookup.php",
				{
					areacode	: areaCode,
					lettercombo	: letterCombo,
					housenumber	: field.getElement().value
				},
				false,
				true
			);

			if (socket.getStatus() == 200)
			{
				if (socket.getResponseText() != "")
				{
					result = json.parse(socket.getResponseText());

					if (result.success == undefined)
					{
						return false;
					}
					else if (result.success == false)
					{
						return false;
					}

					address = result.street + ", " + result.city;

					poBox = (result.street.toLowerCase() == "postbus");

					return true;
				}

				return true;
			}

			return true;
		}

		return false;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[ExistingAddressRequirement]";
	}
}

// Inheritance
ExistingAddressRequirement.prototype = new Requirement();

/** @constant */
ExistingAddressRequirement.LOOKUP_STARTED	= "lookup_started";
/** @constant */
ExistingAddressRequirement.LOOKUP_COMPLETE	= "lookup_complete";




/**
 * NoPOBoxRequirement
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function NoPOBoxRequirement(failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self = this;


	/**
	 * Check if the given existingAddressRequirement doesn't return a P.O. box.
	 * @param {Object} field A reference to a field of which the value is tested.
	 * @return {Boolean} Whether or not the validation was passed.
	 * @throws {Error} When field does not have an ExistingAddressRequirement
	 */
	function validate(field)
	{
		if (self.checkDependencies() == true)
		{
			if (field.getRequirements()["[ExistingAddressRequirement]"] instanceof ExistingAddressRequirement == false)
			{
				throw new Error("Field does not have an ExistingAddressRequirement");
			}

			if (field.getRequirements()["[ExistingAddressRequirement]"].getPOBox() == true)
			{
				return false;
			}

			return true;
		}

		return false;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[NoPOBoxRequirement]";
	}
}

// Inheritance
NoPOBoxRequirement.prototype = new Requirement();




/**
 * ValidBEVatNumberRequirement
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function ValidBEVatNumberRequirement(failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self = this;


	/**
	 * Test the value of the input element against the modulus 97 check.
	 * @param {Object} field A reference to a field of which the value is tested.
	 * @return {Boolean} Whether or not the value passed the modulus 97 check.
	 */
	function validate(field)
	{
		if (self.checkDependencies() == true)
		{
			// Strip "BE", whitespace, dots, comma's, dashes, slashes and backslashes from the value
			var vatNumber = field.getElement().value.replace(/BE| |\t|\.|,|-|\/|\\/gi, "");

			// If the vatNumber is only 9 characters long, add a leading zero
			if (vatNumber.length == 9)
			{
				vatNumber = "0" + vatNumber;
			}

			// VAT number must have 10 characters
			if (vatNumber.length != 10)
			{
				return false;
			}

			// Get the first 7 numbers, with an offset of 1 (skipping leading zero's)
			var modulusValue = Number(vatNumber.substr(1, 7));

			// Get the last 2 numbers
			var verificationValue = Number(vatNumber.substr(8));

			if (97 - (modulusValue % 97) == verificationValue)
			{
				return true;
			}

			return false;
		}

		return false;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[ValidBEVatNumberRequirement]";
	}
}

// Inheritance
ValidBEVatNumberRequirement.prototype = new Requirement();



/**
 * ValidNLKvKNumberRequirement
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function ValidNLKvKNumberRequirement(failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self = this;


	/**
	 * Test the value of the input element against the modulus 97 check.
	 * @param {Object} field A reference to a field of which the value is tested.
	 * @return {Boolean} Whether or not the value passed the modulus 97 check.
	 */
	function validate(field)
	{
		if (self.checkDependencies() == true)
		{
			// Strip "NL", whitespace, dots, comma's, dashes, slashes and backslashes from the value
			var cocNumber = field.getElement().value.replace(/NL| |\t|\.|,|-|\/|\\/gi, "");

			// VAT number must have 10 characters
			if (cocNumber.length == 8)
			{
				return true;
			}

			return false;
		}

		return false;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[ValidNLKvKNumberRequirement]";
	}
}

// Inheritance
ValidNLKvKNumberRequirement.prototype = new Requirement();



/**
 * NonEmptyDateRequirement
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function NonEmptyDateRequirement(failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self = this;


	/**
	 * Check if a date was given.
	 * @param {Object} date The date of the DateField to be checked.
	 * @return {Boolean} Whether or not the validation was passed.
	 */
	function validate(date)
	{
		if (self.checkDependencies() == true)
		{
			if (date != null)
			{
				return true;
			}

			return false;
		}

		return false;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[NonEmptyDateRequirement]";
	}
}

// Inheritance
NonEmptyDateRequirement.prototype = new Requirement();




/**
 * MinimumDateRequirement
 * @param {Object} minimumDate The date must be equal to or older than this date.
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function MinimumDateRequirement(minimumDate, failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self = this;


	/**
	 * Check if the date is equal to or older than the minimum date.
	 * @param {Object} date The date of the DateField to be checked.
	 * @return {Boolean} Whether or not the validation was passed.
	 */
	function validate(date)
	{
		if (self.checkDependencies() == true)
		{
			if ((new Date(date.join("/"))) <= minimumDate)
			{
				return true;
			}

			return false;
		}

		return false;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[MinimumDateRequirement]";
	}
}

// Inheritance
MinimumDateRequirement.prototype = new Requirement();



/**
 * NonHistoricDateRequirement
 * @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
 * @extends Requirement
 * @constructor
 */
function NonHistoricDateRequirement(failedNotice)
{
	// Call the prototype's constructor
	Requirement.call(this, failedNotice);


	// Public
	this.validate	= validate;
	this.toString	= toString;

	// Private
	var self = this;


	/**
	 * Check if the date is beyond the current date.
	 * @param {Object} date The date of the DateField to be checked.
	 * @return {Boolean} Whether or not the validation was passed.
	 */
	function validate(date)
	{
		var today = new Date();

		if (self.checkDependencies() == true)
		{
			if ((new Date(date.join("/"))) > today)
			{
				return true;
			}

			return false;
		}

		return false;
	}


	/**
	 * Returns a string representation of the field.
	 * @return {String} A string representation of the field.
	 */
	function toString()
	{
		return "[NonHistoricDateRequirement]";
	}
}

// Inheritance
NonHistoricDateRequirement.prototype = new Requirement();



/**
* CheckboxField
* @param {Object} inputElement An input element.
* @param {Object} genericNotice An instance of Notice used for generic messages regarding the field.
* @extends Field
* @throws {Invalid Argument} When inputElement is missing, not a DOM node or not an input element
* @throws {Invalid Argument} When given inputElement does not have an 'id' attribute
* @throws {Invalid Argument} When genericNotice is not an instance of Notice
* @constructor
*/
function CheckboxField(inputElement, genericNotice) {
    // Call the prototype's constructor
    Field.call(this);

    // Check arguments
    if (inputElement == undefined || inputElement.nodeType == undefined || inputElement.nodeType != 1 || (inputElement.nodeName.toLowerCase() != "input")) {
        throw new Error("Invalid argument: inputElement is missing, not a DOM node or not an input element");
    }

    if (inputElement.id == undefined) {
        throw new Error("Invalid argument: given inputElement does not have an 'id' attribute");
    }

    if (genericNotice == undefined) {
        genericNotice = null;
    }
    else if (genericNotice instanceof Notice == false) {
        throw new Error("Invalid argument: genericNotice is not an instance of Notice");
    }


    // Public
    this.getValid = getValid;
    this.getElement = getElement;
    this.getGenericNotice = getGenericNotice;
    this.getFailedNotice = getFailedNotice;
    this.getPassedNotice = getPassedNotice;
    this.getValidationTrigger = getValidationTrigger;
    this.focus = focus;
    this.validate = validate;
    this.resetValidation = resetValidation;
    this.toString = toString;

    // Private
    var self = this,
		valid = null,
		validationTrigger = null,
		passedNotice = null,
		failedNotice = null;


    // On instantation
    dom.extend(inputElement);


    /**
    * Get the value of the 'valid' property. The value contains the result of the 'validate' method.
    * @return {Boolean} The result of the 'validate' method.
    * @method
    */
    function getValid() {
        return valid;
    }


    /**
    * Get the input element to which the field instance is bound.
    * @return {Object} The input element to which the field instance is bound.
    * @method
    */
    function getElement() {
        return inputElement;
    }


    /**
    * Get a generic notice shown when no validation has occurred.
    * @return {String} A generic notice shown when no validation has occurred.
    * @method
    */
    function getGenericNotice() {
        return genericNotice;
    }


    /**
    * Get the notice shown when the validation has failed.
    * @return {Object} The notice shown when validation has failed.
    * @method
    */
    function getFailedNotice() {
        return failedNotice;
    }


    /**
    * Get the notice shown when the validation has passed.
    * @return {Object} The notice shown when validation has passed.
    * @method
    */
    function getPassedNotice() {
        return passedNotice;
    }


    /**
    * Get the reason validation was triggered.
    * @return {String} The reason validation was triggered.
    * @method
    */
    function getValidationTrigger() {
        return validationTrigger;
    }


    /**
    * Set focus to the input element.
    * @return {Boolean} Always returns false in order to cancel a DOM event this function may be bound to.
    * @method
    */
    function focus() {
        inputElement.focus();

        return false;
    }


    /**
    * Validate all requirement instances added to the field.
    * @param {String} trigger The function or event that triggered the validation.
    * @return {Boolean} Whether the requirements were met.
    * @method
    */
    function validate(trigger) {
        self.dispatchEvent(new CustomEvent(Field.VALIDATION_STARTED, self));

        // Update the trigger
        if (typeof trigger != "string") {
            validationTrigger = null;
        }
        else {
            validationTrigger = trigger;
        }

        // Reset the notices
        passedNotice = null;
        failedNotice = null;

        // Check dependencies
        self.checkDependencies();

        // If the field is optional, it's allowed to be empty. No further validation occurs
        if (self.getOptional() == true && inputElement.value == "") {
            valid = null;

            self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));

            return null;
        }

        // If the field is disabled, no validation occurs
        if (self.getDisabled() == true) {
            valid = null;

            self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));

            return null;
        }

        // Otherwise, check each requirement
        var requirements = self.getRequirements(),
			validationResult = null;

        for (var requirement in requirements) {
            if (requirements[requirement].getDisabled() == false) {
                validationResult = requirements[requirement].validate(self);

                if (validationResult == true) {
                    valid = true;

                    if (requirements[requirement].getPassedNotice() != null) {
                        passedNotice = requirements[requirement].getPassedNotice();
                    }
                }
                else if (validationResult == false) {
                    valid = false;

                    if (requirements[requirement].getFailedNotice() != null) {
                        failedNotice = requirements[requirement].getFailedNotice();
                    }

                    break;
                }
            }
        }

        self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));

        return valid;
    }


    /**
    * Reset all validation
    */
    function resetValidation() {
        valid = null;

        self.dispatchEvent(new CustomEvent(Field.VALIDATION_COMPLETE, self));
    }


    /**
    * Returns a string representation of the field.
    * @return {String} A string representation of the field.
    */
    function toString() {
        return "[CheckboxField " + inputElement.id + "]";
    }
}

// Inheritance
CheckboxField.prototype = new Field();




/**
* CheckedRequirement
* @param {Object} failedNotice An instance of Notice used when the requirement wasn't met.
* @param {Object} passedNotice An instance of Notice used when the requirement was met.
* @extends Requirement
* @constructor
*/

function CheckedRequirement(failedNotice, passedNotice) {
    // Call the prototype's constructor
    Requirement.call(this, failedNotice, passedNotice);

    // Public
    this.validate = validate;
    this.toString = toString;

    // Private
    var self = this;

    /**
    * Test the value of the input element against the given format.
    * @param {Object} field A reference to a field of which the value is tested.
    * @return {Boolean} Whether or not the value matches the format.
    */
    function validate(field) {
        if (self.checkDependencies() == true) {
            return field.getElement().checked;
        }

        return false;
    }
}

// Inheritance
CheckedRequirement.prototype = new Requirement();

function MinimumPasswordLengthRequirement(minLength, failedNotice) {
    Requirement.call(this, failedNotice);

    this.validate = validate;
    this.toString = toString;

    var self = this;

    function validate(field) {
        return field.getElement().value.length >= minLength;
    }

    function toString() {
        return "[MinimumPasswordLengthRequirement]";
    }
}
MinimumPasswordLengthRequirement.prototype = new Requirement();

function MinimumPasswordLengthNotRequiredRequirement(minLength, failedNotice) {
    Requirement.call(this, failedNotice);

    this.validate = validate;
    this.toString = toString;

    var self = this;

    function validate(field) {
    	if(field.getElement().value.length == 0){
    		return true;//no entry so we don't need to validate, the password can be empty
    	}else{
    		return field.getElement().value.length >= minLength;
    	}        
    }

    function toString() {
        return "[MinimumPasswordLengthNotRequiredRequirement]";
    }
}
MinimumPasswordLengthNotRequiredRequirement.prototype = new Requirement();

function SameValueRequirement(otherField, failedNotice) {
    Requirement.call(this, failedNotice);

    this.validate = validate;
    this.toString = toString;

    var self = this;

    function validate(field) {
        return (field.getElement().value == otherField.getElement().value);
    }

    function toString() {
        return "[SameValueRequirement]";
    }
}
SameValueRequirement.prototype = new Requirement();
