
/*******************************************************************************
FILE: validate.js

AUTHOR: Mike Challis

DATE: 18/09/2001

EMAIL: mchallis@3thinking.com

DESCRIPTION: This file contains a library of validation functions
  using javascript regular expressions.  Library also contains functions that re-
  format fields for display or for storage.
  

  VALIDATION FUNCTIONS:

  validateCurrency - checks for valid currency format.  Only handles UK currency
  validateTime - checks for valid 12 hour time
  validateEmail - checks format of email address
  validateUKPhone - checks format of UK phone number
  validateNumeric - checks for valid numeric value
  validateInteger - checks for valid integer value
  validatePercentage - checks fo a whole percentage value from 0 to 100
  validateNotEmpty - checks for blank form field
  validateUKPostCode - checks for valid UK post code format
  validateUKDate - checks for valid date in UK format e.g. dd/mm/yyyy using a variety of separators e.g. / or -
  validateValue - checks a string against supplied pattern
  
  FORMAT FUNCTIONS:
  
  rightTrim - removes trailing spaces from a string
  leftTrim - removes leading spaces from a string
  trimAll - removes leading and trailing spaces from a string
  removeCurrency - removes currency formatting characters (), £ 
  addCurrency - inserts currency formatting characters
  removeCommas - removes comma separators from a number
  addCommas - adds comma separators to a number
  removeCharacters - removes characters from a string that match passed pattern
*******************************************************************************/


function validateCurrency( strValue)  {
/************************************************
DESCRIPTION: Validates that a string contains a 
  valid currency format. 
  
 PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
*************************************************/
  var objRegExp = /(^\£\d{1,3}(,\d{3})*\.\d{2}$)|(^\(\£\d{1,3}(,\d{3})*\.\d{2}\)$)/;

  return objRegExp.test( strValue );
}

function validateTime ( strValue ) {
/************************************************
DESCRIPTION: Validates that a string contains a 
  valid 12 hour time format. Seconds are optional.
  
 PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.

REMARKS: Returns True for time formats such as:
  HH:MM or HH:MM:SS or HH:MM:SS.mmm (where the
  .mmm is milliseconds as used in SQL Server 
  datetime datatype.  Also, the .mmm portion will 
  accept 1 to 3 digits after the period)
*************************************************/
  var objRegExp = /^([1-9]|1[0-2]):[0-5]\d(:[0-5]\d(\.\d{1,3})?)?$/;

  return objRegExp.test( strValue );

}




function validateEmail( strValue) {
/************************************************
DESCRIPTION: Validates that a string contains a 
  valid email pattern. 
  
 PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
   
REMARKS: Accounts for email with country appended
  does not validate that email contains valid URL
  type (.com, .gov, etc.) and optionally,
  a valid country suffix.  Since email has many
  forms this expression only tests for near valid
  address.  Some additional validation may be
  required.
  
  
*************************************************/
var objRegExp  = /^[a-z0-9]([a-z0-9_\-\.]*)@([a-z0-9_\-\.]*)(\.[a-z]{2,3}(\.[a-z]{2}){0,2})$/i;
//check for valid email
return objRegExp.test(strValue);
}

function validateUKPhone( strValue ) {
/************************************************
DESCRIPTION: Validates that a string contains valid
  UK phone pattern. 
  Ex. +44 (0)1793 636757 or 0800 666 777 or 545675 or 01793 636 757 or 636757
  
PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
*************************************************/
  var objRegExp  = /^([+]{0,1})(\d{0,2})\s*(\(0\)){0,1}\s*(\(*\d{0,5}\)*)\s*(\d{3,4})\s*(\d{3,4})$/;
 
  //check for valid UK phone with or without space between 
  //area code
  return objRegExp.test(strValue); 
}

function  validateNumeric( strValue ) {
/******************************************************************************
DESCRIPTION: Validates that a string contains only valid numbers.

PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
******************************************************************************/
  var objRegExp  =  /(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/; 
 
  //check for numeric characters 
  return objRegExp.test(strValue);
}

function validateInteger( strValue ) {
/************************************************
DESCRIPTION: Validates that a string contains only 
    valid integer number.
    
PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
******************************************************************************/
  var objRegExp  = /(^-?\d\d*$)/;
 
  //check for integer characters
  return objRegExp.test(strValue);
}

function validatePercentage( strValue ) {
/************************************************
DESCRIPTION: Validates that a string contains only 
    valid integer number that is between 0 and 100
    
PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
******************************************************************************/
  var objRegExp  = /(^-?\d\d*$)/;
 
  //check for integer characters
  if (objRegExp.test(strValue)) {
	if (parseInt(strValue)>=0 && parseInt(strValue)<=100) {
		return true;
	}
  }
  return false;
}


function validateNotEmpty( strValue ) {
/************************************************
DESCRIPTION: Validates that a string is not all
  blank (whitespace) characters.
    
PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
*************************************************/
   var strTemp = strValue;
   strTemp = trimAll(strTemp);
   if(strTemp.length > 0){
     return true;
   }  
   return false;
}

function validateSelected( strValue ) {
/************************************************
DESCRIPTION: Validates that a select control has a selection.  Here -1 is taken as no selection
    
PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
*************************************************/


   if(strValue != "-1"){
     return true;
   }  
   return false;
}


function validateUKPostCode( strValue ) {
/************************************************
DESCRIPTION: Validates that a string is a valid UK post code
    
PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.

*************************************************/
var objRegExp  = /^\s*([a-zA-Z]{1,2})(\d{1,2})\s*(\d{1}[a-zA-Z]{2})\s*$/;
 
  //check for valid UK postcode
  return objRegExp.test(strValue);
}

function validateUKDate( strValue ) {
/************************************************
DESCRIPTION: Validates that a string contains only 
    valid dates with 2 digit day,2 digit month, 
    4 digit year. Date separator can be ., -, or /.
    Uses combination of regular expressions and 
    string parsing to validate date.
    Ex. dd/mm/yyyy or dd-mm-yyyy or dd.mm.yyyy
    
PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
   
REMARKS:
   Avoids some of the limitations of the Date.parse()
   method such as the date separator character.
*************************************************/
  var objRegExp = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{4}$/
 
  //check to see if in correct format
  if(!objRegExp.test(strValue))
    return false; //doesn't match pattern, bad date
  else{
    	var arrayDate = strValue.split(RegExp.$1); //split date into day, month and year
	var intDay = parseInt(arrayDate[0],10); 
    	var intMonth = parseInt(arrayDate[1],10);
	var intYear = parseInt(arrayDate[2],10);

	
	//check for valid month
	if(intMonth > 12 || intMonth < 1) {
		return false;
	}
	
    //create a lookup for months not equal to Feb.
    var arrayLookup = { '01' : 31,'03' : 31, '04' : 30,'05' : 31,'06' : 30,'07' : 31,
                        '08' : 31,'09' : 30,'10' : 31,'11' : 30,'12' : 31}
  
    //check if month value and day value agree
    if(arrayLookup[arrayDate[1]] != null) {
      if(intDay <= arrayLookup[arrayDate[1]] && intDay != 0)
        return true; //found in lookup table, good date
    }
		
    //check for February
	var booLeapYear = (intYear % 4 == 0 && (intYear % 100 != 0 || intYear % 400 == 0));
    if( ((booLeapYear && intDay <= 29) || (!booLeapYear && intDay <=28)) && intDay !=0)
      return true; //Feb. had valid number of days
  }
  return false; //any other values, bad date
}

function validateValue( strValue, strMatchPattern ) {
/************************************************
DESCRIPTION: Validates that a string a matches
  a valid regular expression value.
    
PARAMETERS:
   strValue - String to be tested for validity
   strMatchPattern - String containing a valid
      regular expression match pattern.
      
RETURNS:
   True if valid, otherwise false.
*************************************************/
var objRegExp = new RegExp( strMatchPattern);
 
 //check if string matches pattern
 return objRegExp.test(strValue);
}


function rightTrim( strValue ) {
/************************************************
DESCRIPTION: Trims trailing whitespace chars.
    
PARAMETERS:
   strValue - String to be trimmed.  
      
RETURNS:
   Source string with right whitespaces removed.
*************************************************/
var objRegExp = /^([\w\W]*)(\b\s*)$/;
 
      if(objRegExp.test(strValue)) {
       //remove trailing a whitespace characters
       strValue = strValue.replace(objRegExp, '$1');
    }
  return strValue;
}

function leftTrim( strValue ) {
/************************************************
DESCRIPTION: Trims leading whitespace chars.
    
PARAMETERS:
   strValue - String to be trimmed
   
RETURNS:
   Source string with left whitespaces removed.
*************************************************/
var objRegExp = /^(\s*)(\b[\w\W]*)$/;
 
      if(objRegExp.test(strValue)) {
       //remove leading a whitespace characters
       strValue = strValue.replace(objRegExp, '$2');
    }
  return strValue;
}

function trimAll( strValue ) {
/************************************************
DESCRIPTION: Removes leading and trailing spaces.

PARAMETERS: Source string from which spaces will
  be removed;

RETURNS: Source string with whitespaces removed.
*************************************************/ 
 var objRegExp = /^(\s*)$/;

    //check for all spaces
    if(objRegExp.test(strValue)) {
       strValue = strValue.replace(objRegExp, '');
       if( strValue.length == 0)
          return strValue;
    }
    
   //check for leading & trailing spaces
   objRegExp = /^(\s*)([\W\w]*)(\b\s*$)/;
   if(objRegExp.test(strValue)) {
       //remove leading and trailing whitespace characters
       strValue = strValue.replace(objRegExp, '$2');
    }
  return strValue;
}

function removeCurrency( strValue ) {
/************************************************
DESCRIPTION: Removes currency formatting from 
  source string.
  
PARAMETERS: 
  strValue - Source string from which currency formatting
     will be removed;

RETURNS: Source string with commas removed.
*************************************************/
  var objRegExp = /\(/;
  var strMinus = '';
 
  //check if negative
  if(objRegExp.test(strValue)){
    strMinus = '-';
  }
  
  objRegExp = /\)|\(|[,]/g;
  strValue = strValue.replace(objRegExp,'');
  if(strValue.indexOf('$') >= 0){
    strValue = strValue.substring(1, strValue.length);
  }
  return strMinus + strValue;
}

function addCurrency( strValue ) {
/************************************************
DESCRIPTION: Formats a number as currency.

PARAMETERS: 
  strValue - Source string to be formatted

REMARKS: Assumes number passed is a valid 
  numeric value in the rounded to 2 decimal 
  places.  If not, returns original value.
*************************************************/
  var objRegExp = /-?[0-9]+\.[0-9]{2}$/;
   
    if( objRegExp.test(strValue)) {
      objRegExp.compile('^-');
      strValue = addCommas(strValue);
      if (objRegExp.test(strValue)){
        strValue = '($' + strValue.replace(objRegExp,'') + ')';
      }
      else {
        strValue = '$' + strValue;
      }
      return  strValue;
    }
    else
      return strValue;
}

function removeCommas( strValue ) {
/************************************************
DESCRIPTION: Removes commas from source string.

PARAMETERS: 
  strValue - Source string from which commas will 
    be removed;

RETURNS: Source string with commas removed.
*************************************************/
  var objRegExp = /,/g; //search for commas globally
 
  //replace all matches with empty strings
  return strValue.replace(objRegExp,'');
}

function addCommas( strValue ) {
/************************************************
DESCRIPTION: Inserts commas into numeric string.

PARAMETERS: 
  strValue - source string containing commas.
  
RETURNS: String modified with comma grouping if
  source was all numeric, otherwise source is 
  returned.
  
REMARKS: Used with integers or numbers with
  2 or less decimal places.
*************************************************/
  var objRegExp  = new RegExp('(-?[0-9]+)([0-9]{3})'); 

    //check for match to search criteria
    while(objRegExp.test(strValue)) {
       //replace original string with first group match, 
       //a comma, then second group match
       strValue = strValue.replace(objRegExp, '$1,$2');
    }
  return strValue;
}

function removeCharacters( strValue, strMatchPattern ) {
/************************************************
DESCRIPTION: Removes characters from a source string
  based upon matches of the supplied pattern.

PARAMETERS: 
  strValue - source string containing number.
  
RETURNS: String modified with characters
  matching search pattern removed
  
USAGE:  strNoSpaces = removeCharacters( ' sfdf  dfd', 
                                '\s*')
*************************************************/
 var objRegExp =  new RegExp( strMatchPattern, 'gi' );
 
 //replace passed pattern matches with blanks
  return strValue.replace(objRegExp,'');
}

 


var PatternsDict = new Object();
var warningsDict = new Object();
   


PatternsDict.emailPat="validateEmail";
warningsDict.emailPat="This field must contain a valid email address";
// matches valid email address formats


PatternsDict.mandatoryPat="validateNotEmpty";
warningsDict.mandatoryPat="This field is mandatory";
// must contain at least one character, but not just whitespace

PatternsDict.mandatorySelect="validateSelected";
warningsDict.mandatorySelect="Warning - a mandatory field has not been entered.  Please try again.";
// must select a value that is not -1



PatternsDict.ukphonePat="validateUKPhone";
warningsDict.ukphonePat="This field must contain a valid UK style phone number";
 // matches valid phone number formats


PatternsDict.integerPat="validateInteger";
warningsDict.integerPat="This field must contain a valid integer number (whole number)";
 // matches valid integer number

PatternsDict.percentagePat="validatePercentage";
warningsDict.percentagePat="This field must contain a valid integer percentage number (whole number between 0 and 100)";
 // matches valid integer number whose value is between 0 and 100


PatternsDict.numericPat="validateNumeric";
warningsDict.numericPat="This field must contain a valid number";
 // matches valid integer/floating point number


PatternsDict.ukdatePat="validateUKDate";
warningsDict.ukdatePat="This field must contain a valid date in UK format ie. dd/mm/yyyy";
 // matches valid UK style date

PatternsDict.ukpostcodePat="validateUKPostCode";
warningsDict.ukpostcodePat="This field must contain a valid UK postcode";
 // matches valid UK postcode



function validateForm(theForm){
/************************************************
DESCRIPTION: Validates a complete form based on special attributes
             that may exist on form controls.  Pops up an alert describing
	     any validation failures, and puts the focus into the control that
             has failed validation.

PARAMETERS:  theForm - the form object to be validated
  
RETURNS: true if the form validates completely
  
USAGE:<form name="theForm" id="theForm" method="POST" action="mypage.asp"  onSubmit="return validateForm(theForm)">
*************************************************/
 var elArr = theForm.elements; // get all elements of the form into array
 for(var i = 0; i < elArr.length; i++)
  with(elArr[i]){              // for each element of the form...
   var v = elArr[i].validator; // get validator, if any
   var opt=elArr[i].optional;  // find out if the validation is optional, ie the field can be empty
   if(!v) continue;            // no validator property, skip
   var thePat = PatternsDict[v];   // select the validating function text
   var theWarning=warningsDict[v]; // get the warning message
   var gotIt=eval(thePat + "(value);"); // Execute the appropriate validation function
   if((!gotIt && typeof(opt)=='undefined') || (typeof(opt)!='undefined' && value!='' && !gotIt))
	{
		elArr[i].focus();
    		alert(theWarning); 
		return false;
	}
	
  } return true;
 }

