// Tool:   GDK_VALIDATION.js
// Author: Anthony James Blackshaw
// Date:   11-2002

// Getme Ltd (c)2002. All rights reserved.

/*
The GDK_VALIDATION object supports for the validation of data via the client- 
side browser. This saves the user time by highlighting invalid data before it
is sent to a server-side cgi script.

Some validation however, must be done server-side (i.e. database lookups for
duplicate records) in these cases the form will have to be sent before this can
be validated. 

All data validated via the client-side is still validated in the same way on
the server-side again, since we cannot ensure where the data is sent from.
*/

// Public methods

function GDK_VALIDATION()
	{
		
	// Public
		
	// Properties
	this.version = 0.1;
	
	// Methods
	this.exist      = exist_GDK_VALIDATION;
	this.integer    = integer_GDK_VALIDATION;
	this.email      = email_GDK_VALIDATION;
	this.date       = date_GDK_VALIDATION;
	this.time24     = time24_GDK_VALIDATION;
	this.colour     = colour_GDK_VALIDATION;
	this.decimal    = decimal_GDK_VALIDATION;
	this.postalCode = postalCode_GDK_VALIDATION;
	this.card       = card_GDK_VALIDATION;
	this.mask       = mask_GDK_VALIDATION;	
	
	
	// Private
	
	// Properties
	this._months = new Array
		( 
		'junk',
		'January', 
		'February', 
	 	'March', 
		'April',
		'May', 
		'June', 
		'July',
		'August', 
		'September', 
		'October', 
		'November', 
		'December' 
		);

	// Methods
	this._notJunk = _notJunk_GDK_VALIDATION;
				 	
	}

function card_GDK_VALIDATION( str, prefix, digitCount )
	{
	// Validate card number
	cardNumberExp = eval( '/^[0-9]{' + digitCount + '}$/' );
	
	var cardNumberMatch = str.match( cardNumberExp );
	
	if ( !cardNumberMatch )
		{
		return 'requires a valid card number!';
		}
		
	if ( this._notJunk( prefix ) )
		{
		// Validate prefix
		prefixMatch = str.match( eval( '/' + prefix + '/' ) );
		
		if( !prefixMatch )
			{
			return 'requires a valid card number!';
			}
		}
		
	// Validate via Mod10 algorythm
	cardDigits = new Array( digitCount );

    for( digit = 0; digit < digitCount; ++digit )
    	{
    	cardDigits[ digit ] = parseInt( str.charAt( digit ) );
    	}
    
    for( digit = digitCount - 2; digit >= 0; digit -= 2 )
    	{ 
    	cardDigits[ digit ] *= 2;
    	
    	if( cardDigits[ digit ] > 9 )
    		{
    		cardDigits[ digit ] -= 9;
    		}
    	}

	cardValue = 0;
	
    for( digit = 0; digit < digitCount; ++digit ) 
    	{
    	cardValue += cardDigits[ digit ];
		}	
	
	// Validate cardValue is divisable by 10	
	if ( !( ( cardValue % 10 ) == 0 ) )
		{
		return 'requires a valid card number!';
		}
		
	return;
	}

function colour_GDK_VALIDATION( str, colourCount )
	{
	hexExp = eval( '/^([A-Fa-f0-9]{2}){' + colourCount + '}$/' );	
	
	// Validate the hex colour format
	var colourMatch = str.match( hexExp );
	
	if ( !colourMatch )
		{
		return 'requires a valid colour!';
		}
	
	return;
	}

function date_GDK_VALIDATION( str, minDate, maxDate )
	{	
	// Validate the date format
	dateExp = /^(\d{4})\-(\d{2})\-(\d{2})$/;
		
	dateMatch = str.match( dateExp );
	
	if ( !dateMatch )
		{
		return 'requires a valid date!';
		}
	
	// Split the str into day, month, year	
	strDay   = dateMatch[3];
	strMonth = dateMatch[2];
	strYear  = dateMatch[1];
	
	// Calculate str as a date value
	strDate = strYear + strMonth + strDay;
	
	// Convert str date element to numbers
	strDay   = Number( strDay );
	strMonth = Number( strMonth );
	strYear  = Number( strYear );
	
	// Validate the date exists
	if ( strDay < 1 || strDay > 31 )
		{
		return 'requires a day value between 01 and 31!';
		}
			
	if ( strMonth < 1 || strMonth > 12 )
		{
		return 'requires a month value between 01 and 12!';
		}
					
	if ( ( strMonth == 4 || strMonth == 6 || strMonth == 9 || strMonth == 11 ) && strDay == 31 )
		{
		return ( this._months[ strMonth ] + ' does not have 31 days!' );
		}

	// Leap year validation
	if ( strMonth == 2 )
		{
		var isLeap = ( strYear % 4 == 0 && ( strYear % 100 != 0 || strYear % 400 == 0 ) );
					
		if ( strDay > 29 || ( strDay == 29 && !isLeap ) )
			{
			return ( 'February ' + strYear + ' does not have ' + strDay + ' days!' );
			}
		}	
					
	if ( this._notJunk( minDate ) )
		{
		// Check date greater than minimum date 
		
		minDateMatch = minDate.match( dateExp );
			
		// Split the minDate into day, month, year
		minDay   = minDateMatch[3];
		minMonth = minDateMatch[2];
		minYear  = minDateMatch[1];
		
		// Calculate minDate as a date value
		minDate = minYear + minMonth + minDay;
			
		if ( Number( minDate ) > Number( strDate ) )
			{
			return ( 'requires a date from ' + minDay + ', ' + this._months[ Number( minMonth ) ] + ', ' + minYear + ' or after!');
			}
		}
	
	if ( this._notJunk( maxDate ) )
		{
		// Check date greater than minimum date 

		var maxDateMatch = maxDate.match( dateExp );
			
		// Split the maxDate into day, month, year
		maxDay   = maxDateMatch[3];
		maxMonth = maxDateMatch[2];
		maxYear  = maxDateMatch[1];
			
		// Calculate maxDate as a date value
		maxDate = maxYear + maxMonth + maxDay;
			
		if ( Number( maxDate ) < Number( strDate ) )
			{
			return ( 'requires a date from ' + maxDay + ', ' + this._months[ Number( maxMonth ) ] + ', ' + maxYear + ' or before!');
			}
		}
					
	return;
	}

function decimal_GDK_VALIDATION( str, minValue, maxValue, accuracy )
	{
	// Validate floating-point/decimal number
	decimalExp = /^[+-]{0,1}(\d+)\.(\d+)$/;
	
	var decimalMatch = str.match( decimalExp );

	if ( !decimalMatch )
		{
		return 'requires a valid value!';
		}
		
	// If limited accuracy
	if ( this._notJunk( accuracy ) )
		{
		tailLength = Number( decimalMatch[2].length );
			
		// Validate that the accuracy limit has not been exceeded
		if ( tailLength > Number( accuracy ) )
			{
			return 'requires a value with a maximum of ' + accuracy + ' decimals after the decimal point!';
			}	
		}
		
	// Convert string to a number
	str = Number( str )
	
	// Validate string is above minimum value
	if ( this._notJunk( minValue ) && Number( minValue ) > str )
		{
		return ( 'requires a minimum value of ' + minValue + '!' );
		}
		
	// Validate string is below maximum value
	if ( this._notJunk( maxValue ) && Number( maxValue ) < str )
		{
		return ( 'requires a maximum value of ' + maxValue + '!' );
		}	
	
	return;	
	}

function email_GDK_VALIDATION( str )
	{
	isNotEmailExp = /(@.*@)|(\.\.)|(@\.)|(\.@)|(^\.)/;
	isEmailExp 	  = /^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z0-9]+)(\]?)$/;
	
	// Check for character formations that should not exists in an email 
	// address and validate the email format.
	if ( isNotEmailExp.test( str ) || !isEmailExp.test( str ))
		{
		return 'requires a valid email address!';	
		}

	return;
	}

function exist_GDK_VALIDATION( str, minChars, maxChars )
	{	
	existExp = /^\s*$/;
			
	// Check if any field data exists
	if ( existExp.test( str ) )
		{
		return 'is required!';
		}
		
	if ( this._notJunk( minChars ) )
		{
		// Check string is shorter than the minimum length
		if ( Number( str.length ) < Number( minChars ) )
			{
			return ( 'requires a minimum of ' + minChars + ' characters!' );	
			}	
		}
		
	if ( this._notJunk( maxChars ) )
		{
		// Check string is longer than the maximum length
		if ( Number( str.length ) > Number( maxChars ) )
			{
			return ( 'accepts a maximum of ' + maxChars + ' characters!' );	
			}	
		}	
		
	return;
	}

function integer_GDK_VALIDATION( str, minValue, maxValue )
	{
	integerExp = /^[+-]{0,1}\d+$/;
			
	// Check if string is a valid number
	if ( !integerExp.test( str ) )
		{
		return 'requires a valid number!';
		}
		
	if ( this._notJunk( minValue ) )
		{
		// Check string less than minimum value
		if ( Number( str ) < Number( minValue ) )
			{
			return ( 'requires a value of ' + minValue + ' or higher!' );	
			}	
		}
	
	if ( this._notJunk( maxValue ) )
		{
		// Check string greater than maximum value
		if ( Number( str ) > Number( maxValue ) )
			{
			return ( 'requires a value of ' + maxValue + ' or less!' );	
			}	
		}	

	return;
	}

function mask_GDK_VALIDATION( str, mask, message )
	{	
	
	// Validate the via the mask
	var maskMatch = mask.exec( str );
	
	if ( !maskMatch )
		{
		return message;
		}
	
	return;	
	}

function postalCode_GDK_VALIDATION( str, country )
	{
		// Validate postal code for selected country
		if ( country == 'UK' )
			{
			postCodeExp = /^([A-Z]{1,2}[0-9]{1}[0-9A-Z]{0,1} [0-9]{1}[A-Z]{2})$/;
			
			// Match the postcode
			var postCodeMatch = str.match( postCodeExp );
			
			// Validate postcode match
			if ( !postCodeMatch )
				{
				return 'requires a valid postcode!';
				}			
			}
		else if ( country == 'USA' )
			{
			zipCodeExp = /^(\d{5})(-\d{4}){0,1}$/;			

			// Match the zip code
			var zipCodeMatch = str.match( zipCodeExp );
			
			// Validate zip code match
			if ( !zipCodeMatch )
				{
				return 'requires a valid zip code!';
				}				
			}
		else
			{
				/* The current javascript implementation is for client-side validation only.
				   Therefore only the major 2 countrys are catered for, if this is not sufficient
				   other countries can be added above, or validated server-side with the use of a
				   database table.
				*/
				return;
			}
	
		return;
	}
	
function time24_GDK_VALIDATION( str, minTime, maxTime )
	{
	// Validate the time format
	timeExp = /^(\d{2})\:(\d{2})\:(\d{2})$/;
	
	var timeMatch = str.match( timeExp );
	
	if ( !timeMatch )
		{
		return 'requires a valid time!';
		}
	
	// Split the strTime into hours, mimutes, seconds
	strHrs  = timeMatch[1];
	strMins = timeMatch[2];
	strSecs = timeMatch[3];
	
	// Calculate str as a time value
	strTime = strHrs + strMins + strSecs;
	
	// Convert str time elements to numbers
	strHrs  = Number( strHrs );
	strMins = Number( strMins );
	strSecs = Number( strSecs );
	
	// Validate the time exists
	if ( strHrs < 0 || strHrs > 23 )
		{
		return 'requires an hour value between 00 and 23!';
		}
		
	if ( strMins < 0 || strMins > 59 )
		{
		return 'requires a minute value between 00 and 59!';
		}

	if ( strSecs < 0 || strSecs > 59 )
		{
		return 'requires a second value between 00 and 59!';
		}	

	if ( this._notJunk( minTime ) )
		{
		// Check time greater than minimum time
		
		var minTimeMatch = minTime.match( timeExp );
			
		// Split the minTime into hours, minutes, seconds
		minHrs  = minTimeMatch[1];
		minMins = minTimeMatch[2];
		minSecs = minTimeMatch[3];
			
		// Calculate minTime as a date value
		minTime = minHrs + minMins + minSecs;
			
		if ( Number( minTime ) > Number( strTime ) )
			{
			return ( 'requires a time of ' + minHrs + ':' + minMins + ':' + minSecs + ' or before!');
			}
		}	
		
	// Check if maximum time validation required
	if ( this._notJunk( maxTime ) )
		{
		// Check time greater than minimum time
		
		var maxTimeMatch = maxTime.match( timeExp );
			
		// Split the maxTime into hours, minutes, seconds
		maxHrs  = maxTimeMatch[1];
		maxMins = maxTimeMatch[2];
		maxSecs = maxTimeMatch[3];
			
		// Calculate maxTime as a date value
		maxTime = maxHrs + maxMins + maxSecs;
			
		if ( Number( maxTime ) < Number( strTime ) )
			{
			return ( 'requires a time of ' + maxHrs + ':' + maxMins + ':' + maxSecs + ' or before!');
			}
		}	
		
	return;
	}
	
// Private methods

function _notJunk_GDK_VALIDATION( str )
	{
	// Validate the string contains valid data
	if ( str != 'junk' ) return true;
	return;
	}