﻿/**
 * Validate.js: unobtrusive HTML form validation.
 *
 * On document load, this module scans the document for HTML forms and
 * textfield form elements.  If it finds elements that have a "required" or 
 * "pattern" attribute, it adds appropriate event handlers for client-side 
 * form validation.
 *
 * If a form element has a "pattern" attribute, the value of that attribute
 * is used as a JavaScript regular expression, and the element is given an
 * onchange event handler that tests the user's input against the pattern.
 * If the input does not match the pattern, the background color of the
 * input element is changed to bring the error to the user's attention.
 * By default, the textfield value must contain some substring that matches
 * the pattern. If you want to require the complete value to match precisely,
 * use the ^ and $ anchors at the beginning and end of the pattern.
 *
 * A form element with a "required" attribute must have a value provided.
 * The presence of "required" is shorthand for pattern="\S".  That is, it 
 * simply requires that the value contain a single non-whitespace character
 *
 * If a form element passes validation, its "class" attribute is set to 
 * "valid".  And if it fails validation, its class is set to "invalid".
 * In order for this module to be useful, you must use it in conjunction with
 * a CSS stylesheet that defines styles for "invalid" class.  For example:
 *
 *    <!-- attention grabbing orange background for invalid form elements -->
 *    <style>input.invalid { background: #fa0; }</style>
 *
 * When a form is submitted the textfield elements subject to validation are
 * revalidated.  If any fail, the submission is blocked and a dialog box
 * is displayed to the user letting him know that the form is incomplete
 * or incorrect.
 *
 * You may not use this module to validate any form fields or forms on which
 * you define your own onchange or onsubmit event handlers, or any fields
 * for which you define a class attribute.
 *
 * This module places all its code within an anonymous function and does
 * not define any symbols in the global namespace.
 */
(function()
{ // Do everything in this one anonymous function
	// When the document finishes loading, call init()
	if (window.addEventListener)
		window.addEventListener("load", init, false);
	else
		if (window.attachEvent)
			window.attachEvent("onload", init);

	// Define event handlers for any forms and form elements that need them.
	function init()
	{
		// Loop through all forms in the document
		for(var i = 0; i < document.forms.length; i++)
		{
			var f = document.forms[i];  // the form we're working on now
			// Assume, for now, that this form does not need any validation
			var needsValidation = false;
			
			// Now loop through the elements in our form
			for( j = 0; j < f.elements.length; j++)
			{
				var e = f.elements[j];  // the element we're working on
				
				// We're only interested in <input type="text"> textfields
				if (e.type != "text")
					continue;
				
				// See if it has attributes that require validation
				var pattern = e.getAttribute("pattern");
				// We could use e.hasAttribute() but IE doesn't support it
				var required = e.getAttribute("required") != null;
				var minimumAge = e.getAttribute("minimumAge") != null;
				
				// Required is just a shortcut for a simple pattern
				if (required && !pattern)
				{
					pattern = "\\S";
					e.setAttribute("pattern", pattern);
				}
				
				// If this element requires validation,
				if (pattern || minimumAge)
				{
					// validate the element each time it changes
					e.onchange = validateOnChange;
					// Remember to add an onsubmit handler to this form
					needsValidation = true;
				}
			}

			// If at least one of the form elements needed validation,
			// we also need an onsubmit event handler for the form
			if (needsValidation)
				f.onsubmit = validateOnSubmit;
		}
	}
	      
	// This function is the onchange event handler for textfields that 
	// require validation.  Remember that we converted the required attribute
	// to a pattern attribute in init().
	function validateOnChange()
	{
		var textfield = this; // the textfield
		var pattern = textfield.getAttribute("pattern"); // the pattern
		var value = this.value; // the user's input

		// If the value does not match the pattern set the class to "invalid".
		if (value.search(pattern) == -1)
			textfield.className = "invalid";
		else
			textfield.className = "valid";
			
	}

	// This function is the onsubmit event handler for any form that 
	// requires validation.
	function validateOnSubmit()
	{
		// When the form is submitted, we revalidate all the fields in the
		// form and then check their classNames to see if they are invalid.
		// If any of those fields are invalid, display an alert and prevent
		// form submission.
		var invalid = false;  // Start by assuming everything is valid
		// Loop through all form elements
		for(var i = 0; i < this.elements.length; i++)
		{
			var e = this.elements[i];
		
			// If the element is a text field and has our onchange handler
			if (e.type == "text" && e.onchange == validateOnChange)
			{
				e.onchange(); // Invoke the handler to re-validate
				// If validation fails for the element, it fails for the form
				if (e.className == "invalid")
					invalid = true;
			}
			
			var minimumAge = e.getAttribute("minimumAge");
			if (minimumAge != null)
			{
				var now = new Date();
				var minimumDate = new Date(now.getFullYear() - minimumAge, now.getMonth(), now.getDay(), 0, 0, 0, 0);
				var chunks = e.value.split('/');
				var birthday = new Date(chunks[2], chunks[0], chunks[1], 0, 0, 0, 0);
				if (birthday > minimumDate)
				{
					alert("You must be " + minimumAge + " years of age in order to enter.");
					return false;
				}
			}
		}

		// If the form is invalid, alert user and block submission
		if (invalid)
		{
			alert("The form is incompletely or incorrectly filled out.\n" +
						"Please correct the highlighted fields and try again.");
			return false;
		}
	}
})();


