/***************************************************************************************************

*

*-- Form validation script by Peter Bailey, Copyright (c) 2001-2003

*	Version 5.01b

*	Updated on Feb 07, 2004

*	www.peterbailey.net

*	me@peterbailey.net

*

*	IF YOU USE THIS SCRIPT, GIVE ME CREDIT PLEASE =)

*

*	Visit http://www.peterbailey.net/fValidate/ for more info

*

*	Feel free to contact me with any questions, comments, problems, or suggestions

*

*	Note: This document most easily read with tab spacing set to 4

*

*******************************************************************************************************/



/*	Create static fvalidate object

------------------------------------------- */

if ( typeof fvalidate == 'undefined' )

{

	var fvalidate = new Object();

}



/*	Generic event handling

------------------------------------------- */

fvalidate.addEvent = function( obj, evt, fn, useCapture )

{

	if ( typeof obj.attachEvent != 'undefined' )

	{

		obj.attachEvent( "on" + evt, fn );

	}

	else if ( typeof obj.attachEventListener != 'undefined' )

	{

		obj.addEventListener( evt, fn, Boolean( useCapture ) );

	}

}

fvalidate.addEvents = function( obj, evts, fn, useCapture )

{

	var i = 0, evt;

	while( evt = evts[i++] )

	{

		this.addEvent( obj, evt, fn, Boolean( useCapture ) );

	}

}



/*	Main validation routine

------------------------------------------- */

function validateForm( f, bConfirm, bDisable, bDisableR, groupError, errorMode )

{

	//	Set defaults

	bConfirm	= Boolean( bConfirm );

	bDisable	= Boolean( bDisable );

	bDisableR	= Boolean( bDisableR );

	groupError	= Boolean( groupError );

	errorMode	= ( typeof errorMode != 'undefined' ) ? parseInt( errorMode, 10 ) : 0;



	//	Init vars and fValidate object

	var params, fvCode, type;

	if ( typeof f.fv == 'undefined' )

	{

		f.fv = new fValidate( f, errorMode, groupError );

	} else {		

		f.fv._reset();

		f.fv.errorMode = errorMode;

	}

	

	//	Loop through all form elements	

	var elem, i = 0, attr = f.fv.config.code;

	while ( elem = f.elements[i++] )

	{

		//	Skip fieldsets

		if ( elem.nodeName == "FIELDSET" ) continue;



		//	Does element have validator attribute? (short-circuit check)

		fvCode			= ( elem[attr] ) ? elem[attr] : elem.getAttribute( attr );

		if ( !( typeof fvCode == 'undefined' || fvCode == null || fvCode == "" ) )

		{

			//	Set params, validation type, and validation state

			params			= fvCode.split( "|" );

			type			= params[0];

			elem.validated	= true;

			

			//	Valid validator type?

			if ( typeof f.fv[type] == 'undefined' )

			{				

				f.fv.devError( [type, elem.name], 'notFound' );

				return false;

			}

			

			//	Check for modifiers

			switch( params.last() )

			{				

				case 'bok'	:	//	bok requested

					params = params.reduce( 1, 1 );

					elem.bok = true;

					break;

				case 'if'	:	//	Conditional validation requested

					params = params.reduce( 1, 1 );

					elem._if_ = true;

					break;

				case 'then'	:	//	Conditional validation requested

					params = params.reduce( 1, 1 );

					elem._then_ = true;

					break;

				default		:	//	No modifiers

					params = params.reduce( 1, 0 );

				

			}



			//	Is element an array?

			if ( /radio|checkbox/.test( elem.type ) )

			{

				//	Set group property

				elem.group = f.elements[elem.name];

			}

			

			//	Add events if not already added

			if ( typeof elem.fName == 'undefined' )

			{

				//	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

						if ( f.fv.config.clearEvent != null )

						{

						//	fvalidate.addEvent( elem.group.item( j ), fv.config.clearEvent, fv.revertError, false );

							addEvent( elem.group.item( j ), f.fv.config.clearEvent, f.fv, 'revertError', false );

						}

					}

				}

				else

				{

					//	Apply event-function to element

				//	fvalidate.addEvent( elem, fv.config.clearEvent, fv.revertError, false );

					addEvent( elem, f.fv.config.clearEvent, f.fv, 'revertError', false );

				}

			}

			

			//	Set formatted name, current element

			elem.fName	= elem.name.format();

			f.fv.elem		= elem;

			f.fv.type		= type;



			//	Create function to call the proper validator method of the fValidate class

			var func = new Function( "obj", "method", "obj[method]( " + params.toArgString() + " );" );

			func( f.fv, type );

		

			//	If element test failed AND group error is off, return false

			if ( elem.validated == false && groupError == false ) return false;

			

			//	Clear error if field okay

			if ( elem.validated == true ) f.fv.revertError();

		}

	} //	end of element loop

	

	//	If group error, show it

	if ( groupError ) f.fv.showGroupError();



	//	Return false if errors found

	if ( f.fv.errors.length > 0 ) return false;



	//	Show pre-submission confirmation

	if ( bConfirm && !confirm( f.fv.config.confirmMsg ) )

	{

		if ( f.fv.config.confirmAbortMsg != '' ) alert( f.fv.config.confirmAbortMsg );

		return false;

	}

	

	//	Disable reset and/or submit buttons if requested

	if ( bDisable ) 

	{

		if ( typeof f.fv.config.submitButton == 'object' )

		{

			var sb, j = 0;

			while( sb = f.fv.config.submitButton[j++] )

			{

				if ( f.fv.elementExists( sb ) )

				{

					f.elements[sb].disabled = true;

				}

			}

		}

		else if ( f.fv.elementExists( f.fv.config.submitButton ) )

		{

			f.elements[f.fv.config.submitButton].disabled = true;

		}

	}

	if ( bDisableR && f.fv.elementExists( f.fv.config.resetButton ) )

	{

		f.elements[f.fv.config.resetButton].disabled = true;

	}



	//	Successful Validation.  Submit form

	return true;

	

	function addEvent( elem, evt, obj, method, capture )

	{

		var self = elem;

		if ( typeof elem.attachEvent != 'undefined' )

		{

			elem.attachEvent( "on" + evt, function() { obj[method]( self ) } );

		}

		else if ( typeof elem.addEventListener != 'undefined' )

		{

			elem.addEventListener( evt, function() { obj[method]( self ) }, capture );

		}

		else if ( f.fv.config.eventOverride )

		{

			eleme['on' + evt] = function() { obj[method]( self ) };

		}

	}

}



/*	Constructor

------------------------------------------- */

function fValidate( f, errorMode, groupError )

{

	var self        = this;

	this.form       = f;

	this.errorMode  = errorMode;

	this.groupError = groupError;

	this.errors     = new Array();

	this.validated  = true;

	this.config     = new fValConfig();

	this.i18n		= fvalidate.i18n;

	

	//	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( [label.htmlFor], 'noLabel' );

			}

			else if ( typeof elem.label != 'undefined' )

			{	//	label property already added

				continue;

			}

			else if ( typeof elem.length != 'undefined' && elem.length > 1 && elem.nodeName != 'SELECT' )

			{	//	For arrayed elements

				for ( j = 0; j < elem.length; j++ )

				{

					elem.item( j ).label = label;

				}

			}

			//	Regular label

			elem.label = label;

		}

	}		

}



/*	Reset for another validation

------------------------------------------- */

fValidate.prototype._reset = function()

{

	this.errors		= new Array();

	this.showErrors	= new Array();

}



/*	Checks if element exists in form

------------------------------------------- */

fValidate.prototype.elementExists = function( elemName )

{

	return Boolean( typeof this.form.elements[elemName] != 'undefined' );

}



/*	Receives error message and determines action

------------------------------------------- */

fValidate.prototype.throwError = function( args, which )

{

	var elem  = this.elem;



	//	Arrayed element?

	if ( typeof elem.name == 'undefined' )

	{

		elem = elem[0];

	}



	//	Bok requested AND element blank OR conditional validation?

	if ( elem.bok && this.isBlank() )

	{	//	skip		

		elem.validated = true;

		return;

	}



	//	Part of a conditional validation?

	if ( elem.cv )

	{

		return;

	}

	

	//	Set failsafe to false	

	elem.validated = false;



	//	Create error message

	which	= this.setArg( which, 0 );

	args	= this.setArg( args, [] );

	emsgElem = ( typeof this.elem.getAttribute == "undefined" ) ?

			this.elem[0]:

			this.elem;

	if ( emsgElem.getAttribute( this.config.emsg ) )

	{

		var error = emsgElem.getAttribute( this.config.emsg );

	}

	var error = this.translateMessage( args, this.i18n.errors[this.type][which] );



	//	Group error mode?

	if ( this.groupError )

	{

		//	Push error onto stack

		this.errors.push( {'elem':elem, 'msg': error} );		

	}

	else

	{

		//	Process error message		

		this.showError( error, false, emsgElem );



		var focusElem = ( typeof elem.fields != 'undefined' )?

			elem.fields[0]:

			elem;

		

		//	Focus and select elements, if possible

		this.selectFocus( focusElem );

	}

}





/*	Shows error message to user

------------------------------------------- */

fValidate.prototype.showError = function( emsg, last, elem )

{

	//	Set variables

	var self		= this,

		elem		= this.setArg( elem, this.elem ),

		isHidden	= Boolean( elem.type == 'hidden' ),

		label		= ( isHidden ) ? null : elem.label || null,

		emsg		= ( elem.getAttribute( this.config.emsg ) ) ? elem.getAttribute( this.config.emsg ).replace( /\\n/g, "\n" ) : emsg,

		errorClass	= this.config.errorClass,

		singleCSS	= this.config.useSingleClassNames;



	if ( typeof this.showErrors == 'undefined' ) this.showErrors = new Array();	

	

	//	Determine which error modes to use

	switch( this.errorMode )

	{	//	This represents all possible combinations

		case 0  : alertError(); break;

		case 1  : inputError(); break;

		case 2  : labelError(); break;

		case 3  : appendError(); break;

		case 4  : boxError(); break;

		case 5  : inputError(); labelError(); break;

		case 6  : inputError(); appendError(); break;

		case 7  : inputError(); boxError(); break;

		case 8  : inputError(); alertError(); break;

		case 9  : labelError(); appendError(); break;

		case 10 : labelError(); boxError(); break;

		case 11 : labelError(); alertError(); break;

		case 12 : appendError(); boxError(); break;

		case 13 : appendError(); alertError(); break;

		case 14 : boxError(); alertError(); break;

		case 15 : inputError(); labelError(); appendError(); break;

		case 16 : inputError(); labelError(); boxError(); break;

		case 17 : inputError(); labelError(); alertError(); break;

		case 18 : inputError(); appendError(); boxError(); break;

		case 19 : inputError(); appendError(); alertError(); break;

		case 20 : inputError(); boxError(); alertError(); break;

		case 21 : labelError(); appendError(); boxError(); break;

		case 22 : labelError(); appendError(); alertError(); break;

		case 23 : appendError(); boxError(); alertError(); break;

		case 24 : inputError(); labelError(); appendError(); boxError(); break;

		case 25 : inputError(); labelError(); appendError(); alertError(); break;

		case 26 : inputError(); appendError(); boxError(); alertError(); break;

		case 27 : labelError(); appendError(); boxError(); alertError(); break;

		case 28 : inputError(); labelError(); appendError(); boxError(); alertError(); break;		

	}

	//	Regular alert error

	function alertError()

	{

		if ( self.groupError ) self.showErrors.push( emsg );

		else alert( emsg );

		if ( last ) alert( self.i18n.groupAlert + self.showErrors.join( "\n\n- " ) );			

	}

	//	Applies class to form element

	function inputError()

	{

		if ( ( typeof elem.length != 'undefined' && elem.length > 1 && elem.nodeName != 'SELECT' ) || isHidden )

		{

			var subelem, i = 0;

			while( subelem = ( isHidden ) ? elem.fields[i++] : elem.item( i++ ) )			

			{

				if ( subelem.className != '' && singleCSS )

				{

					subelem.revertClass = subelem.className;

					subelem.className = errorClass;

				} else {

					self.addCSSClass( subelem, errorClass );

				}				

			}

		}

		else

		{

			if ( singleCSS )

			{

				elem.revertClass = elem.className;

				elem.className = errorClass;

			} else {

				self.addCSSClass( elem, errorClass );

			}

		}

	}

	//	Applies class to element's label

	function labelError()

	{

		if ( label == null ) return;

		if ( self.config.useSingleClassNames )

		{

			label.className = errorClass;

		} else {

			self.addCSSClass( label, 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();

	}

	//	Appends Error message to pre-defined element

	function boxError()

	{

		if ( typeof self.boxError == 'undefined' ) self.boxError = document.getElementById( self.config.boxError );

		if ( self.boxError == null )

		{			

			self.devError( [self.config.boxError], 'noBox' );

			return;

		}

		if ( typeof self.elem.name == 'undefined' || self.elem.name == "" )

		{

			self.devError( [self.elem[self.config.code]], 'missingName' );

			return;

		}

		var errorId = self.config.boxErrorPrefix + self.elem.name,

			errorElem;

		if ( errorElem = document.getElementById( errorId ) ) // short-circuit

		{

			errorElem.firstChild.nodeValue = emsg.toHTML();

		}

		else

		{

			errorElem = document.createHTMLElement( 'li', { id: errorId, 'innerHTML': emsg.toHTML(), title: self.i18n.boxToolTip } );

			self.boxError.appendChild( errorElem );

			errorElem.onclick = function()

			{

				var elem = self.form.elements[this.id.replace( self.config.boxErrorPrefix, "" )];

				if ( typeof elem.fields != 'undefined' ) elem = elem.fields[0];

				if ( typeof elem.select != 'undefined' ) elem.select();

				if ( typeof elem.focus != 'undefined' ) elem.focus();

			}

		}

		self.boxError.style.display = "block";

	}

}



/*	Handles element className manipulation

------------------------------------------- */

fValidate.prototype.removeCSSClass = function( elem, className )

{

	elem.className = elem.className.replace( className, "" ).trim();

}

fValidate.prototype.addCSSClass = function( elem, className )

{

	this.removeCSSClass( elem, className );

	elem.className = ( elem.className + " " + className ).trim();

}



/*	Processes errors in stack for group error mode

------------------------------------------- */

fValidate.prototype.showGroupError = function()

{

	for ( var error, firstElem, i = 0; ( error = this.errors[i] ); i++ )

	{

		if ( i == 0 ) firstElem = error.elem;

		this.elem = error.elem;

		this.showError( error.msg, Boolean( i == ( this.errors.length - 1 ) ) );

	}

	var focusElem = ( typeof firstElem.fields != 'undefined' )?

		firstElem.fields[0]:

		firstElem;

	this.selectFocus( focusElem );

}



/*	Reverts any visible error notification upon event

------------------------------------------- */

fValidate.prototype.revertError = function( elem )

{

	elem = this.setArg( elem, this.elem );

	var isHidden	= Boolean( elem.type == 'hidden' ),

		errorClass	= this.config.errorClass,

		i			= 0,

		errorElem,

		subelem;



	if ( ( typeof elem.length != 'undefined' && elem.length > 1 && elem.nodeName != 'SELECT' ) || isHidden )

	{

		if ( isHidden && typeof elem.fields != 'undefined' )

		{		

			while( subelem = ( isHidden ) ? elem.fields[i++] : elem.item( i++ ) )		

			{

				if ( typeof subelem.revertClass != 'undefined' )

				{

					subelem.className = subelem.revertClass;

				}

			}

		}

	} else {

		if ( this.config.useSingleClassNames )

		{

			if ( typeof subElement.revertClass != 'undefined' )

			{

				elem.className = elem.revertClass;

			}

		} else {

			this.removeCSSClass( elem, errorClass );

		}		

	}

	if ( typeof elem.label != 'undefined' )

	{

		if ( this.config.useSingleClassNames )

		{

			elem.label.className = '';

		} else {

			this.removeCSSClass( elem.label, errorClass );

		}

		elem.label.innerHTML = ( elem.label.original || elem.label.innerHTML );

	}

	if ( typeof this.boxError != 'undefined' )

	{

		if ( typeof this.boxError.normalize != 'undefined' ) this.boxError.normalize();

		if ( errorElem = document.getElementById( this.config.boxErrorPrefix + elem.name ) )

		{

			this.boxError.removeChild( errorElem );

		}

		if ( this.boxError.childNodes.length == 0 ) this.boxError.style.display = "none";

	}

}



/*	Focus and select elements, if possible

------------------------------------------- */

fValidate.prototype.selectFocus = function( elem )

{

	if ( typeof elem.select != 'undefined' ) elem.select();

	if ( typeof elem.focus != 'undefined' )  elem.focus();

}



/*	Developer assistance method - shows error if validator/element-type mismatch

------------------------------------------- */

fValidate.prototype.typeMismatch = function()

{

	var pats = {

		'text':		'text|password|textarea',

		'ta':		'textarea',

		'hidden':	'hidden',

		's1':		'select-one',

		'sm':		'select-multiple',

		'select':	'select-one|select-multiple',

		'rg':		'radio',

		'radio':	'radio',

		'cb':		'checkbox',

		'file':		'file'

		};

	var fail		= false,

		expected	= new Array(),

		result = key = type = regex = "";

	for ( var i = 0; i < arguments.length; i++ )

	{

		type	= pats[arguments[i]];

		regex	= new RegExp( type );

		result	+= ( regex.test( this.elem.type ) ) ? "1" : "0";

		key		+= "0";

		expected.push( type );		

	}

	if ( key ^ result == 0 )

	{

		this.devError( [this.elem.fName, this.elem.type, expected.join( "|" ).replace( /\|/g, this.i18n.or )], 'mismatch' );

		this.elem.validated = false;

		return true;

	}

	return false;

}



/*	Returns value(s) of reference element passed

------------------------------------------- */

fValidate.prototype.getValue = function( elem )

{

	switch ( elem.type )

	{

		case 'text' :

		case 'password' :

		case 'textarea' :

		case 'hidden' :

		case 'file' :

			return elem.value;

		case 'radio':

		case 'select-single':

			if ( typeof elem.length == 'undefined' )

			{

				return elem.value;

			} else {

				for ( var i = 0; i < elem.length; i++ )

				{

					choice = ( elem.type == 'radio' ) ? "checked" : "selected";

					if ( elem[i][choice] )

					{

						return elem[i].value;

					}

				}

			}

		case 'select-multiple' :

		case 'checkbox' :

			if ( typeof elem.length == 'undefined' )

			{

				return elem.value

			} else {

				var returnValues = new Array();

				for ( var i = 0; i < elem.length; i++ )

				{

					choice = ( elem.type == 'checkbox' ) ? "checked" : "selected";

					if ( elem[i][choice] )

					{

						returnValues.push( elem[i].value );

					}

				}

				return returnValues;

			}

		default: return null;

	}

}



/*	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 )

{

	var elem = this.form.elements[el] || this.elem;

	return Boolean( /^\s*$/.test( elem.value ) );

}



/*	Translates messages using language file

------------------------------------------- */

fValidate.prototype.translateMessage = function( args, format )

{

	var msg		= ""

	for ( var i = 0; i < format.length; i++ )

	{			

			msg += ( typeof format[i] == 'number' ) ? args[format[i]] : format[i];

	}

	return msg;

}



/*	Throws developer errors

------------------------------------------- */

fValidate.prototype.devError = function( args, which )

{

	if ( typeof args == 'string' )

	{

		which = args;

		args = [];

	}

	which = this.setArg( which, this.type );

	var format = this.i18n.devErrors[which];

	var a = [

		this.i18n.devErrors.lines[0],

		'----------------------------------------------------------------------------------------------',

		this.translateMessage( args, format ),

		'----------------------------------------------------------------------------------------------',

		this.i18n.devErrors.lines[1]

		];

	alert( a.join( "\n" ) );

}



/*	Throws specific developer error

------------------------------------------- */

fValidate.prototype.paramError = function( param, elemName )

{

	elemName = this.setArg( elemName, this.elem.name );

	this.devError( [param, this.type, elemName], 'paramError' );

}

/* Non-fValidate methods *****************************************/



/*	For easy creation of DOM nodes

------------------------------------------- */

document.createHTMLElement = function( elemName, attribs )

{

	if ( typeof document.createElement == 'undefined' ) return;

	var elem = document.createElement( elemName );

	if ( typeof attribs != 'undefined' )

	{

		for ( var i in attribs )

		{

			switch ( true )

			{

				case ( i == 'text' )  : elem.appendChild( document.createTextNode( attribs[i] ) ); break;

				case ( i == 'class' ) : elem.className = attribs[i]; break;

				default : elem.setAttribute( i, '' ); elem[i] = attribs[i];

			}

		}

	}

	return elem;    

}



/*	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; i < this.length; i++ )

	{

		a.push( "'" + this[i] + "'" );

	}	

	return a.toString();

}



/*	Prototype push if missing

------------------------------------------- */

if ( typeof Array.push == 'undefined' )

Array.prototype.push = function()

{

	var arg, i = 0;

	while( arg = arguments[i++] )

	{

		this[this.length] = arg;

	}

	return this.length;

}



/*	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 />" ).replace( /\t/g, "&nbsp;&nbsp;&nbsp;&nbsp;" );

}



/*	Trims leading and trailing whitespace from string

------------------------------------------- */

String.prototype.trim = function()

{

	return this.replace( /^\s+|\s+$/, "" );

}



/*	Escapes necessary charactes for string-generated regular expressions

------------------------------------------- */

String.prototype.toPattern = function()

{

	return this.replace( /([\.\*\+\{\}\(\)\<\>\^\$\\])/g, "\\$1" );

}

//	EOF