// to make a field validateable it needs two things:
// 	1. the element must have "validateable" as one of its classes
//  2. the element must have a "validationParameters" attribute the value of which takes the following form:
//		<type>[=<typeValue>]@<errorMessage>
//	  where <type> is one of the types checked for in the isValid() function of the Validation object below
//	  more than one validation can be associated with the element by putting a ';' between them (there should not be a semicolon at the very end)
// example:
//	<input type="text" class="validateable" validationParameters="notEmpty@Can not be empty;limit=30@Max. 30 characters;matchesPattern=^\d{3}-\d{4}-\d{5}$@Doesn't match pattern" />



// takes a form, checks for any "validateable" elements and, if any are found, validates them
// returns true if all are valid
// returns an array of objects if any are invalid. The array will contain one object for each validateable field and has the following format:
//   {element: <the validateable element>, failureMessages: <an array of error messages>} NOTE: if element is valid, failureMessages will be null 
function validateForm(form)
{
	var isValid = true;
	var failures = new Array();
	var validateables = dojo.query(".validateable");
	
	if (validateables != null && validateables.length > 0)
	{
		var validations = getAllValidations(validateables);
		if (validations != null && validations.length > 0)
		{
			for (var i=0; i<validations.length; i++)
			{
				if (validations[i] != null && validations[i].parameters.length > 0)
				{
					var results = validations[i].isValid(); 
					if (results != true)
					{
						isValid = false;
						failures.push(results);
					}
					else
					{
						failures.push({element: validations[i].element, failureMessages: null});
					}
				}
			}
		
			if (isValid == false && failures.length > 0)
			{
				return failures;
			}
		}
	}
	
	return isValid;
}

// validates a single validateable element and either returns true if valid, or an object {element: <element>, failureMessages: <messages[]>} if not valid
function validateField(element)
{
	var isValid = true;
	var failures = new Array();
	var results = null;
	
	var validations = getValidationsForElement(element);
	if (validations != null && validations.parameters.length > 0)
	{
		results = validations.isValid(); 
		if (results != true)
		{
			isValid = false;
		}
		
		if (isValid == false && results != null)
		{
			return results;
		}
	}

	return isValid;
}

// takes an array of validateable elements and parses the validationParameters for each one
function getAllValidations(validateables)
{
	var validations = new Array();
	for (var i=0; i<validateables.length; i++)
	{
		var elementValidations = getValidationsForElement(validateables[i]);
		if (elementValidations != null && elementValidations.parameters.length > 0)
		{
			validations.push(elementValidations);
		}
	}
	return validations;
}

// parses the validationParameters for a single element
function getValidationsForElement(element)
{
	var validationParameters = new Array();
	var validationParametersString = element.getAttribute("validationParameters");
	if (validationParametersString != null && validationParametersString != "")
	{
		var validationParts = validationParametersString.split(";");
		if (validationParts != null && validationParts.length > 0)
		{
			for (var i=0; i<validationParts.length; i++)
			{
				var validationParameter = new ValidationParameter(validationParts[i]);
				if (validationParameter != null)
				{
					validationParameters.push(validationParameter);
				}
			}
		}
	}
	if (validationParameters.length > 0)
	{
		return new Validation(element, validationParameters);
	}
	return null;
}

// an object to hold the validation info for an element, also provides the isValid() function
function Validation(element, parameters)
{
	this.element = element;
	this.parameters = parameters;
	
	this.getElementValue = function()
	{
		if (this.element == null || this.element.value == null)
		{
			return null;
		}
		else
		{
			return trim(this.element.value);
		}
	}
	
	this.isValid = function()
	{
		var isValid = true;
		var value = this.getElementValue();
		var failureMessages = new Array();
		var results = {element: this.element, failureMessages: null};
		
		if (parameters == null || parameters.length == 0)
		{
			return true;
		}
		
		for (var i=0; i<parameters.length; i++)
		{
			var parameterIsValid = true;
	
			// new validation "types" can easily be added here
			if (parameters[i].type == "notEmpty")
			{
				if (value == null || value == "")
				{
					parameterIsValid = false;
				}
			}
			else if (parameters[i].type == "limit" && parameters[i].typeValue != null)
			{
				var limit = parseInt(parameters[i].typeValue);
				if (value.length > limit)
				{
					parameterIsValid = false;
				}
			}
			else if ((parameters[i].type == "matchesPattern" || parameters[i].type == "notMatchesPattern") && parameters[i].typeValue != null)
			{
				var regex = new RegExp(parameters[i].typeValue, "i");
				if (regex == null)
				{
					parameterIsValid = false;
				}
				var matches = (value.match(regex) != null);
				if (parameters[i].type == "matchesPattern" && matches == false)
				{
					parameterIsValid = false;
				}
				else if (parameters[i].type == "notMatchesPattern" && matches == true)
				{
					parameterIsValid = false;
				}
			}
			if (parameterIsValid == false)
			{
				isValid = false;
				failureMessages.push(parameters[i].failMessage);
			}
		}
		if (failureMessages.length > 0)
		{
			results.failureMessages = failureMessages;
		}
		
		return (isValid == true ? isValid : results);
	}
}

// an object to hold the information for one validation type
function ValidationParameter(paramStr)
{
	this.type = null;			// notEmpty, limit, matchesPattern, etc...
	this.typeValue = null;		// if type is, for example, limit type value holds what the limit is, i.e.: "limit=30" type is "limit", typeValue is "30"
	this.failMessage = null;	// the message to display if this validation fails
	
	if (paramStr != null && paramStr != "" && paramStr.indexOf("@") > 0)
	{
		this.type = paramStr.split("@")[0];
		if (this.type.indexOf("=") > 0)
		{
			this.typeValue = this.type.split("=")[1];
			this.type = this.type.split("=")[0];
		}
		
		this.failMessage = paramStr.split("@")[1];
		return this;
	}
	else
	{
		return null;
	}
}

// simple function to trim leading/trailing spaces
function trim(str)
{
	if (typeof(str) != "string")
	{
		return str;
	}
	str = str.replace(/^[\s]+/, ""); //trim leading spaces
	return str.replace(/[\s]+$/, ""); //trim trailing spaces
}
