//------------------------------------------------------------------------
// Name: LW_Form_Validator
// Desc: Exploses methods to validate the input parameters passed in to a
//       form.
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Some Global Form_Validator Variables
//------------------------------------------------------------------------
LW_Form_Validator.validators = Object();


//------------------------------------------------------------------------
// Name: Form_Validator
// Desc: Class constructor.
//------------------------------------------------------------------------
function LW_Form_Validator( form, options_object )
{

	// Add this object to a global validator array.
	LW_Form_Validator.validators[form.id] = this;

	this.form                  = form;
	this.input_elements        = new Array();
	this.groups                = new Array();

	// Store the parameters.
	this.submit_button         = options_object.submit_button;
	this.submit_form           = (options_object.submit_form == null) ? false : true;
	this.processing_message    = options_object.processing_message;
	this.success_message       = options_object.success_message;
	this.request_url           = options_object.request_url;
	this.redirect_url          = options_object.redirect_url;
	this.modified_request_url  = "";
	this.alert_errors          = options_object.alert_errors;
	this.errors                = new Array();

	this.processing            = false;

	// Add on onsubmit event for the form.
  LW_Events_Handler.addEvent( this.form, "onsubmit", new Function( "return LW_Form_Validator.validators['" + this.form.id + "'].validateForm();" ) );

	// If there is a processing message, create the container for it.
	if ( this.processing_message != null || this.success_message != null )
	{

		this.message_container = LW_DOM_Library.createContainer( LW_DOM_Library.CREATE_HTML,
																													   "",
																													   { width: 250 },
																													   { top: 10 },
																													   { halign: "center" } );

		LW_DOM_Library.setStyle( this.message_container, "visibility", "hidden" );

		this.message_container.className = "form_message_container";

	}  // End if processing message not null.

}


//----------------------------------------------------------------------
// Name: addElement()
// Desc: Add an input element.
//----------------------------------------------------------------------
LW_Form_Validator.prototype.addElement = function( object )
{

	// Create an input element object.
	var input_element = { index:             this.input_elements.length,
												id:                object.id,
												name:              object.name,
												validate:          (object.validate == null) ? true : false,
												descriptive_name:  object.descriptive_name,
												maxlength:         object.maxlength,
												minlength:         object.minlength,
												rangelength:       object.rangelength,
												length:            object.length,
												compare:           object.compare,
												email:             object.email,
												regex:             object.regex,
												alpha:             object.alpha,
												numeric:           object.numeric,
												alphanumeric:      object.alphanumeric,
												nonzero:           object.nonzero };

	// Store the input element.
	this.input_elements[input_element.index] = input_element;

}


//----------------------------------------------------------------------
// Name: addGroup()
// Desc: Add a group.
//----------------------------------------------------------------------
LW_Form_Validator.prototype.addGroup = function( group_id, descriptive_name, elements )
{

	// Create a group object.
	var group = { index:             this.groups.length,
								id:                group_id,
								descriptive_name:  descriptive_name,
								elements:          elements };

	// Store the group.
	this.groups[group.index] = group;

}


//----------------------------------------------------------------------
// Name: validateForm()
// Desc: Start the validation.
//----------------------------------------------------------------------
LW_Form_Validator.prototype.validateForm = function()
{

	var errors = false;

	// Are we processing?
	if ( this.processing == true )
		return false;
	else
		this.processing = true;

	// Disable the submit button.
	if ( this.submit_button != null ) this.submit_button.disabled = true;

	// First if there is a processing message, show it.
	if ( this.processing_message != null )
	{

		this.message_container.innerHTML = this.processing_message;
		LW_DOM_Library.setStyle( this.message_container, "visibility", "visible" );

	}

	// First make sure the form is clean.
	this.cleanupForm();

	// Call the form's onprevalidate function.
	if ( this.form['onprevalidate'] != null )
	  this.form['onprevalidate']();

	// Loop through all the input elements.
	for ( var i = 0; i < this.input_elements.length; i++ )
	{

		var input_element = this.input_elements[i];

		// Do we validate?
		if ( !input_element.validate ) continue;

		var html_element = document.getElementById( input_element.id );

		// Type of element...
		if ( html_element.type == "select-multiple" )
		{

			var return_val = this.validateSelectMultiple( i );

			if ( !return_val )
			  errors = true;

		}  // End if type select-multiple.
		else if ( html_element.type == "text" || html_element.type == "password" || html_element.type == "file" || html_element.type == "textarea" )
		{

			var return_val = this.validateOther( i );

			if ( !return_val )
			  errors = true;

		}  // End if other.

	}  // Next input element.

	// Loop through all the groups.
	for ( var i = 0; i < this.groups.length; i++ )
	{

		var group = this.groups[i];

		// Get the first element of the group.
		var html_element = document.getElementById( group.elements[0] );

		// What type of element?
		if ( html_element.type == "radio" )
		{

			var radio_buttons  = document.getElementsByName( html_element.name );
			var chosen         = false;

			// Loop through each radio button with this name.
			for ( var n = 0; n < radio_buttons.length; n++ )
			{

				var radio_button = radio_buttons[n];

				// Is this radio button checked?
				if ( radio_button.checked )
				  chosen = true;

			}  // Next radio button.

			// If no radio button was checked, post an error.
			if ( !chosen )
			{

				this.postError( radio_buttons[0].id, "You have not selected a choice for " + group.descriptive_name + ". <br /> Please choose one." );
				errors = true;

			}

		}  // End if radio button.
		else if ( html_element.type == "checkbox" )
		{

			var chosen = false;

			// Loop through each group element.
			for ( var n = 0; n < group.elements.length; n++ )
			{

				var html_element = document.getElementById( group.elements[n] );

				// Checked?
				if ( html_element.checked )
				  chosen = true;

			}  // Next group element.

			// If no checkbox was checked, post an error.
			if ( !chosen )
			{

				this.postError( group.elements[0], "You have not selected a choice for " + group.descriptive_name + ". <br /> Please choose one." );
				errors = true;

			}

		}  // End if checkbox.
		else if ( html_element.type == "text" || html_element.type == "password" || html_element.type == "file" || html_element.type == "textarea" )
		{

			var all_filled = true;

			// Loop through each group element.
			for ( var n = 0; n < group.elements.length; n++ )
			{

				var html_element = document.getElementById( group.elements[n] );

				// If not filled in, bad...
				if ( html_element.value.length == 0 )
				  all_filled = false;

			}  // Next group element.

			// If not all the elements in the group were filled out, post an error.
			if ( !all_filled )
			{

				this.postError( group.elements[0], "You must fill out all the fields in " + group.descriptive_name + "." );
				errors = true;

			}

		}  // End if others.

	}  // Next group.

	// Were there any errors?
	if ( errors == true )
	{

		// Do we alert the user, or show it as HTML?
		if ( !this.alert_errors )
		{

		  this.postError( null, "There were errors while processing the form. Please fix them and try submitting the form again." );

		}  // End if show errors as HTML.
		else
		{

			// Show the first error as an alert.
			alert( this.errors[0] );

			// Empty out the errors array.
			this.errors.length = 0;

		}  // End if show errors as an alert.

		// If there is a message container, hide it.
		if ( this.message_container != null )
			LW_DOM_Library.setStyle( this.message_container, "visibility", "hidden" );

		// Set processing to false.
		this.processing = false;

		// Enable the submit button.
		if ( this.submit_button != null ) this.submit_button.disabled = false;

		// Return. We don't want to process any more.
		return false;

	}

	// If there is a request URL, send the request.
	if ( this.request_url != null )
	{

	  var query_string = this.getQueryString();

		// Send the request.
		LW_RequestManager.makeRequest( this.modified_request_url, new Function( "response", "LW_Form_Validator.validators['" + this.form.id + "'].processResponse( response );" ), query_string );

		// Return. We will continue processing in the processResponse() function.
		return false;

	}

	// Finish the validation.
	return this.finishValidation();

}


//----------------------------------------------------------------------
// Name: processResponse()
// Desc: Processes the response from the XHR request.
//----------------------------------------------------------------------
LW_Form_Validator.prototype.processResponse = function( response )
{

	var response = response.responseXML;

	// Hide the message container, if there is one.
	if ( this.message_container != null )
		LW_DOM_Library.setStyle( this.message_container, "visibility", "hidden" );

	// Get the errors.
	var errors = response.getElementsByTagName( "error" );

	// Is there any errors?
	if ( errors.length != 0 )
	{

		// Loop through each error.
		for ( var i = 0; i < errors.length; i++ )
		{

			// Get the error details.
			var id = errors[i].getElementsByTagName( "id" );

			if ( id.length == 0 )
				id = null;
			else
				id = id[0].firstChild.data;

			var message = errors[i].getElementsByTagName( "message" )[0].firstChild.data;

			// Post the error.
			this.postError( id, message );

		}  // Next error.

		// Should we alert the errors?
		if ( this.alert_errors )
		{

		  // Show the first error as an alert.
		  alert( this.errors[0] );

		  // Empty out the errors array.
		  this.errors.length = 0;

		}  // End if alerting errors.

	}  // End if errors.
	else
	{

		// Finish validation.
		LW_Form_Validator.validators[this.form.id].finishValidation();

		// Return.
		return;

	}  // End if no errors.

	// If there was errors, show the generic error message.
	if ( errors.length > 0 && !this.alert_errors ) this.postError( null, "There were errors while processing the form. Please fix them and try submitting the form again." );

	// Set processing to false.
	this.processing = false;

	// Enable the submit button.
	if ( this.submit_button != null ) this.submit_button.disabled = false;

}


//----------------------------------------------------------------------
// Name: finishValidation()
// Desc: Finishes the validation.
//----------------------------------------------------------------------
LW_Form_Validator.prototype.finishValidation = function()
{

	// If we've gotten this far, check for a sucess message, and show it, or else hide it.
	if ( this.success_message != null )
	{
		this.message_container.innerHTML = this.success_message;
		LW_DOM_Library.setStyle( this.message_container, "visibility", "visible" );
	}
	else if ( this.message_container != null )
	{
		LW_DOM_Library.setStyle( this.message_container, "visibility", "hidden" );
	}

	// Set processing to false.
	this.processing = false;

	// Enable the submit button.
	if ( this.submit_button != null ) this.submit_button.disabled = false;

	// Call the form's onfinishvalidation function.
	// This can be set by the user and it will be called here.
	if ( this.form['onfinishvalidation'] != null )
	  this.form['onfinishvalidation']();

	// Check for a redirect URL and if there is one redirect them.
	if ( this.redirect_url != null )
		window.location = this.redirect_url;

	// If the submit form flag is set, submit the form.
	if ( this.submit_form )
		this.form.submit();

	// Else, do not submit the form.
	return false;

}


//----------------------------------------------------------------------
// Name: postError()
// Desc: Posts an error to the form with information about what went
//       wrong and why it went wrong.
// Note: If null is passed in for element_id, the error will be placed
//       at the end of the form.
//----------------------------------------------------------------------
LW_Form_Validator.prototype.postError = function( element_id, error )
{

	// Are we alerting the errors?
	if ( this.alert_errors )
	{

		// Add the error to the errors array and return from the function.
		this.errors.push( error.replace( /<br \/>/ig, "\n" ) );
		return;

	}  // End if alerting errors.

	// Create the error node.
	var error_node = document.createElement( "p" );
	error_node.className = "error";

	// Stick the text into the error node.
	error_node.innerHTML = error;

	// What type of placement?
	if ( element_id == null )
	{

		// Do we have a submit button?
		if ( this.submit_button != null )
		{

		  this.submit_button.parentNode.parentNode.insertBefore( error_node, this.submit_button.parentNode.parentNode.firstChild );

		}  // End if submit button.
		else
		{

			document.getElementById( this.form.id ).appendChild( error_node );

		}  // End if no submit button.

	}  // End if general error message.
	else
	{

		var html_element = document.getElementById( element_id );

		// Group or normal?
		if ( html_element.parentNode.parentNode.className == "group" )
		{

			// Insert the error node before the input element.
			html_element.parentNode.parentNode.insertBefore( error_node, html_element.parentNode.parentNode.firstChild );

		}  // End if group.
		else
		{

			// Insert the error node before the input element.
			html_element.parentNode.insertBefore( error_node, html_element.parentNode.firstChild );

		}  // End if normal.

	}  // End if normal/group error message.

}


//----------------------------------------------------------------------
// Name: validateSelectMultiple()
// Desc: Validates select-multiple input elements.
//----------------------------------------------------------------------
LW_Form_Validator.prototype.validateSelectMultiple = function( element_index )
{

	var input_element  = this.input_elements[element_index];
	var html_element   = document.getElementById( input_element.id );
	var chosen         = 0;
	var errors         = false;

	// Loop through each option.
	for ( var n = 0; n < html_element.options.length; n++ )
	{

		if ( html_element.options[n].selected )
			chosen++;

	}  // Next option.

	// Validate.
	if ( chosen == 0 )
	{

		this.postError( input_element.id, "You have not chosen any options for " + input_element.descriptive_name + ". <br /> Please do so." );
		errors = true;

	}  // Validate.
	else if ( input_element.minlength != null && chosen < input_element.minlength )
	{

		this.postError( input_element.id, "You have not chosen enough options for " + input_element.descriptive_name + ". <br /> You must choose at least " + input_element.minlength + "." );
		errors = true;

	}  // Min options.
	else if ( input_element.maxlength != null && chosen > input_element.maxlength )
	{

		this.postError( input_element.id, "You have chosen too many options for " + input_element.descriptive_name + ". <br /> You can not choose more than " + input_element.maxlength + "." );
		errors = true;

	}  // Max options.
	else if ( input_element.rangelength != null )
	{

		if ( chosen < input_element.rangelength[0] )
		{

			this.postError( input_element.id, "You have not chosen enough options for " + input_element.descriptive_name + ". <br /> You must choose between " + input_element.rangelength[0] + " and " + input_element.rangelength[1] + " options." );
			errors = true;

		}
		else if ( chosen > input_element.rangelength[1] )
		{

			this.postError( input_element.id, "You have chosen too many options for " + input_element.descriptive_name + ". <br /> You must choose between " + input_element.rangelength[0] + " and " + input_element.rangelength[1] + " options." );
			errors = true;

		}

	}  // Range options.
	else if ( input_element.length != null && chosen != input_element.length )
	{

		this.postError( input_element.id, "You have not chosen the correct amount of options for " + input_element.descriptive_name + ". <br /> You must choose " + input_element.length + " option(s)." );
		errors = true;

	}  // Number of options.

	return !errors;

}


//----------------------------------------------------------------------
// Name: validateOther()
// Desc: Validates other input elements.
//----------------------------------------------------------------------
LW_Form_Validator.prototype.validateOther = function( element_index )
{

	var input_element  = this.input_elements[element_index];
	var html_element   = document.getElementById( input_element.id );
	var errors         = false;

	// Validate.
	if ( html_element.value.length == 0 )
	{

		this.postError( input_element.id, "The " + input_element.descriptive_name + " field is empty. <br /> You must fill it in." );
		errors = true;

	}  // Validate.
	else if ( input_element.minlength != null && html_element.value.length < input_element.minlength )
	{

		this.postError( input_element.id, "The " + input_element.descriptive_name + " field is too short. <br /> It must be at least " + input_element.minlength + " characters long." );
		errors = true;

	}  // Min length.
	else if ( input_element.maxlength != null && html_element.value.length > input_element.maxlength )
	{

		this.postError( input_element.id, "The " + input_element.descriptive_name + " field is too long. <br /> It can not be longer than " + input_element.maxlength + " characters." );
		errors = true;

	}  // Max length.
	else if ( input_element.rangelength != null && (html_element.value.length < input_element.rangelength[0] || html_element.value.length > input_element.rangelength[1]) )
	{

		if ( html_element.value.length < input_element.rangelength[0] )
		{

			this.postError( input_element.id, "The " + input_element.descriptive_name + " field is too short. <br /> It must be between " + input_element.rangelength[0] + " and " + input_element.rangelength[1] + " characters long." );
			errors = true;

		}
		else if ( html_element.value.length > input_element.rangelength[1] )
		{

			this.postError( input_element.id, "The " + input_element.descriptive_name + " field is too long. <br /> It must be between " + input_element.rangelength[0] + " and " + input_element.rangelength[1] + " characters long." );
			errors = true;

		}

	}  // Range length.
	else if ( input_element.length != null && html_element.value.length != input_element.length )
	{

		this.postError( input_element.id, "The " + input_element.descriptive_name + " field is not the correct length. <br /> It must be " + input_element.length + " characters long." );
		errors = true;

	}  // Length.
	else if ( input_element.alpha != null && html_element.value.search( /^[a-zA-Z\s\t\n\r]+$/ ) == -1 )
	{

		this.postError( input_element.id, "The " + input_element.descriptive_name + " field can not contain numbers or punctuation characters." );
		errors = true;

	}  // Alpha.
	else if ( input_element.numeric != null && html_element.value.search( /^[\d]+$/ ) == -1 )
	{

		this.postError( input_element.id, "The " + input_element.descriptive_name + " field must be a numerical value." );
		errors = true;

	}  // Numeric.
	else if ( input_element.alphanumeric != null && html_element.value.search( /^[a-zA-Z0-9\s\t\n\r]+$/ ) == -1 )
	{

		this.postError( input_element.id, "The " + input_element.descriptive_name + " field must contain only letters and numbers." );
		errors = true;

	}  // Alpha-numeric.
	else if ( input_element.nonzero != null && html_element.value.search( /^[0\D]*$/ ) != -1 )
	{

		this.postError( input_element.id, "The " + input_element.descriptive_name + " field must be a nonzero numerical value." );
		errors = true;

	}  // Nonzero.
	else if ( input_element.email != null && html_element.type == "text" && html_element.value.search( /^[A-Z0-9._%-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$/i ) == -1 )
	{

		this.postError( input_element.id, "The " + input_element.descriptive_name + " you entered was not in the correct email format." );
		errors = true;

	}  // Email.
	else if ( input_element.regex != null && html_element.value.search( input_element.regex ) == -1 )
	{

		this.postError( input_element.id, "The value you entered in the " + input_element.descriptive_name + " field is not in the correct format." );
		errors = true;

	}  // Regex.
	else if ( input_element.compare != null && html_element.value != document.getElementById( input_element.compare[0] ).value )
	{

		this.postError( input_element.id, "The " + input_element.descriptive_name + " and " + input_element.compare[1] + " fields are not the same." );
		errors = true;

	}  // Compare.

	return !errors;

}


//----------------------------------------------------------------------
// Name: cleanupForm()
// Desc: Cleans up the form to make it ready for form validation.
//----------------------------------------------------------------------
LW_Form_Validator.prototype.cleanupForm = function()
{

	// Get all the forms error's.
	var errors = this.form.getElementsByTagName( "p" );

	// Loop through each error.
	// We get the length before hand, because we take elements away from the
	// array in the loop.
	for ( var i = 0, length = errors.length, index = 0; i < length; i++ )
	{

		// Get the element. We retrieve the 0th element because we remove
		// the child below, and the next one will fall in this place.
		var error_element = errors[index];

		// Is this an error element?
		if ( error_element.getAttribute( "class" ) == "error" )
			error_element.parentNode.removeChild( error_element );  // Remove the element.
		else
			index++; // Increment the index if we have skipped an element.


	}  // Next error node.

}


//----------------------------------------------------------------------
// Name: getQueryString()
// Desc: Concatenates all the managed input elements in to a query
//       string suitable for appending to a URL.
//----------------------------------------------------------------------
LW_Form_Validator.prototype.getQueryString = function()
{

	var values = Array();

	// Loop through each input element in the form.
	for ( var i = 0; i < this.form.elements.length; i++ )
	{

		var element = this.form.elements[i];

		// What type of element?
		switch ( element.type )
		{

		// Simple elements.
	  case 'text':
		case 'password':
		case 'file':
		case 'textarea':
		case 'hidden':
		case 'select-one':

		  values.push( (element.name + "=" + encodeURIComponent( element.value )) );
			break;

		// Checkboxes and radio buttons.
		case 'checkbox':
		case 'radio':

		  // Only add if checked.
			if ( element.checked )
			  values.push( (element.name + "=" + encodeURIComponent( element.value )) );

			break;

		// Select multiples.
		case 'select-multiple':

		  // Loop through each option.
			for ( var n = 0; n < element.options.length; n++ )
			{

				// Only add if option is selected.
				if ( element.options[n].selected )
				  values.push( (element.name + "=" + encodeURIComponent( element.options[n].value )) );

			}  // Next option.

			break;

		}  // End what type of elements.

	}  // Next input element.

	// Put the values together into a string.
	var query_string = values.join( "&" );

	// Get the pieces of the request URL.
	var pieces = this.request_url.match( /(\S*)\?(\S*)/ );

	// If the request_url does not have a ? in it, get rid of the query values from
	// the request URL, and add it to the query string.
	if ( pieces != null )
	{

	  this.modified_request_url = pieces[1];
		query_string              = pieces[2] + "&" + query_string;

	}
	else
	{

		this.modified_request_url = this.request_url;

	}

	// Return the query string.
	return query_string;

}


