/*< blank basic *******************************************************************/
fValidate.prototype.blank = function()
{
  if ( this.typeMismatch( 'text' ) ) return;
  if ( this.isBlank() )
  {
    this.throwError( [this.elem.fName] );
  }
}
/*/>*/
/*< number numbers *******************************************************************/
fValidate.prototype.number = function( type, lb, ub )
{
  if ( this.typeMismatch( 'text' ) ) return;
  var num  = ( type == 0 ) ? parseInt( this.elem.value, 10 ) : parseFloat( this.elem.value );
  lb       = this.setArg( lb, 0 );
  ub       = this.setArg( ub, Number.infinity );
  if ( lb > ub )
  {
    this.devError( [lb, ub, this.elem.name] );
    return;
  }
  var fail = Boolean( isNaN( num ) || num != this.elem.value );
  if ( !fail )
  {
    switch( true )
    {
      case ( lb != false && ub != false ) : fail = !Boolean( lb <= num && num <= ub ); break;
      case ( lb != false ) : fail = Boolean( num < lb ); break;
      case ( ub != false ) : fail = Boolean( num > ub ); break;
    }
  }
  if ( fail )
  {
    this.throwError( [this.elem.fName] );
    return;
  }
  this.elemPass = true;
}
/*/>*/
/*< numeric numbers *******************************************************************/
fValidate.prototype.numeric = function( len )
{
  if ( this.typeMismatch( 'text' ) ) return;
  len = this.setArg( len, '*' );
  var regex = new RegExp( ( len == '*' ) ? "^\\d+$" : "^\\d{" + parseInt( len, 10 ) + "}\\d*$" );
  if ( !regex.test( this.elem.value ) )
  {
    if ( len == "*" )
    {
      this.throwError( [this.elem.fName] );
    } else {
      this.throwError( [len, this.elem.fName], 1 );
    }
  }
}
/*/>*/
/*< length basic *******************************************************************/
fValidate.prototype.length = function( len, maxLen )
{
  if ( this.typeMismatch( 'text' ) ) return;
  var vlen = this.elem.value.length;
  len    = Math.abs( len );
  maxLen  = Math.abs( this.setArg( maxLen, Number.infinity ) );
  if ( len > maxLen )
  {
    this.devError( [len, maxLen, this.elem.name] );
    return;
  }
  if ( len > parseInt( vlen, 10 ) )
  {
    this.throwError( [this.elem.fName, len] );
  }  
  if ( vlen > maxLen )
  {
    this.throwError( [this.elem.fName, maxLen, vlen], 1 );
  }
}
/*/>*/
/*< alnum extended *******************************************************************/
fValidate.prototype.alnum = function( minLen, tCase, numbers, spaces, puncs )
{
  if ( this.typeMismatch( 'text' ) ) return;

  tCase = this.setArg( tCase, "a" );
  
  //alert( [minLen,tCase,numbers,spaces,puncs] );

  numbers = ( numbers == "true" || numbers == "1" );
  spaces = ( spaces == "true" || spaces == "1" );

  //alert( [minLen,tCase,numbers,spaces,puncs] );
    
  var okChars = "",
    arrE  = ['None','Any','No','No','Any'];

  if ( minLen != '*' )
  {
    minLen =  parseInt( minLen, 10 );
    arrE[0] = minLen;
  } else {
    minLen = 0;
  }

  switch( tCase.toUpperCase() )
  {
    case 'U':
      okChars += 'A-Z';
      arrE[1] =  'UPPER';
      break;
    case 'L':
      okChars += 'a-z';
      arrE[1] =  'lower';
      break;
    case 'C':
      okChars += 'A-Z][a-z';
      arrE[1] =  'Intial capital';
      minLen--;
      break;
    default:
      okChars += 'a-zA-Z';
      break;    
  }

  if ( numbers == true )
  {
    okChars += '0-9';
    arrE[2] =  'Yes';
  }
  if ( spaces == true )
  {
    okChars += ' ';
    arrE[3] =  'Yes';
  }
  if ( puncs == "any" )
  {
    arrE[4]  = "Any";
  }
  else if ( puncs == "none" )
  {
    arrE[4] = "None";
  }
  else 
  {
    puncs = puncs.replace( /pipe/g, "|" );
    okChars += puncs;
    arrE[4] =  puncs; //.toPattern().replace( /\\/g, "" );
  }
  var length = ( minLen != "*" )?
    "{" + minLen + ",}":
    "+";
  var regex = ( puncs == "any" ) ?
    new RegExp( "^([" + okChars + "]|[^a-zA-Z0-9\\s])" + length + "$" ):
    new RegExp( "^[" + okChars + "]" + length + "$" );
  
  if ( !regex.test( this.elem.value ) )
  {
    this.throwError( [this.elem.value, this.elem.fName, arrE[0], arrE[1], arrE[2], arrE[3], arrE[4]] );
  }
}
/*/>*/
/*< equalto logical *******************************************************************/
fValidate.prototype.equalto = function( oName )
{
  if ( this.typeMismatch( 'text' ) ) return;
  if ( typeof oName == 'undefined' )
  {
    this.paramError( 'oName' );
  }
  var otherElem = this.form.elements[oName];
  if ( this.elem.value != otherElem.value )
  {
    this.throwError( [this.elem.fName,otherElem.fName] );      
  }
}
/*/>*/
/*< ssn extended *******************************************************************/
fValidate.prototype.ssn = function()
{
  if ( this.typeMismatch( 'text' ) ) return;
  if ( !( /^\d{3}\-\d{2}\-\d{4}$/.test( this.elem.value ) ) )
    this.throwError();
}
/*/>*/
/*< select controls *******************************************************************/
fValidate.prototype.select = function()
{
  if ( this.typeMismatch( 's1' ) ) return;
  if ( this.elem.selectedIndex == 0 )
  {
    this.throwError( [this.elem.fName] );
  }
}
/*/>*/
/*< selectm controls *******************************************************************/
fValidate.prototype.selectm = function( minS, maxS )
{
  if ( this.typeMismatch( 'sm' ) ) return;
  if ( typeof minS == 'undefined' )
  {
    this.paramError( 'minS' );
  }
  if ( maxS == 999 || maxS == '*' || typeof maxS == 'undefined' || maxS > this.elem.length ) maxS = this.elem.length;

  var count = 0;  
  for ( var opt, i = 0; ( opt = this.elem.options[i] ); i++ )
  {
    if ( opt.selected ) count++;
  }

  if ( count < minS || count > maxS )
  {
    this.throwError( [minS, maxS, this.elem.fName, count] );
  }
}
/*/>*/
/*< selecti controls *******************************************************************/
fValidate.prototype.selecti = function( indexes )
{
  
  if ( this.typeMismatch( 's1' ) ) return;
  if ( typeof indexes == 'undefined' )
  {
    this.paramError( 'indexes' );
    return;
  }
  indexes = indexes.split( "," );
  var selectOK = true;

  for ( var i = 0; i < indexes.length; i++ )
  {
    if ( this.elem.options[indexes[i]].selected )
    {
      selectOK = false;
      break;
    }
  }
  if ( !selectOK )
  {
    this.throwError( [this.elem.fName] );
  }
}
/*/>*/
/*< cazip international *******************************************************************/
fValidate.prototype.cazip = function()
{
  var elem = this.elem;
  if ( this.typeMismatch( 'text' ) ) return;
  elem.value = elem.value.toUpperCase();
  if ( !( /^[A-Z][0-9][A-Z] [0-9][A-Z][0-9]$/.test( elem.value ) ) )
  {
    this.throwError();
  }
}
fValidate.prototype.capost = fValidate.prototype.cazip;
/*/>*/
/*< ukpost international *******************************************************************/
fValidate.prototype.ukpost = function()
{
  var elem = this.elem;
  if ( this.typeMismatch( 'text' ) ) return;
  elem.value = elem.value.toUpperCase();
  if ( !( /^[A-Z]{1,2}\d[\dA-Z] ?\d[A-Z]{2}$/.test( elem.value ) ) )
  {
    this.throwError();
  }
}
/*/>*/
/*< germanpost international *******************************************************************/
fValidate.prototype.germanpost = function()
{
  var elem = this.elem;
  if ( this.typeMismatch( 'text' ) ) return;
  elem.value = elem.value.toUpperCase();
  if ( !( /^(?:CH\-)\d{4}$/.test( elem.value ) ) )
  {
    this.throwError();
  }
}
/*/>*/
/*< swisspost international *******************************************************************/
fValidate.prototype.swisspost = function()
{
  var elem = this.elem;
  if ( this.typeMismatch( 'text' ) ) return;
  elem.value = elem.value.toUpperCase();
  if ( !( /^(?:D\-)\d{5}$/.test( this.elem.value ) ) )
  {
    this.throwError();
  }
}
/*/>*/
/*< email web *******************************************************************/
fValidate.prototype.email = function( level )
{
  if ( this.typeMismatch( 'text' ) ) return;
  if ( typeof level == 'undefined' ) level = 0;
  var emailPatterns = [
    /.+@.+\..+$/i,
    /^\w.+@\w.+\.[a-z]+$/i,
    /^\w[-_a-z~.]+@\w[-_a-z~.]+\.[a-z]{2}[a-z]*$/i,
    /^\w[\w\d]+(\.[\w\d]+)*@\w[\w\d]+(\.[\w\d]+)*\.[a-z]{2,7}$/i
    ];
  if ( ! emailPatterns[level].test( this.elem.value ) )
  {
    this.throwError();
  }  
}  
/*/>*/
/*< url web *******************************************************************/
fValidate.prototype.url = function( hosts, hostOptional, allowQS )
{
  if ( this.typeMismatch( 'text' ) ) return;

  this.setArg( hosts, "http" );
  
  var front = "^(?:(" + hosts.replace( /\,/g, "|" ) + ")\\:\\/\\/)";
  var end   = ( Boolean( allowQS ) == true ) ? "(\\?.*)?$" : "$";

  if ( Boolean( hostOptional ) == true ) front += "?";
  var regex = new RegExp( front + "([\\w\\d-]+\\.?)+" + end );
  
  if ( !regex.test( this.elem.value ) )
  {
    this.throwError( [this.elem.fName] );
  }
}  
/*/>*/
/*< ip web *******************************************************************/
fValidate.prototype.ip = function( portMin, portMax )
{
  if ( this.typeMismatch( 'text' ) ) return;
  portMin = this.setArg( portMin, 0 );
  portMax = this.setArg( portMax, 99999 );
  if ( !( /^\d{1,3}(\.\d{1,3}){3}(:\d+)?$/.test( this.elem.value ) ) )
  {
    this.throwError();
  }
  else
  {
    var part, i = 0, parts = this.elem.value.split( /[.:]/ );
    while ( part = parts[i++] )
    {
      if ( i == 5 ) // Check port
      {
        if ( part < portMin || part > portMax )
        {
          this.throwError( [part, portMin, portMax], 1 );
        }
      }
      else if ( part < 0 || part > 255 )
      {
        this.throwError();
      }
    }
  }
}
/*/>*/
/*< decimal numbers *******************************************************************/
fValidate.prototype.decimal = function( lval, rval )
{
  if ( this.typeMismatch( 'text' ) ) return;
  var regex = '', elem = this.elem;
  if ( lval != '*' ) lval = parseInt( lval, 10 );
  if ( rval != '*' ) rval = parseInt( rval, 10 );
  
  if ( lval == 0 )
    regex = "^\\.[0-9]{" + rval + "}$";  
  else if ( lval == '*' )
    regex = "^[0-9]*\\.[0-9]{" + rval + "}$";
  else if ( rval == '*' )
    regex = "^[0-9]{" + lval + "}\\.[0-9]+$";
  else
    regex = "^[0-9]{" + lval + "}\\.[0-9]{" + rval + "}$";
    
  regex = new RegExp( regex );

  if ( !regex.test( elem.value ) )
  {
    this.throwError( [elem.value,elem.fName] );
  }  
}
/*/>*/
/*< decimalr numbers *******************************************************************/
fValidate.prototype.decimalr = function( lmin, lmax, rmin, rmax )
{
  if ( this.typeMismatch( 'text' ) ) return;
  lmin = ( lmin == '*' )? 0 : parseInt( lmin, 10 );
  lmax = ( lmax == '*' )? '': parseInt( lmax, 10 );
  rmin = ( rmin == '*' )? 0 : parseInt( rmin, 10 );
  rmax = ( rmax == '*' )? '': parseInt( rmax, 10 );
  var  decReg = "^[0-9]{"+lmin+","+lmax+"}\\.[0-9]{"+rmin+","+rmax+"}$"
  var regex = new RegExp(decReg);
  if ( !regex.test( this.elem.value ) )
  {
    this.throwError( [this.elem.fName] );
  }
  return true;
}
/*/>*/
/*< zip extended *******************************************************************/
fValidate.prototype.zip = function( sep )
{
  if ( this.typeMismatch( 'text' ) ) return;
  sep = this.setArg( sep, "- " );
  var regex = new RegExp( "^[0-9]{5}(|[" + sep.toPattern() + "][0-9]{4})?$" );
  if ( ! regex.test( this.elem.value ) )
  {
    this.throwError();
  }
}
/*/>*/
/*< phone extended *******************************************************************/
fValidate.prototype.phone = function( format )
{
  if ( this.typeMismatch( 'text' ) ) return;
  format       = this.setArg( format, 0 );
  var patterns = [
    /^(\(?\d\d\d\)?)?[ -]?\d\d\d[ -]?\d\d\d\d$/,  //  loose
    /^(\(\d\d\d\) )?\d\d\d[ -]\d\d\d\d$/      //  strict
    ];
  if ( !patterns[format].test( this.elem.value ) )
  {
    if ( format == 1 )
    {
      this.throwError();
    } else {
      this.throwError( [], 1 );
    }
  }
}
/*/>*/
/*< phone extended with blank ok *******************************************************************/
fValidate.prototype.phonebok = function( format )
{
  if ( this.typeMismatch( 'text' ) ) return;
  format       = this.setArg( format, 0 );
  var patterns = [
    /^((\(?\d\d\d\)?)?[ -]?\d\d\d[ -]?\d\d\d\d)?$/,  //  loose
    /^((\(\d\d\d\) )?\d\d\d[ -]\d\d\d\d)?$/      //  strict
    ];
  if ( !patterns[format].test( this.elem.value ) )
  {
    if ( format == 1 )
    {
      this.throwError();
    } else {
      this.throwError( [], 1 );
    }
  }
}
/*/>*/
/*< password *******************************************************************/
fValidate.prototype.password = function( length, symbols, numbers )
{
  var error = false;
  if( this.elem.value.search(/\s/) > -1) error = true;
  if( this.elem.value.length < length) error = true; 
  if( this.elem.value.length - this.elem.value.replace(/[0-9]/g, "").length < numbers ) error = true; 
  if( this.elem.value.length - this.elem.value.replace(/[0-z]/g, "").length < symbols ) error = true; 
  if ( error )
  {
      this.throwError([length,symbols,numbers]);
  }
}
/*/>*/
/*< date datetime *******************************************************************/
fValidate.prototype.date = function( formatStr, delim, code, specDate )
{
  if ( this.typeMismatch( 'text' ) ) return;
  if ( typeof formatStr == 'undefined' )
  {
    this.paramError( 'formatStr' );
    return;
  }

  delim = this.setArg( delim, "/" );

  var error  = [this.elem.fName, formatStr.replace( /\//g, delim )];
  var format  = formatStr.split( "/" );
  var compare = this.elem.value.split( delim );
  var order   = new Object();
  
  for ( var i = 0; i < format.length; i++ )
  {
    switch( format[i].charAt( 0 ).toLowerCase() )
    {
      case 'm' :
        order.months = i;
        break;
      case 'd' :
        order.days = i;
        break;
      case 'y' :
        order.years = i;
        break;
    }
  }
  var thisDate = new Date( compare[order.years], compare[order.months]-1, compare[order.days] );
  
  if ( isNaN( thisDate ) || thisDate.getDate() != compare[order.days] || thisDate.getMonth() != compare[order.months]-1 || thisDate.getFullYear().toString().length != formatStr.match( /y/g ).length )
  {
    this.throwError( error );
    return;
  }
  
  var compareElem = this.form.elements[specDate];
  if ( typeof compareElem != 'undefined' )
  {
    specDate = compareElem.validDate || compareElem.value;
  }
  var compareDate = ( specDate == 'today' )?
    new Date():
    new Date( specDate );
  compareDate.setHours(0);
  compareDate.setMinutes(0);
  compareDate.setSeconds(0);
  compareDate.setMilliseconds(0);
  
  var timeDiff = compareDate.getTime() - thisDate.getTime();
  var dateOk   = false;
  
  switch ( parseInt( code ) ) {
    case 1 :  // Before specDate
      dateOk  = Boolean( timeDiff > 0 );
      error  = 1;
      break;
    case 2 :  // Before or on specDate
      dateOk  = Boolean( ( timeDiff + 86400000 ) > 0 );
      error  = 2;
      break;
    case 3 :  // After specDate
      dateOk  = Boolean( timeDiff < 0 );
      error  = 3;
      break;
    case 4 :  // After or on specDate
      dateOk  = Boolean( ( timeDiff - 86400000 ) < 0 );
      error  = 4;
      break;
    default : dateOk = true;
    }
  if ( !dateOk )
  {
    this.throwError( [specDate], error );
  }
  this.elem.validDate = thisDate.toString();
} 
/*/>*/
/*< time *******************************************************************/
fValidate.prototype.time = function( formatStr, code, specTime )
{
	
  if ( this.typeMismatch( 'text' ) ) return;
  if ( typeof formatStr == 'undefined' )
  {
    this.paramError( 'formatStr' );
    return;
  }

	specTime = this.setArg( specTime, 0 );
  	var error  = [this.elem.fName, formatStr];

	var regexp = formatStr.replace(/hh/, "([0-1][0-9])");
	regexp = regexp.replace(/h/, "(1[012]|\\d)");
	regexp = regexp.replace(/mm/, "([0-5][0-9])");
	regexp = regexp.replace(/m/, "([1-5]?[0-9])");
	regexp = regexp.replace(/tt/, "([Aa][Mm]|[Pp][Mm])");
	regexp = regexp.replace(/t/, "([Aa]|[Pp])");

  if ( this.elem.value.search(new RegExp(regexp)) == -1 )
  {
    this.throwError(  error );
  } 
}

/*/>*/
/*< money ecommerce *******************************************************************/
fValidate.prototype.money = function( ds, grp, dml )
{
  if ( this.typeMismatch( 'text' ) ) return;
  
  ds  = ( ds == ' ' )  ? false : ds.toPattern();
  grp = ( grp == ' ' ) ? false : grp.toPattern();
  dml = ( dml == ' ' ) ? false : dml.toPattern();
  
  var moneySyntax, pattern;
  
  switch( true )
  {
    case Boolean( ds && grp && dml ):    // Dollar sign, grouping, and decimal
      pattern    = "^" + ds + "(?:(?:[0-9]{1,3}" + grp + ")(?:[0-9]{3}" + grp + ")*[0-9]{3}|[0-9]{1,3})(" + dml + "[0-9]{2})$";
      moneySyntax = ds + "XX" + grp + "XXX" + dml + "XX";
      break;
    case Boolean( ds && grp && !dml ):    // Dollar sign and grouping
      pattern    = "^" + ds + "(?:(?:[0-9]{1,3}" + grp + ")(?:[0-9]{3}" + grp + ")*[0-9]{3}|[0-9]{1,3})$";
      moneySyntax = "" + ds + "XX" + grp + "XXX";
      break;
    case Boolean( ds && !grp && dml ):    // Dollar sign and decimal
      pattern    ="^" + ds + "[0-9]*(\\.[0-9]{2})$";
      moneySyntax ="" + ds + "XXXXX" + dml + "XX";
      break;
    case Boolean( !ds && grp && dml ):    // Grouping and decimal
      pattern    ="^(?:(?:[0-9]{1,3}" + grp + ")(?:[0-9]{3}" + grp + ")*[0-9]{3}|[0-9]{1,3})(" + dml + "[0-9]{2})?$";
      moneySyntax ="XX" + grp + "XXX" + dml + "XX";
      break;
    case Boolean( ds && !grp && !dml ):    // Dollar sign only
      pattern    ="^" + ds + "[0-9]*$";
      moneySyntax ="" + ds + "XXXXX";
      break;
    case Boolean( !ds && grp && !dml ):    // Grouping only
      pattern    ="^(?:(?:[0-9]{1,3}" + grp + ")(?:[0-9]{3}" + grp + ")*[0-9]{3}|[0-9]{1,3})$";
      moneySyntax ="XX" + grp + "XXX";
      break;
    case Boolean( !ds && !grp && dml ):    // Decimal only
      pattern    ="^[0-9]*(" + dml + "[0-9]{2})$";
      moneySyntax ="XXXXX" + dml + "XX";
      break;
    case Boolean( !ds && !grp && !dml ):  // No params set, all special chars become optional
      pattern    ="^.?(?:(?:[0-9]{1,3}.?)(?:[0-9]{3}.?)*[0-9]{3}|[0-9]{1,3})(.[0-9]{2})?$";
      moneySyntax ="[?]XX[?]XXX[?XX]";
  }
      
  var regex = new RegExp( pattern );
  if ( !regex.test( this.elem.value ) )
  {
    this.throwError( [this.elem.fName, moneySyntax.replace( /\\/g, '' )] );
  }
}
/*/>*/
/*< checkbox controls *******************************************************************/
fValidate.prototype.checkbox = function( minC, maxC )
{
  if ( this.typeMismatch( 'cb' ) ) return;
  if ( typeof minC == 'undefined' )
  {
    this.paramError( 'minC' );
    return;
  }
  if ( this.elem == this.form.elements[this.elem.name] && !this.elem.checked )
  {
    this.throwError( [this.elem.fName] );
  }
  else
  {
    this.elem = this.form.elements[this.elem.name];
    var len   = this.elem.length;
    var count = 0;
    
    if ( maxC == 999 || maxC == '*' || typeof maxC == 'undefined' || maxC > this.elem.length )
    {
      maxC == len;
    }
    var i = len;
    while( i-- > 0 )
    {
      if ( this.elem[i].checked )
      {
        count++;
      }
    }
    if ( count < minC || count > maxC )
    {
      this.throwError( [minC, maxC, this.elem[0].fName, count] );
    }      
  }
}
/*/>*/
/*< radio controls *******************************************************************/
fValidate.prototype.radio = function()
{
  if ( this.typeMismatch( 'rg' ) ) return;
  if ( this.elem == this.form.elements[this.elem.name] && !this.elem.checked )
  {
    this.throwError( [this.elem.fName] );
  }
  else
  {
    this.elem = this.form.elements[this.elem.name];
    
    for ( var i = 0; i < this.elem.length; i++ )
    {
      if ( this.elem.item( i ).checked )
      {
        return;
      }
    }
    this.throwError( [this.elem[0].fName] );
  }
}
/*/>*/
/*< eitheror logical *******************************************************************/
fValidate.prototype.eitheror = function()
{
  if ( this.typeMismatch( 'hidden' ) ) return;
  if ( typeof arguments[0] == 'undefined' )
  {
    this.paramError( 'delim' );
    return;
  }
  if ( typeof arguments[1] == 'undefined' )
  {
    this.paramError( 'fields' );
    return;
  }
  var arg, i  = 0,
    fields  = new Array(),
    field,
    nbCount = 0,    
    args    = arguments[1].split( arguments[0] );    

  this.elem.fields = new Array();
  
  while ( arg = args[i++] )
  {
    field = this.form.elements[arg];
    fields.push( field.fName );
    this.elem.fields.push( field );

    if ( !this.isBlank( arg ) )
    {
      nbCount++;
    }
  }
  if ( nbCount != 1 )
  {
    this.throwError( [fields.join( "\n\t-" )] );
  }
}
/*/>*/
/*< atleast logical *******************************************************************/
fValidate.prototype.atleast = function()
{
  if ( this.typeMismatch( 'hidden' ) ) return;
  if ( typeof arguments[0] == undefined )
  {
    this.paramError( 'qty' );
    return;
  }
  if ( typeof arguments[1] == undefined )
  {
    this.paramError( 'delim' );
    return;
  }
  if ( typeof arguments[2] == undefined )
  {
    this.paramError( 'fields' );
    return;
  }
  var arg, i  = 0,
    fields  = new Array(),
    field,
    nbCount = 0,
    args    = arguments[2].split( arguments[1] );

  this.elem.fields = new Array();
  
  while ( arg = args[i++] )
  {
    field = this.form.elements[arg];
    fields.push( field.fName );
    this.elem.fields.push( field );

    if ( !this.isBlank( arg ) )
    {
      nbCount++;
    }
  }
  if ( nbCount < arguments[0] )
  {
    this.throwError( [arguments[0], fields.join( "\n\t-" ), nbCount] );
  }
}
/*/>*/
/*< allornone logical *******************************************************************/
fValidate.prototype.allornone = function()
{
  if ( this.typeMismatch( 'hidden' ) ) return;
  if ( typeof arguments[0] == 'undefined' )
  {
    this.paramError( 'delim' );
    return;
  }
  if ( typeof arguments[1] == 'undefined' )
  {
    this.paramError( 'fields' );
    return;
  }
  var arg, i  = 0,
    fields  = new Array(),
    field,
    nbCount = 0,
    args    = arguments[1].split( arguments[0] );
  
  this.elem.fields = new Array();

  while ( arg = args[i++] )
  {
    field = this.form.elements[arg];
    fields.push( field.fName );
    this.elem.fields.push( field );

    if ( !this.isBlank( arg ) )
    {
      nbCount++;
    }
  }
  if ( nbCount > 0 && nbCount < args.length )
  {
    this.throwError( [fields.join( "\n\t-" ), nbCount] );
  }
}
/*/>*/
/*< comparison logical *******************************************************************/
fValidate.prototype.comparison = function( field1, operator, field2 )
{
  if ( this.typeMismatch( 'hidden' ) ) return;
  var elem1  = this.form.elements[field1],
    elem2  = this.form.elements[field2],
    value1  = this.getValue( elem1 ),
    value2  = this.getValue( elem2 );
    i18n  = this.i18n.comparison;
    i    = -1;
  
  var operators =
  [
    ['>',  i18n.gt],
    ['<',  i18n.lt],
    ['>=',  i18n.gte],
    ['<=',  i18n.lte],
    ['==',  i18n.eq],
    ['!=',  i18n.neq]
  ];
  while( operators[++i][0] != operator ){ }
  this.elem.fields = [elem1, elem2];
  if ( ! eval( value1 + operator + value2 ) )
  {
    this.throwError( [elem1.fName, operators[i][1], elem2.fName] );
  }
}
/*/>*/
/*< file controls *******************************************************************/
fValidate.prototype.file = function( extensions, cSens )
{
  if ( this.typeMismatch( 'file' ) ) return;
  if ( typeof extensions == 'undefined' )
  {
    this.paramError( 'extensions' );
    return;
  }
  cSens = Boolean( cSens ) ? "" : "i";
  var regex = new RegExp( "^.+\\.(" + extensions.replace( /,/g, "|" ) + ")$", cSens );
  if ( ! regex.test( this.elem.value ) )
  {
    this.throwError( [extensions.replace( /,/g, "\n" )] );
  }
    var RX = new RegExp(".*,.*");
  if( RX.test( this.elem.value ) )
  {
	  this.throwError( [extensions.replace( /,/g, "\n" )] );
  }
}
/*/>*/
/*< custom special *******************************************************************/
fValidate.prototype.custom = function( flags, reverseTest )
{
  if ( this.typeMismatch( 'text' ) ) return;
  flags     = ( flags ) ? flags.replace( /[^gim]/ig ) : "";
  var regex = new RegExp( this.elem.getAttribute( this.config.pattern ), flags );
  if ( !regex.test( this.elem.value ) )
  {
    this.throwError( [this.elem.fName] );
  }  
}
/*/>*/
/*< cc ecommerce *******************************************************************/
fValidate.prototype.cc = function(oName)
{
  if ( this.typeMismatch( 'text' ) ) return;
  var typeElem = this.form.elements[this.config.ccType];

  if ( !typeElem )
  {
  	var typeElem = this.form.elements[oName];
  }
  if ( !typeElem )
  {
    this.devError( 'noCCType' )
    return;
  }
	
  if(typeElem.item(0).tagName.toUpperCase() == "INPUT") {
	for(var i = 0; i < typeElem.length ;i++) {
    	if(typeElem.item(i).checked) {
        	var ccType = typeElem.item(i).value;
    	}
	}
  } else {
  	var ccType   = typeElem.options[typeElem.selectedIndex].value.toUpperCase();
  }
  var types    = {
    'VISA'    : /^4\d{12}(\d{3})?$/,
    'MC'      : /^5[1-5]\d{14}$/,
    'DISC'    : /^6011\d{12}$/,
    'AMEX'    : /^3(4|7)\d{13}$/,        
    'DINERS'  : /^3[0|6|8]\d{12}$/,
    'ENROUTE' : /^2[014|149]\d{11}$/,
    'JCB'     : /^3[088|096|112|158|337|528]\d{12}$/,
    'SWITCH'  : /^(49030[2-9]|49033[5-9]|49110[1-2]|4911(7[4-9]|8[1-2])|4936[0-9]{2}|564182|6333[0-4][0-9]|6759[0-9]{2})\d{10}(\d{2,3})?$/,
    'DELTA'   : /^4(1373[3-7]|462[0-9]{2}|5397[8|9]|54313|5443[2-5]|54742|567(2[5-9]|3[0-9]|4[0-5])|658[3-7][0-9]|659(0[1-9]|[1-4][0-9]|50)|844[09|10]|909[6-7][0-9]|9218[1|2]|98824)\d{10}$/,
    'SOLO'    : /^(6334[5-9][0-9]|6767[0-9]{2})\d{10}(\d{2,3})?$/
    };
  if ( typeElem.validated == false && this.groupError == true ) return;
  if ( typeof types[ccType] == 'undefined' && typeElem.validated == false && this.groupError == false )
  {
    this.devError( [ccType] );
    return;
  }
  this.elem.value = this.elem.value.replace( /[^\d]/g, "" );
  if ( !types[ccType].test( this.elem.value ) || !this.elem.value.luhn() )
  {
    this.throwError( [this.elem.fName] );
  }
}

String.prototype.luhn = function()
{
  var i = this.length;
  var checkSum = "", digit;
  if(i % 2 == 0) {
	  while ( digit = this.charAt( --i ) )
	  {
		checkSum += ( i % 2 == 0 ) ? digit * 2 : digit;
	  }
  } else {
	  while ( digit = this.charAt( --i ) )
	  {
		checkSum += ( i % 2 == 1 ) ? digit * 2 : digit;
	  }
  }
  checkSum = eval( checkSum.split('').join('+') );
  return ( checkSum % 10 == 0 );
}
/*/>*/
/*< ccDate ecommerce *******************************************************************/
fValidate.prototype.ccDate = function( month, year )
{
  if ( this.typeMismatch( 's1' ) ) return;
  year  = parseInt( this.getValue( this.form.elements[year] ), 10 ) + 2000;
  month  = parseInt( this.getValue( this.form.elements[month] ), 10 );

  var today  = new Date();
  var expDate = new Date( year, month )

  if ( expDate < today )
  {
    alert( ["Card Expired",today,expDate].join( "\n" ) );
  }
}
/*/>*/
/*  EOF */
