/***************************************************************************************************
*
*-- Form validation script by Peter Bailey, Copyright (c) 2001-2002
*	Version 5.00b
*	Updated on March 18, 2002
*	www.peterbailey.net
*	me@peterbailey.net
*
*	IF YOU USE THIS SCRIPT, GIVE ME CREDIT PLEASE =)
*
*	Visit http://www.peterbailey.net/fValidate for more info
*
*	Please contact me with any questions, comments, problems, or suggestions
*
*	Note: This document most easily read with tab spacing set to 4
*
*******************************************************************************************************/

// Main validation routine
function validateForm( f, bConfirm, bDisable, bDisableR, errorType, groupError )
{

	// Init vars and fValidate object
	var params, fvCode, type;
	var fv = new fValidate( f, errorType, Boolean( groupError ) );
	
	// Loop through all form elements	
	var elem, i = 0;
	while ( elem = f.elements[i++] )
	{
		// Does element have validator attribute?
		if ( fvCode = elem.getAttribute( fv.config.code ) )
		{
			// Set params, validation type, and pass state
			params      = fvCode.split( "|" );
			type        = params[0];
			fv.elemPass = true;
			
			// Valid validator type?
			if ( typeof fv[type] == 'undefined' )
			{
				fv.devError( "The validator '" + type + "' was not found.\nRequested by: " + elem.name );
				return false;
			}
			
			// Bok requested?
			if ( params.last() == 'bok' )
			{
				// Trim type and bok from params
				params = params.reduce( 1, 1 );
				elem.bok = true;
			}
			else
			{
				// Trim bok from params
				params = params.reduce( 1, 0 );
			}
			
			// Is element an array?
			if ( /radio|checkbox/.test( elem.type ) )
			{
				// Set group property
				elem.group = f.elements[elem.name];
			}
			
			// Set formatted name, current element
			elem.fName    = elem.name.format();
			fv.elem       = elem;
			
			// If element is an array
			//
			//if ( typeof elem.group != 'undefined' )
			//{
				//for ( var j = 0; j < elem.group.length; j++ )				
				//{
					// Apply event-function to each child
					//elem.group.item( j ).onchange = function() { fv.revertError( this ) }
				//}
			//}
			//else
			//{
				// Apply event-function to element
				//elem.onchange = function() { fv.revertError( this ) }
			//}
			//
			// Create function to call the proper validator method of the fValidate class
			var func = new Function( "obj", "method", "obj[method]( " + params.toArgString() + " );" );
			func( fv, type );
		
			// If elemen test failed AND group error is off, return false
			if ( !fv.elemPass && !groupError ) return false;			
		}
	} // end of element loop
	
	// If group error, show it
	if ( groupError ) { 
		fv.showGroupError(); 
		// Focus and select elements, if possible
		if ( fv.errors.length > 0 ) {
			if ( typeof fv.errors[0]["elem"].select != 'undefined' ) fv.errors[0]["elem"].select();
			if ( typeof fv.errors[0]["elem"].focus != 'undefined' )  fv.errors[0]["elem"].focus();
		}
	}
	
	

	// Return false if errors found
	if ( fv.errors.length > 0 ) return false;

	// Show pre-submission confirmation
	if ( bConfirm && !confirm( fv.config.confirmMsg ) )
	{
		if ( fv.config.confirmAbortMsg != '' ) alert( fv.config.confirmAbortMsg );
		return false;
	}
	
	// Disable reset and/or submit buttons if requested
	if ( bDisable ) 
	{
		if ( typeof fv.config.submitButton == 'object' )
		{
			for ( var sb, j = 0; ( sb = fv.config.submitButton[j] ); j++ )
			{
				if ( typeof f.elements[sb] != 'undefined' )
				{
					f.elements[sb].disabled = true;
				}
			}
		} else
		{
			f.elements[fv.config.submitButton].disabled = true;
		}
	}
	if ( bDisableR ) 
	{
		if ( typeof fv.config.resetButton == 'object' )
		{
			for ( var sb, j = 0; ( sb = fv.config.resetButton[j] ); j++ )
			{
				if ( typeof f.elements[sb] != 'undefined' )
				{
					f.elements[sb].disabled = true;
				}
			}
		} else
		{
			if (typeof f.elements[fv.config.resetButton] != 'undefined') {
				f.elements[fv.config.resetButton].disabled = true;
			}
		}
	}

	// For validated and all options exercised - allow submission
	return true;
}

// Constructor
function fValidate( f, errorType, groupError )
{
	var self        = this;
	this.form       = f;
	this.errorType  = errorType;
	this.groupError = groupError;
	this.errors     = new Array();
	this.validated  = true;
	this.config     = new fValConfig();
	this.elemPass   = false;
	
	// Add reset action to clear visual error cues
	f.onreset = function()
	{
		var elem, i = 0;
		while ( elem = this.elements[i++] )
		{
			self.revertError( elem );
		}
	}
	
	addLabelProperties();
	
	// Parses form and adds label properties to elements that have one specified
	function addLabelProperties()
	{
		// Collect all label elements in form, init vars		
		if ( typeof f.getElementsByTagName == 'undefined' ) return;
		var labels = f.getElementsByTagName( "label" );
		var label, i = j = 0;
		var elem;

		// Loop through labels retrieved
		while ( label = labels[i++] )
		{
			// For Opera 6
			if ( typeof label.htmlFor == 'undefined' ) return;
			
			// Retrieve element
			elem = f.elements[label.htmlFor];
			if ( typeof elem == 'undefined' )
			{ // No element found for label
				// self.devError( "No element found for label: " + label.htmlFor );
			}
			else if ( typeof elem.length != 'undefined' && elem.length > 1 )
			{ // For arrayed elements
				for ( j = 0; j < elem.length; j++ )
				{
					elem.item( j ).label = label;
				}
			}
			else
			{ // Regular label
				f.elements[label.htmlFor].label = label;
			}
		}
	}		
}

// Receives error message and determines action
fValidate.prototype.throwError = function( error )
{
	var rtrn   = false;
	var elem  = this.elem;
	
	// Arrayed element?
	if ( typeof elem.name == 'undefined' )
	{
		elem = elem[0];
	}

	// Bok requested AND element blank?
	if ( elem.bok && this.isBlank() ) // bok check
	{ // skip
		return true;
	}
	
	// Retrieve error message, set failsafe to false
	var emsg = ( elem.getAttribute( this.config.emsg ) ) ? elem.getAttribute( this.config.emsg ) : error;
	this.elemPass = false;
	
	// Group error mode?
	if ( this.groupError )
	{
		// Push error onto stack
		this.errors.push( {'elem':this.elem, 'msg': emsg} );
		//	orig line of code was:
		//  this.errors.push( {'elem':this.elem, 'msg': error} );
		// changed by CAT 4/26/2004 to show custom emsg in grouped errors.

		rtrn = true;
	
	}
	else
	{
		// Process error message
		this.showError( emsg );
		if ( elem.type != "hidden" )
		{
			// Focus and select elements, if possible
			if ( typeof elem.select != 'undefined' ) elem.select();
			if ( typeof elem.focus != 'undefined' )  elem.focus();
		}
	}
	return rtrn;
}

// Shows error message to user
fValidate.prototype.showError = function( emsg, last )
{
	var elem  = this.elem;
	// retrieve label element, if it exists
	var label = ( elem.type == 'hidden' ) ? null : ((typeof elem.label != "undefined")? elem.label : null) || ((elem.length > 1) ? ((typeof elem[0].label != "undefined")? elem[0].label : null) : null  ) || null;
	if ( !this.showErrors ) this.showErrors = new Array();
	var self  = this;
	
	// Determine which error modes to use
	switch( this.errorType )
	{	// Add additional case numbers for your own combinations
		case 0  : alertError(); break;			
		case 1  : inputError(); break;
		case 2  : inputError(); alertError(); break;
		case 3  : labelError(); break;
		case 4  : labelError(); alertError(); break;
		case 5  : labelError(); appendError(); break;
		case 6  : labelError(); appendError(); alertError(); break;
		case 7  : inputError(); labelError(); break;
		case 8  : inputError(); labelError(); appendError(); break;
		case 9  : inputError(); labelError(); alertError(); break;
		case 10 : inputError(); labelError(); appendError(); alertError(); break;
	}
	// Regular alert error
	function alertError()
	{
		if ( self.groupError ) self.showErrors.push( emsg );
		else alert( emsg );
		if ( last ) alert( "The following fields had errors\n\n" + self.showErrors.join( "\n" ) );			
	}
	// Applies class to form element
	function inputError()
	{
		elem.className = self.config.errorClass;
	}
	// Applies class to element's label
	function labelError()
	{
		if ( label == null ) return;
		label.className = self.config.errorClass;
	}
	// Appends error message to element's label
	function appendError()
	{
		if ( label == null || typeof label.innerHTML == 'undefined' ) return;
		if ( typeof label.original == 'undefined' )
			label.original = label.innerHTML;
		label.innerHTML = label.original + " - " + emsg.toHTML();
	}
}

// Developer assistance method - shows error if validator/element-type mismatch
fValidate.prototype.typeMismatch = function( type )
{
	var pats = {
		'text': 'text|password|textarea',
		'ta': 'textarea',
		'hidden': 'hidden',
		's1': 'select-one',
		'sm': 'select-multiple',
		'rg': 'radio',
		'cb': 'checkbox',
		'file': 'file'
		};
	if ( ! new RegExp( pats[type] ).test( this.elem.type ) )
	{
		this.devError( "Validator/Element type mismatch.\n\nElement: " + this.elem.fName + "\nElement type: " + this.elem.type + "\nType required by validator: " + pats[type].replace( /\|/g, " or " ) );
		this.elemPass = false;
		return true;
	}
	return false;
}

// Processes errors in stack for group error mode
fValidate.prototype.showGroupError = function()
{
	for ( var error, i = 0; ( error = this.errors[i] ); i++ )
	{
		this.elem = error.elem;
		this.showError( error.msg, Boolean( i == ( this.errors.length - 1 ) ) );
	}
}

// Reverts any visible error notification upon event
fValidate.prototype.revertError = function( elem )
{
	if ( typeof elem.label != 'undefined' )
	{
		elem.label.className = '';
		elem.label.innerHTML = ( elem.label.original || elem.label.innerHTML );
	}
	elem.className = '';	
}

// Adds error to error stack - group error mode
fValidate.prototype.addError = function( error )
{
	this.errors.push( {'elem':this.elem, 'msg': error} );
}

// Generic argument setting method
fValidate.prototype.setArg = function( arg, def )
{
	return ( typeof arg == 'undefined' || arg == '' || arg == null ) ? def : arg;
}

// Blank checker.  Optional string argument for evaluating element other than current
fValidate.prototype.isBlank = function( el )
{
	elem = this.form.elements[el] || this.elem;
	return Boolean( /^\s*$/.test( elem.value ) );
}

// Throws developer errors
fValidate.prototype.devError = function( msg )
{
	var a = [
		'! WARNING ! -- fValidate developer-assist error\n',
		'----------------------------------------------------------------------------------------------',
		msg,
		'----------------------------------------------------------------------------------------------',
		'\nIf you are not the developer, please contact the website administrator regarding this error.'
		];
	alert( a.join( "\n" ) );
}
// Non-fValidate methods

// Trims b items from the beginning of the array, e items from the end
Array.prototype.reduce = function( b, e )
{
	var a = new Array();
	var count = 0;
	for ( var i = b; i < this.length - e; i++ )
	{
		a[count++] = this[i];
	}
	return a;
}

// Returns array as argument-compatible string
Array.prototype.toArgString = function()
{
	var a = new Array();
	for ( var i = 0; ( arg = this[i] ); i++ )
	{
		a.push( '"' + arg + '"' );
	}
	return a.toString();
}

// Prototype push if missing
if ( typeof Array.push == 'undefined' )
Array.prototype.push = function( val )
{
	this[this.length] = val;
}

// Returns last item of the array
Array.prototype.last = function()
{
	return this[this.length-1];
}

// Removes the follow charaters _[] from an elements name for human-reading
String.prototype.format = function()
{
	return this.replace( /\_/g, " ").replace( /\[|\]/g, "" );
}

// Replaces newline characters with XHTML BR tags
String.prototype.toHTML = function()
{
	return this.replace( /\n/g, "<br />" );
}

// Escapes necessary charactes for string-generated regular expressions
String.prototype.toPattern = function()
{
	//return this;
	return this.replace( /([\.\*\+\{\}\(\)\<\>\^\$\\])/g, "\\$1" );
}

//EOF
