/*******************************************************************************
FILE: RegExpValidate.js

DESCRIPTION: This file contains a library of validation functions
  using javascript regular expressions.

  VALIDATION FUNCTIONS:

  validateCurrency - checks for valid currency format
  validateTime - checks for valid 12 hour time
  validateState -  checks for valid state abbreviation  
  validateSSN - checks format of social security number
  validateEmail - checks format of email address
  validateUSPhone - checks format of US phone number
  validateNumeric - checks for valid numeric value
  validateInteger - checks for valid integer value
  validateNotEmpty - checks for blank form field
  validateUSZip - checks for valid US zip code
  validateUSDate - checks for valid date in US format
  validateValue - checks a string against supplied pattern

  FORMAT FUNCTIONS:

  trimAll - removes leading and trailing spaces from a string
*******************************************************************************/

/************************************************
DESCRIPTION: Validates that a string contains a valid currency format. 
 
PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
*************************************************/
function validateCurrency( strValue)  {
//  var objRegExp = ^\$?\d+(\.(\d{2}))?$;
	var objRegExp = /^\d+(?:\.\d{0,2})?$/;
//	var objRegExp = ^\d+(\.\d\d)?$;
//	var objRegExp = /(^\$\d{1,3}(,\d{3})*\.\d{2}$)|(^\(\$\d{1,3}(,\d{3})*\.\d{2}\)$)/;

	return objRegExp.test( 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)
*************************************************/
function validateTime ( strValue ) {
  var objRegExp = /^([1-9]|1[0-2]):[0-5]\d(:[0-5]\d(\.\d{1,3})?)?$/;

  return objRegExp.test( strValue );

}

/************************************************
DESCRIPTION: Validates that a string contains a valid state abbreviation. 
  
PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
*************************************************/
function validateState (strValue ) {

var objRegExp = /^(AK|AL|AR|AZ|CA|CO|CT|DC|DE|FL|GA|HI|IA|ID|IL|IN|KS|KY|LA|MA|MD|ME|MI|MN|MO|MS|MT|NB|NC|ND|NH|NJ|NM|NV|NY|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VA|VT|WA|WI|WV|WY)$/i; 

  return objRegExp.test(strValue);
}

/************************************************
DESCRIPTION: Validates that a string contains a valid social security number. 
  
PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
*************************************************/
function validateSSN( strValue ) {
var objRegExp  = /^\d{3}\-\d{2}\-\d{4}$/;
 
  //check for valid SSN
  return objRegExp.test(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.
*************************************************/
function validateEmail( strValue) {
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
	var strTemp = strValue;
	strTemp = trimAll(strTemp);
	if(strTemp.length > 0 && strTemp.length <= 100){
		return objRegExp.test(strValue);
	}  
	return false;
}

/************************************************
DESCRIPTION: Validates that a string contains valid US phone pattern. 
  Ex. (999) 999-9999 or (999)999-9999

PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
*************************************************/
function validateUSPhone( strValue ) {
  var objRegExp  = /^\([1-9]\d{2}\)\s?\d{3}\-\d{4}$/;
 
  //check for valid us phone with or without space between 
  //area code
  return objRegExp.test(strValue); 
}

/******************************************************************************
DESCRIPTION: Validates that a string contains only valid numbers.

PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
******************************************************************************/
function  validateNumeric( strValue ) {
  var objRegExp  =  /(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/; 
 
  //check for numeric characters 
  return objRegExp.test(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.
******************************************************************************/
function validateInteger( strValue ) {
  var objRegExp  = /(^-?\d\d*$)/;
 
  //check for integer characters
  return objRegExp.test(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.
*************************************************/
function validateNotEmpty( strValue ) {
var strTemp = strValue;
   strTemp = trimAll(strTemp);
	if(strTemp.length > 0){
     return true;
   }  
   return false;
}

/************************************************
DESCRIPTION: Validates that a string a United States zip code in 5 digit format or zip+4
  format. 99999 or 99999-9999
    
PARAMETERS:
   strValue - String to be tested for validity
   
RETURNS:
   True if valid, otherwise false.

*************************************************/
function validateUSZip( strValue ) {
var objRegExp  = /(^\d{5}$)|(^\d{5}-\d{4}$)/;
 
  //check for valid US Zipcode
  return objRegExp.test(strValue);
}

/************************************************
DESCRIPTION: Validates that a string contains only valid dates with 2 digit month, 2 digit day, 
    4 digit year. Date separator can be ., -, or /.
    Uses combination of regular expressions and string parsing to validate date.
    Ex. mm/dd/yyyy or mm-dd-yyyy or mm.dd.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.
*************************************************/
function validateUSDate( strValue ) {
  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 month, day, year
	var intDay = parseInt(arrayDate[1],10); 
	var intYear = parseInt(arrayDate[2],10);
    var intMonth = parseInt(arrayDate[0],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[0]] != null) {
      if(intDay <= arrayLookup[arrayDate[0]] && 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
}

/************************************************
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.
*************************************************/
function validateUKDate( strValue ) {
  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, 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 = { '1' : 31, '2' : 28, '3' : 31, '4' : 30,'5' : 31,'6' : 30,'7' : 31,
                        '8' : 31,'9' : 30,'10' : 31,'11' : 30,'12' : 31}
    //check if month value and day value agree
    if(arrayLookup[intMonth] != null) {
      if(intDay <= arrayLookup[intMonth] && 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
}

/************************************************
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.
*************************************************/
function validateValue( strValue, strMatchPattern ) {
var objRegExp = new RegExp( strMatchPattern);
 
 //check if string matches pattern
 return objRegExp.test(strValue);
}

/************************************************
DESCRIPTION: Removes leading and trailing spaces.

PARAMETERS: Source string from which spaces will be removed;

RETURNS: Source string with whitespaces removed.
*************************************************/ 
function trimAll( strValue ) {
 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;
}

/************************************************
Validation types supported out of the box. All are optional, which means that all passwords are allowed by default.

- Minimum and maximum length. 
- Minimum n lowercase characters (a–z). 
- Minimum n uppercase characters (A–Z). 
- Minimum n combined a–z and A–Z characters. 
- Minimum n numeric characters (0–9). 
- Minimum n special characters (characters other than a–z, A–Z, and 0–9). 
- Ban particular words (tested case-insensitively). 
- Ban n-length character sequences (e.g. "abc", "XYZ", or "789", with a sequence length of 3; does not apply to special characters). 
- Ban n-length qwerty character sequences (e.g. "qwerty" or "asdf", with a sequence length of 4; does not apply to special characters). 
- Ban sequential, identical characters (e.g. "aa" or "!!"). 
- Use custom regular expressions (tested using RegExp.prototype.test) and functions (the password is provided as the first argument, and a Boolean value is expected in return). 

Here's an example of how it can be used:

var password = "password";
var passed = validatePassword(password, {
	length:   [8, Infinity],
	lower:    1,
	upper:    1,
	numeric:  1,
	special:  1,
	badWords: ["password", "steven", "levithan"],
	badSequenceLength: 4
});

passed: false

The above requires that password is at least eight characters long; has at least one lowercase, uppercase, numeric, and special character; doesn't include the words "password", "steven", or "levithan"; and doesn't include an alphanumeric sequence four or more characters in length (e.g. "1234").
************************************************/
function validateString (pw, options) {
	// default options (allows any password)
	var o = {
		lower:    0,
		upper:    0,
		alpha:    0, /* lower + upper */
		numeric:  0,
		special:  0,
		length:   [0, Infinity],
		custom:   [ /* regexes and/or functions */ ],
		badWords: [],
		badSequenceLength: 0,
		noQwertySequences: false,
		noSequential:      false
	};

	for (var property in options)
		o[property] = options[property];

	var	re = {
			lower:   /[a-z]/g,
			upper:   /[A-Z]/g,
			alpha:   /[A-Z]/gi,
			numeric: /[0-9]/g,
			special: /[\W_]/g
		},
		rule, i;

	// enforce min/max length
	if (pw.length < o.length[0] || pw.length > o.length[1])
		return false;

	// enforce lower/upper/alpha/numeric/special rules
	for (rule in re) {
		if ((pw.match(re[rule]) || []).length < o[rule])
			return false;
	}

	// enforce word ban (case insensitive)
	for (i = 0; i < o.badWords.length; i++) {
		if (pw.toLowerCase().indexOf(o.badWords[i].toLowerCase()) > -1)
			return false;
	}

	// enforce the no sequential, identical characters rule
	if (o.noSequential && /([\S\s])\1/.test(pw))
		return false;

	// enforce alphanumeric/qwerty sequence ban rules
	if (o.badSequenceLength) {
		var	lower   = "abcdefghijklmnopqrstuvwxyz",
			upper   = lower.toUpperCase(),
			numbers = "0123456789",
			qwerty  = "qwertyuiopasdfghjklzxcvbnm",
			start   = o.badSequenceLength - 1,
			seq     = "_" + pw.slice(0, start);
		for (i = start; i < pw.length; i++) {
			seq = seq.slice(1) + pw.charAt(i);
			if (
				lower.indexOf(seq)   > -1 ||
				upper.indexOf(seq)   > -1 ||
				numbers.indexOf(seq) > -1 ||
				(o.noQwertySequences && qwerty.indexOf(seq) > -1)
			) {
				return false;
			}
		}
	}

	// enforce custom regex/function rules
	for (i = 0; i < o.custom.length; i++) {
		rule = o.custom[i];
		if (rule instanceof RegExp) {
			if (!rule.test(pw))
				return false;
		} else if (rule instanceof Function) {
			if (!rule(pw))
				return false;
		}
	}

	// great success!
	return true;
}

/************************************************
DESCRIPTION: Validates that a string contains a valid URL
    
PARAMETERS:
   s - String to be tested for validity
      
RETURNS:
   True if valid, otherwise false.
*************************************************/
function isUrl(s) {
	var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/
	return regexp.test(s);
}

/************************************************
DESCRIPTION: Validates that a checkbox is not checked
    
PARAMETERS:
   strValue - Field to be tested for validity
   
RETURNS:
   True if valid, otherwise false.
*************************************************/
function validateNotChecked( checkedbox ) {
	if(checkedbox.checked == false){
     return true;
   }  
   return false;
}


