	// 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.
	
	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.supportedFileFormat = supportedFileFormat_GDK_VALIDATION;
		this.alwaysTrue = alwaysTrue_GDK_VALIDATION;	
		this.bool = bool_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;
				 	
	}
	
	
	// Public methods
	
	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 ) )
			{
				errorMsg = "requires a minimum of " + minChars + " characters!";
				return errorMsg;	
			}	
		}
		
		if ( this._notJunk( maxChars ) )
		{
			// Check string is longer than the maximum length
			if( Number( str.length ) > Number( maxChars ) )
			{
				errorMsg = "accepts a maximum of " + maxChars + " characters!";
				return errorMsg;	
			}	
		}	
		
		return false;
	}

	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 bool_GDK_VALIDATION( str )
    {
    	// FUNCTION REPLACES boolean_GDK_VALIDATION - 25/5/2005 NS
    	
    	// In this context false may be signified by an empty string or undef
    	if ( ! str ) 
        {
       		return "";
        }

    	booleanExp = /^([01])$/;

		// Validate str is an integer
		var booleanMatch = str.match( booleanExp );
		
		if ( ! booleanMatch )
		{
			return "invalid boolean setting!";
		}
	
		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;
	}

	function supportedFileFormat_GDK_VALIDATION( filename, extensionList )
    {
	    // Validate file format validates a filename against an array (ref) of file
	    // formats to see if it is within the list.
	      
	    inExtensionList = false;
	    
	    // Get the file extension 
	    var extension = filename.match( /^.*?\.(\S*)\s*$/ );
    
   		if ( extension )
        {
            extension = extension[1];
        }
    	else
        {
            return false;
        }
    
	    // Test the file extension against each extension in the list until we find
	    // a match.
	    for ( i = 0; i < extensionList.length; i++ )
	    {
	            
	        // Test for matching extension
	        if ( extensionList[i].toLowerCase() == extension.toLowerCase() )
	        {
	            inExtensionList = true;
	        	break;
	        }
	            
	    }
	    
	    return inExtensionList;
    }

	function alwaysTrue_GDK_VALIDATION()
    {
    	// Alway returns true
    	return true;
    }
	
	// Private methods
	function _notJunk_GDK_VALIDATION( str )
	{
		// Validate the string contains valid data
		if ( str != "junk" ) return true;
		return;
	}
