/* TED: Ugly up front, but classes are better than arrays IMHO */

/*********************************************************************************
 * Portions of this file are subject to the SugarCRM Public License Version
 * 1.1.3 ("License"); You may not use these portions except in compliance with the
 * License. You may obtain a copy of the License at http://www.sugarcrm.com/SPL
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
 * All Rights Reserved.
 *
 * The portions of this file were taken from sugar.js.
 *
 * The rest of this file is Copyright (C) Ted Milker - Design Data and does not
 * fall under the SugarCRM Public License.
 ********************************************************************************/

DEBUG = false;

/*

Type comparison chart:

			bool		varchar		int			float		email		phone		date		time		enum	radio
bool		1			1			1			1			1			1			1			1			1		1
varchar		1			1			0			0			0			0			0			0			0		0
int			1			0			1			0			0			0			0			0			0		0
float		1			0			0			1			0			0			0			0			0		0
email		1			0			0			0			1			0			0			0			0		0
phone		1			0			0			0			0			1			0			0			0		0
date		1			0			0			0			0			0			1			0			0		0
time		1			0			0			0			0			0			0			1			0		0
enum		1			0			0			0			0			0			0			0			1		0
radio		1			0			0			0			0			0			0			0			0		1
*/

valid_compare = new Array();

valid_compare['bool'] = new Array();
valid_compare['bool']['bool'] = 1;
valid_compare['bool']['varchar'] = 1;
valid_compare['bool']['int'] = 1;
valid_compare['bool']['float'] = 1;
valid_compare['bool']['email'] = 1;
valid_compare['bool']['phone'] = 1;
valid_compare['bool']['date'] = 1;
valid_compare['bool']['time'] = 1;
valid_compare['bool']['enum'] = 1;
valid_compare['bool']['datetime'] = 1;
valid_compare['bool']['radio'] = 1;
valid_compare['bool']['execute'] = 0;

valid_compare['varchar'] = new Array();
valid_compare['varchar']['bool'] = 1;
valid_compare['varchar']['varchar'] = 1;
valid_compare['varchar']['int'] = 0;
valid_compare['varchar']['float'] = 0;
valid_compare['varchar']['email'] = 0;
valid_compare['varchar']['phone'] = 0;
valid_compare['varchar']['date'] = 0;
valid_compare['varchar']['time'] = 0;
valid_compare['varchar']['enum'] = 0;
valid_compare['varchar']['datetime'] = 0;
valid_compare['varchar']['radio'] = 0;
valid_compare['varchar']['execute'] = 0;

valid_compare['int'] = new Array();
valid_compare['int']['bool'] = 1;
valid_compare['int']['varchar'] = 0;
valid_compare['int']['int'] = 1;
valid_compare['int']['float'] = 0;
valid_compare['int']['email'] = 0;
valid_compare['int']['phone'] = 0;
valid_compare['int']['date'] = 0;
valid_compare['int']['time'] = 0;
valid_compare['int']['enum'] = 0;
valid_compare['int']['datetime'] = 0;
valid_compare['int']['radio'] = 0;
valid_compare['int']['execute'] = 0;

valid_compare['float'] = new Array();
valid_compare['float']['bool'] = 1;
valid_compare['float']['varchar'] = 0;
valid_compare['float']['int'] = 0;
valid_compare['float']['float'] = 1;
valid_compare['float']['email'] = 0;
valid_compare['float']['phone'] = 0;
valid_compare['float']['date'] = 0;
valid_compare['float']['time'] = 0;
valid_compare['float']['enum'] = 0;
valid_compare['float']['datetime'] = 0;
valid_compare['float']['radio'] = 0;
valid_compare['float']['execute'] = 0;

valid_compare['email'] = new Array();
valid_compare['email']['bool'] = 1;
valid_compare['email']['varchar'] = 0;
valid_compare['email']['int'] = 0;
valid_compare['email']['float'] = 0;
valid_compare['email']['email'] = 1;
valid_compare['email']['phone'] = 0;
valid_compare['email']['date'] = 0;
valid_compare['email']['time'] = 0;
valid_compare['email']['enum'] = 0;
valid_compare['email']['datetime'] = 0;
valid_compare['email']['radio'] = 0;
valid_compare['email']['execute'] = 0;

valid_compare['phone'] = new Array();
valid_compare['phone']['bool'] = 1;
valid_compare['phone']['varchar'] = 0;
valid_compare['phone']['int'] = 0;
valid_compare['phone']['float'] = 0;
valid_compare['phone']['email'] = 0;
valid_compare['phone']['phone'] = 1;
valid_compare['phone']['date'] = 0;
valid_compare['phone']['time'] = 0;
valid_compare['phone']['enum'] = 0;
valid_compare['phone']['datetime'] = 0;
valid_compare['phone']['radio'] = 0;
valid_compare['phone']['execute'] = 0;

valid_compare['date'] = new Array();
valid_compare['date']['bool'] = 1;
valid_compare['date']['varchar'] = 0;
valid_compare['date']['int'] = 0;
valid_compare['date']['float'] = 0;
valid_compare['date']['email'] = 0;
valid_compare['date']['phone'] = 0;
valid_compare['date']['date'] = 1;
valid_compare['date']['time'] = 0;
valid_compare['date']['enum'] = 0;
valid_compare['date']['datetime'] = 1;
valid_compare['date']['radio'] = 0;
valid_compare['date']['execute'] = 0;

valid_compare['time'] = new Array();
valid_compare['time']['bool'] = 1;
valid_compare['time']['varchar'] = 0;
valid_compare['time']['int'] = 0;
valid_compare['time']['float'] = 0;
valid_compare['time']['email'] = 0;
valid_compare['time']['phone'] = 0;
valid_compare['time']['date'] = 0;
valid_compare['time']['time'] = 1;
valid_compare['time']['enum'] = 0;
valid_compare['time']['datetime'] = 0;
valid_compare['time']['radio'] = 0;
valid_compare['time']['execute'] = 0;

valid_compare['enum'] = new Array();
valid_compare['enum']['bool'] = 1;
valid_compare['enum']['varchar'] = 0;
valid_compare['enum']['int'] = 0;
valid_compare['enum']['float'] = 0;
valid_compare['enum']['email'] = 0;
valid_compare['enum']['phone'] = 0;
valid_compare['enum']['date'] = 0;
valid_compare['enum']['time'] = 0;
valid_compare['enum']['enum'] = 1;
valid_compare['enum']['datetime'] = 0;
valid_compare['enum']['radio'] = 0;
valid_compare['enum']['execute'] = 0;

valid_compare['datetime'] = new Array();
valid_compare['datetime']['bool'] = 1;
valid_compare['datetime']['varchar'] = 0;
valid_compare['datetime']['int'] = 0;
valid_compare['datetime']['float'] = 0;
valid_compare['datetime']['email'] = 0;
valid_compare['datetime']['phone'] = 0;
valid_compare['datetime']['date'] = 1;
valid_compare['datetime']['time'] = 0;
valid_compare['datetime']['enum'] = 0;
valid_compare['datetime']['datetime'] = 1;
valid_compare['datetime']['radio'] = 0;
valid_compare['datetime']['execute'] = 0;

valid_compare['radio'] = new Array();
valid_compare['radio']['bool'] = 1;
valid_compare['radio']['varchar'] = 0;
valid_compare['radio']['int'] = 0;
valid_compare['radio']['float'] = 0;
valid_compare['radio']['email'] = 0;
valid_compare['radio']['phone'] = 0;
valid_compare['radio']['date'] = 1;
valid_compare['radio']['time'] = 0;
valid_compare['radio']['enum'] = 0;
valid_compare['radio']['datetime'] = 0;
valid_compare['radio']['radio'] = 1;
valid_compare['radio']['execute'] = 0;

valid_compare['execute'] = new Array();
valid_compare['execute']['bool'] = 0;
valid_compare['execute']['varchar'] = 0;
valid_compare['execute']['int'] = 0;
valid_compare['execute']['float'] = 0;
valid_compare['execute']['email'] = 0;
valid_compare['execute']['phone'] = 0;
valid_compare['execute']['date'] = 0;
valid_compare['execute']['time'] = 0;
valid_compare['execute']['enum'] = 0;
valid_compare['execute']['datetime'] = 0;
valid_compare['execute']['radio'] = 0;
valid_compare['execute']['execute'] = 0;

aliases = new Array();
aliases['user'] = 'int';

function setVar1( value )
{
	with( this )
	{
 		if( typeof( value ) == 'string' )
		{
			var1 = value;
		}
		else
		{
			valid = false;

			if( DEBUG )
			{
				alert( 'Error: Rule::setVar1() requires a string argument, got '+typeof( value ) );
			}
		}
	}
}

function setVar2( value )
{
	with( this )
	{
		if( typeof( value ) == 'string' )
		{
			var2 = value;
		}
		else
		{
			valid = false;

			if( DEBUG )
			{
				alert( 'Error: Rule::setVar2() requires a string argument, got '+typeof( value ) );
			}
		}
	}
}

function setVtype1( value )
{
	with( this )
	{
		if( typeof( value ) == 'string' )
		{
			if( value.length > 0 )
			{
				vtype1 = value;
			}
			else
			{
				valid = false;

				if ( DEBUG )
				{
					alert( 'Error: Rule::setVtype1 requires a string argument, got an empty string' );
				}
			}
		}
		else
		{
			valid = false;

			if( DEBUG )
			{
				alert( 'Error: Rule::setVtype1 requires a string argument, got '+typeof( value ) );
			}
		}
	}
}

function setVtype2( value )
{
	with( this )
	{
		if( typeof( value ) == 'string' )
		{
			if( value.length > 0 )
			{
				vtype2 = value;
			}
			else
			{
				valid = false;

				if ( DEBUG )
				{
					alert( 'Error: Rule::setVtype2 requires a string argument, got an empty string' );
				}
			}
		}
		else
		{
			valid = false;

			if( DEBUG )
			{
				alert( 'Error: Rule::setVtype2 requires a string argument, got '+typeof( value ) );
			}
		}
	}
}

function setVlabel1( value )
{
	with( this )
	{
		if( typeof( value ) == 'string' )
		{
			vlabel1 = value;
		}
		else
		{
			valid = false;

			if( DEBUG )
			{
				alert( 'Error: Rule::setVlabel1 requires a string argument, got '+typeof( value ) );
			}
		}
	}
}

function setVlabel2( value )
{
	with( this )
	{
		if( typeof( value ) == 'string' )
		{
			vlabel2 = value;
		}
		else
		{
			valid = false;

			if( DEBUG )
			{
				alert( 'Error: Rule::setVlabel2 requires a string argument, got '+typeof( value ) );
			}
		}
	}
}

function setVvalue1( value )
{
	with( this )
	{
		if( typeof( value ) == 'string' )
		{
			vvalue1 = value;
		}
		else
		{
			valid = false;
			
			if( DEBUG )
			{
				alert( 'Error: Rule::setVvalue1 requires a string argument, got '+typeof( value ) );
			}
		}
	}
}

function setVvalue2( value )
{
	with( this )
	{
		if( typeof( value ) == 'string' )
		{
			vvalue2 = value;
		}
		else
		{
			valid = false;
			
			if( DEBUG )
			{
				alert( 'Error: Rule::setVvalue1 requires a string argument, got '+typeof( value ) );
			}
		}
	}
}

function setMessage( value )
{
	with( this )
	{
		if( typeof( value ) == 'string' )
		{
			message = value;
		}
		else
		{
			valid = false;

			if( DEBUG )
			{
				alert( 'Error: Rule::setMessage requires a string argument, got '+typeof( value ) );
			}
		}
	}
}

function set1Var( var1_val, vtype1_val, vlabel1_val )
{
	with( this )
	{
		if( var1_val.length == 0 || typeof( var1_val ) != 'string' )
		{
			alert( 'Error: Rule::set1Var requires a string argument for the first variable' );
			return false;
		}
		else
		{
			setVar1( var1_val );
		}

		// Check to see if this is an aliased type, if so, use the real type for the rule
		if( aliases[vtype1_val] )
		{
			vtype1_val = aliases[vtype1_val];
		}
		
		setVtype1( vtype1_val );
		setVlabel1( vlabel1_val );
	}
}

function set2Vars( var1_val, vtype1_val, vlabel1_val, var2_val, vtype2_val, vlabel2_val )
{
	with( this )
	{
		setVar1( var1_val );
		setVtype1( vtype1_val );
		setVlabel1( vlabel1_val );
		setVar2( var2_val );
		setVtype2( vtype2_val );
		setVlabel2( vlabel2_val );
	}
}

function dump( )
{
	if( DEBUG )
	{
		with( this )
		{
			dumpstr = '';
			dumpstr += 'type: '+type+'\n';
			dumpstr += 'var1: '+var1+'\n';
			dumpstr += 'var2: '+var2+'\n';
			dumpstr += 'vtype1: '+vtype1+'\n';
			dumpstr += 'vtype2: '+vtype2+'\n';
			dumpstr += 'vlabel1: '+vlabel1+'\n';
			dumpstr += 'vlabel2: '+vlabel2+'\n';
			dumpstr += 'message: '+message+'\n';
			dumpstr += 'valid: '+valid+'\n';
			alert( dumpstr );
		}
	}
}

function Rule( type, message )
{
	// member variables
	this.type = type;

	this.var1 = "";
	this.var2 = "";
	this.vtype1 = "";
	this.vtype2 = "";
	this.vlabel1 = "";
	this.vlabel2 = "";
	this.vvalue1 = "";
	this.vvalue2 = "";
	
	this.message = message;

	this.valid = true;

	// member functions
	this.setVar1 = setVar1;
	this.setVar2 = setVar2;
	this.setVtype1 = setVtype1;
	this.setVtype2 = setVtype2;
	this.setVlabel1 = setVlabel1;
	this.setVlabel2 = setVlabel2;
	this.setVvalue1 = setVvalue1;
	this.setVvalue2 = setVvalue2;
	this.setMessage = setMessage;

	// Helper functions
	this.set1Var = set1Var;
	this.set2Vars = set2Vars;
	this.dump = dump;
}

function setForm( value )
{
	with( this )
	{
		form_name = value;
	}
}

function check_var( var1 )
{
	with( this )
	{
		if( var1 )
		{
			if( typeof( eval( "document."+form_name+"."+var1 ) ) == 'undefined' )
			{
				return true;
			}
		}

		return false;
	}
}

function check_type( vtype )
{
	if( vtype )
	{
		if( !valid_compare[vtype] )
		{
			return true;
		}
		else if( !valid_compare[vtype][vtype] )
		{
			alert( 'Serious Error: valid_compare table is corrupt!' );
			return true;
		}
	}

	return false;
}

function addRule( rule )
{
	with( this )
	{
		error = false;
		error_msg = "";

		// Do substitutions to make message legible
		rule.message = rule.message.replace( /_VAR1_/g, rule.vlabel1 );
		rule.message = rule.message.replace( /_VAR2_/g, rule.vlabel2 );

		// Need to verify that all the variables in the rule exist before adding rule
		switch( rule.type )
		{
			// is_set: 1 variable, var1 must be set
			case 'is_set':
				// Make sure variable exists in the form
				error = check_var( rule.var1 );
				if( error )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var1+" does not exist.\n";
					}
				}

				// Make sure the type specified is supported
				error = check_type( rule.vtype1 );
				if( error )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var1+" has an unknown type: "+rule.vtype1+"\n";
					}
				}

				// It doesn't make sense to make a bool(checkbox), user probably wants a mutex or
				// required_if_set rule instead
				if( rule.vtype1 == 'bool' )
				{
					error = true;
					rule.valid = false;
					
					if( DEUBG )
					{
						error_msg += rule.var1+" is a bool, is_set cannot be used with bools\n";
					}
				}
				break;
			// mutex: 2 variables, var1 or var2 set but not both set(both can be unset)
			case 'mutex':
				// Make sure first variable exists in the form
				error = check_var( rule.var1 );
				if( error )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var1+" does not exist.\n";
					}
				}

				// Make sure the first variable's type specified is supported
				error = check_type( rule.vtype1 );
				if( error )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var1+" has an unknown type: "+rule.vtype1+"\n";
					}
				}

				// Make sure second variable exists in the form
				error = check_var( rule.var2 );
				if( error )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var2+" does not exist.\n";
					}
				}

				// Make sure the second variable's type specified is supported
				error = check_type( rule.vtype2 );
				if( error )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var2+" has an unknown type: "+rule.vtype2+"\n";
					}
				}

				if( rule.var1 == rule.var2 )
				{
					rule.valid = false;
					error = true;

					if( DEBUG )
					{
						error_msg += "cannot use the same variable for mutual exclusion\n";
					}
				}
				break;
			// mutex-strict: 2 variables, var1 or var2 set but not both set(one MUST be set)
			case 'mutex-strict':
				// Make sure first variable exists in the form
				error = check_var( rule.var1 );
				if( error )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var1+" does not exist.\n";
					}
				}

				// Make sure the first variable's type specified is supported
				error = check_type( rule.vtype1 );
				if( error )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var1+" has an unknown type: "+rule.vtype1+"\n";
					}
				}

				// Make sure second variable exists in the form
				error = check_var( rule.var2 );
				if( error )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var2+" does not exist.\n";
					}
				}

				// Make sure the second variable's type specified is supported
				error = check_type( rule.vtype2 );
				if( error )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var2+" has an unknown type: "+rule.vtype2+"\n";
					}
				}

				if( rule.var1 == rule.var2 )
				{
					rule.valid = false;
					error = true;

					if( DEBUG )
					{
						error_msg += "cannot use the same variable for mutual exclusion\n";
					}
				}
				break;
			// required_if_set: 2 variables, var1 required if var2 is set
			case 'required_if_set':
				// Make sure first variable exists in the form
				error = check_var( rule.var1 );
				if( error )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var1+" does not exist.\n";
					}
				}

				// Make sure the first variable's type specified is supported
				error = check_type( rule.vtype1 );
				if( error )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var1+" has an unknown type: "+rule.vtype1+"\n";
					}
				}

				// Make sure second variable exists in the form
				error = check_var( rule.var2 );
				if( error )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var2+" does not exist.\n";
					}
				}

				// Make sure the second variable's type specified is supported
				error = check_type( rule.vtype2 );
				if( error )
				{
					rule.valid = false;
					
					if( DEBUG )
					{
						error_msg += "variable "+rule.var2+" has an unknown type: "+rule.vtype2+"\n";
					}
				}

				if( rule.var1 == rule.var2 )
				{
					rule.valid = false;
					error = true;
					
					if( DEBUG )
					{
						error_msg += "cannot use the same variable for required_if_set\n";
					}
				}
				break;
				
			// require_if_set_to: 2 variables, var1 required if var2 is set to a specified value
			case 'required_if_set_to':
				// Make sure first variable exists in the form
				error = check_var( rule.var1 );
				if( error )
				{
					rule.valid = false;
					
					if( DEBUG )
					{
						error_msg += "variable "+rule.var1+" does not exist.\n";
					}
				}
				
				// Make sure the first variable's type specified is supported
				error = check_type( rule.vtype1 );
				if( error )
				{
					rule.valid = false;
					
					if( DEBUG )
					{
						error_msg += "variable "+rule.var1+" has an unknown type: "+rule.vtype1+"\n";
					}
				}
				
				// Make sure the second variable exists in the form
				error = check_var( rule.var2 );
				if( error )
				{
					rule.valid = false;
					
					if( DEBUG )
					{
						error_msg += "variable "+rule.var2+" does not exist.\n";
					}
				}
				
				// Make sure the second variable's type specified is supported
				error = check_type( rule.vtype2 );
				if( error )
				{
					rule.valid = false;
					
					if( DEBUG )
					{
						error_msg += "variable "+rule.var2+" has an unknown type: "+rule.vtype2+"\n";
					}
				}
				
				// Make sure the second variable's value is specified
				if( rule.vvalue2.length <= 0 )
				{
					rule.valid = false;

					if( DEBUG )
					{
						error_msg += "variable "+rule.var2+" does not have a value specified for this rule.\n";
					}
				}
				
				// Make sure they aren't the same variable
				if( rule.var1 == rule.var2 )
				{
					rule.valid = false;
					error = true;
					
					if( DEBUG )
					{
						error_msg += "cannot use the same variable for required_if_set_to\n";
					}
				}
			break;
			
			case 'executeCommand':
				if( typeof window[rule.var1] != 'function' )
				{
					rule.valid = false;
					error = true;
					
					if( DEBUG )
					{
						error_msg += "could not eval() function: " + rule.var1 + "\n";
					}
				}
			break;
			
			default:
				rule.valid = false;
				
				if( DEBUG )
				{
					error_msg += 'Unknown rule type: '+rule.type;
				}
		}

		if( error_msg != "" )
		{
			alert( error_msg );
		}

		if( rule.valid )
		{
			rules[num_rules] = rule;
			num_rules = num_rules + 1;
		}
		else
		{
			if( DEBUG )
			{
				alert( 'addRule called on a rule that has been invalidated due to errors.' );
			}
		}
	}
}

function validate()
{
	with( this )
	{
		overall_pass = true;
		output = "";

		for( var i = 0; i < num_rules; i++ )
		{
			pass = false;

			rule = rules[i];
			
			if( DEBUG )
			{
				alert( 'Processing rule '+rule.type+' '+rule.vlabel1+' '+rule.vlabel2+'\n' );
			}
			//rule.dump();

			switch( rule.type )
			{
				case 'is_set':
					if( rule.vtype1 == 'radio' )
					{
						radio = eval( "document."+form_name+"."+rule.var1 );

						pass = false;
						
						for( j = 0; j < radio.length; j++ )
						{
							if( radio[j].checked )
							{
								pass = true;
								break;
							}
						}
					}
					else if( rule.vtype1 == 'enum' )
					{
						field = eval( "document."+form_name+"."+rule.var1 );

						pass = false;
						
						if( !field.multiple )
						{
							if( field.options[field.selectedIndex].text != '' )
							{
								pass = true;
								break;
							}
						}
					}
					else
					{
						var1_str = "document."+form_name+"."+rule.var1+".value";

						if( isValid( eval( var1_str ), rule.vtype1 ) )
						{
							pass = true;
						}
					}
				break;
				
				case 'mutex':
					var1_str = "document."+form_name+"."+rule.var1;
					var2_str = "document."+form_name+"."+rule.var2;

					if( rule.vtype1 == 'bool' || rule.vtype2 == 'bool' )
					{
						if( rule.vtype1 == 'bool' )
						{
							var1_str += ".checked";
						}
						else
						{
							var1_str += ".value";
						}

						if( rule.vtype2 == 'bool' )
						{
							var2_str += ".checked";
						}
						else
						{
							var2_str += ".value";
						}
					}
					else
					{
						var1_str += ".value";
						var2_str += ".value";
					}

					if( eval( var1_str ) && !eval( var2_str ) )
					{
						if( isValid( eval( var1_str ), rule.vtype1 ) ) pass = true;
					}

					if( !eval( var1_str ) && eval( var2_str ) )
					{
						if( isValid( eval( var2_str ), rule.vtype2 ) ) pass = true;
					}

					if( !eval( var1_str ) && !eval( var2_str ) )
					{
						pass = true;
					}
				break;
				
				case 'mutex-strict':
					var1_str = "document."+form_name+"."+rule.var1;
					var2_str = "document."+form_name+"."+rule.var2;

					if( rule.vtype1 == 'bool' || rule.vtype2 == 'bool' )
					{
						if( rule.vtype1 == 'bool' )
						{
							var1_str += ".checked";
						}
						else
						{
							var1_str += ".value";
						}

						if( rule.vtype2 == 'bool' )
						{
							var2_str += ".checked";
						}
						else
						{
							var2_str += ".value";
						}
					}
					else
					{
						var1_str += ".value";
						var2_str += ".value";
					}

					if( eval( var1_str ) && !eval( var2_str ) ) pass = true;
					if( !eval( var1_str ) && eval( var2_str ) ) pass = true;
				break;
				
				case 'required_if_set':
					var1_str = "document."+form_name+"."+rule.var1;
					var2_str = "document."+form_name+"."+rule.var2;

					if( rule.vtype1 == 'bool' || rule.vtype2 == 'bool' )
					{
						if( rule.vtype1 == 'bool' )
						{
							var1_str += ".checked";
						}
						else
						{
							var1_str += ".value";
						}

						if( rule.vtype2 == 'bool' )
						{
							var2_str += ".checked";
						}
						else
						{
							var2_str += ".value";
						}
					}
					else
					{
						var1_str += ".value";
						var2_str += ".value";
					}

					if( eval( var2_str ) && !eval( var1_str ) )
					{
						pass = false;
					}
					else
					{
						pass = true;
					}
				break;
					
				case 'required_if_set_to':
					var1_str = "document."+form_name+"."+rule.var1;
					var2_str = "document."+form_name+"."+rule.var2;

					if( rule.vtype1 == 'bool' || rule.vtype2 == 'bool' )
					{
						if( rule.vtype1 == 'bool' )
						{
							var1_str += ".checked";
						}
						else
						{
							var1_str += ".value";
						}

						if( rule.vtype2 == 'bool' )
						{
							var2_str += ".checked";
						}
						else
						{
							var2_str += ".value";
						}
					}
					else
					{
						var1_str += ".value";
						var2_str += ".value";
					}
					
					if( eval(var2_str) == rule.vvalue2 )
					{
						if( eval( var2_str ) && !eval( var1_str ) )
						{
							pass = false;
						}
						else
						{
							pass = true;
						}
					}
					else
					{
						pass = true;
					}
				break;
				
				case 'executeCommand':
					command = eval( rule.var1 );
					pass = command();
				break;
				
				default:
					alert( 'Error: Unsupported rule type: '+rule.type );
			}

			if( pass )
			{
				if( DEBUG )
				{
					alert( 'PASSED' );
				}
			}
			else
			{
				if( DEBUG )
				{
					alert( 'FAILED' );
				}

				output += rule.message+"\n";
				overall_pass = false;
			}
		}
		
		if( !overall_pass )
		{
			alert( output );
			return false;
		}
		else
		{
			return true;
		}
	}
}

function Validate( form_name )
{
	form = eval( "document."+form_name );

	if( typeof( form ) == 'undefined' )
	{
		alert( "Form: \""+form_name+"\" doesn't exist!" );
		return false;
	}
	else
	{
		this.form_name = form_name;
	}

	this.rules = new Array();
	this.num_rules = 0;

	// member functions
	this.setForm = setForm;
	this.addRule = addRule;
	this.check_var = check_var;
	this.check_type = check_type;
	this.validate = validate;
}

function isValid( value, vtype )
{
	switch( vtype )
	{
		case 'bool':
			if( isBool( value ) ) return true;
			break;

		case 'varchar':
			if( trim( value ).length > 0 ) return true;
			break;

		case 'int':
			if( isInteger( trim( value ) ) ) return true;
			break;

		case 'float':
			if( isFloat( trim( value ) ) ) return true;
			break;

		case 'email':
			if( isEmail( trim( value ) ) ) return true;
			break;

		case 'phone':
			if( isPhone( trim( value ) ) ) return true;
			break;

		case 'date':
			if( isDate( trim( value ) ) ) return true;
			break;

		case 'time':
			if( isTime( trim( value ) ) ) return true;
			break;

		case 'enum':
			if( isEnum( trim( value ) ) ) return true;
			break;
		
		case 'datetime':
			var splitted = value.split( ' ' );
			
			if( splitted.length == 2 )
			{
				if( isDate(trim(splitted[0])) && isTime(trim(splitted[1])) ) return true;
			}
			
			break;
			
		default:
			alert( 'Unsupported type: '+vtype );
	}

	// If it is unsupported or fails its check, it falls through to here
	return false;
}

function isBool( value )
{
	if( value == 0 || value == 1 || value == 'on' || value == 'off' ) return true;

	return false;
}
	
/**
 * DHTML date validation script. Courtesy of SmartWebby.com (http://www.smartwebby.com/dhtml/)
 */

// Declaring valid date character, minimum year and maximum year
var dtCh = "-";
var minYear = 1900;
var maxYear = 2100;

function isInteger( s )
{
	var i;
	var start = 0;
	
	if( s.length == 0 ) return false;

	if( s.charAt(0) == '-' )
	{
		start = 1;
	}
	
    for (i = start; i < s.length; i++)
	{
        // Check that current character is number.
        var c = s.charAt(i);

        if ( ((c < "0") || (c > "9")) ) return false;
    }

    // All characters are numbers.
    return true;
}

function stripCharsInBag( s, bag )
{
	var i;
    var returnString = "";

    // Search through string's characters one by one.
    // If character is not in bag, append to returnString.
    for (i = 0; i < s.length; i++)
	{
        var c = s.charAt(i);

        if (bag.indexOf(c) == -1) returnString += c;
    }

    return returnString;
}

function daysInFebruary (year)
{
	// February has 29 days in any year evenly divisible by four,
    // EXCEPT for centurial years which are not also divisible by 400.
    return (((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400 == 0))) ? 29 : 28 );
}

function DaysArray( n )
{
	for (var i = 1; i <= n; i++)
	{
		this[i] = 31;

		if ( i==4 || i==6 || i==9 || i==11 ) this[i] = 30;
		if (i==2) this[i] = 29;
   } 

   return this;
}

function isDate( dtStr )
{
	var daysInMonth = DaysArray( 12 );
	var pos1 = dtStr.indexOf( dtCh );
	var pos2 = dtStr.indexOf( dtCh, pos1+1 );
	var strMonth = dtStr.substring( 0, pos1 );
	var strDay = dtStr.substring( pos1+1, pos2 );
	var strYear = dtStr.substring( pos2+1 );
	strYr = strYear;

	if ( strDay.charAt(0) == "0" && strDay.length > 1 ) strDay = strDay.substring(1);

	if ( strMonth.charAt(0) == "0" && strMonth.length > 1 ) strMonth = strMonth.substring(1);

	for (var i = 1; i <= 3; i++)
	{
		if ( strYr.charAt(0) == "0" && strYr.length > 1 ) strYr = strYr.substring(1);
	}

	month = parseInt(strMonth);
	day = parseInt(strDay);
	year = parseInt(strYr);

	if ( pos1 == -1 || pos2 == -1 )
	{
		alert( 'The date format should be : mm'+dtCh+'dd'+dtCh+'yyyy' );

		return false;
	}

	if ( strMonth.length < 1 || month < 1 || month > 12 )
	{
		alert("Please enter a valid month");

		return false;
	}

	if ( strDay.length < 1 || day < 1 || day > 31 || (month == 2 && day > daysInFebruary( year )) || day > daysInMonth[month] )
	{
		alert("Please enter a valid day");

		return false;
	}

	if ( strYear.length != 4 || year == 0 || year < minYear || year > maxYear )
	{
		alert( "Please enter a valid 4 digit year between "+minYear+" and "+maxYear );

		return false;
	}

	if ( dtStr.indexOf( dtCh, pos2+1 ) != -1 || isInteger( stripCharsInBag( dtStr, dtCh ) ) == false )
	{
		alert( "Please enter a valid date" );

		return false;
	}

	return true;
}

function isEnum( enumStr )
{
	if( enumStr.length == 0 )
	{
		return false;
	}

	return true;
}

function isEmail( emailStr )
{
    if ( emailStr.length == 0 )
	{
        return false;
    }

    if ( !/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(emailStr) ) return false;

    return true;
}

function isPhone( phoneStr )
{
    if( phoneStr.length == 0 )
	{
        return false;
    }

    if( !/^[0-9\-\(\)]+$/.test(phoneStr) ) return false;

    return true;
}

function isFloat( floatStr )
{
    if( floatStr.length == 0 )
	{
        return false;
    }

    if( !/^[0-9\.]+$/.test(floatStr) ) return false;

    return true;
}

function isTime( timeStr )
{
    if( timeStr.length == 0 )
	{
        return false;
    }

    //time must be in the 24:00 format
	if ( timeStr.length < 4 || timeStr.length > 5 ) return false;

    if ( timeStr.length == 4 )
	{
		thetimeStr = '0'+timeStr;
	}
    else
	{
		thetimeStr = timeStr;
	}

    if( !/^[0-9]{2}\:[0-9]{2}$/.test(thetimeStr) ) return false;

    var strHour = thetimeStr.substring( 0, 2 );
    var strMin = thetimeStr.substring( 3, 5 );
    var strTime = strHour+strMin;
    var delimiter = thetimeStr.substring( 2, 3 );

    if ( strHour > maxHours || strMin > 60  || delimiter != ':' || strTime > 2400 )
	{
        return false;
    }

	return true;
}

function trim( s )
{
    if( typeof( s ) == 'undefined' ) return s;

    while ( s.substring( 0, 1 ) == " " )
	{
		s = s.substring( 1, s.length );
	}

	while ( s.substring( s.length-1, s.length ) == ' ' )
	{
		s = s.substring( 0, s.length-1 );
	}

	return s;
}
