/*!
 * Form client side validation
 *
 * @author Alex Leon
 * 
 * Date: 2009-02-21
 * Revision: 1.0
 *
 * 
 *
 */  
 
function Frm( el, callback ){
  var self = this;
  self.init( el, callback );
}
Frm.prototype = {
  init: function( el, callback )
  {
    var self = this;
        self.$el = el;
        self.callback = callback!=undefined ? callback : null;
    this.addRequired();
    this.attachFrmHandler();
    self.$el.find(':text:first').focus();
  },
  addRequired: function()
  {
    var self = this;
    // for the admin side
    self.$el.find('label.required').each(function(){
      $j(this).find('strong').append('<span class="required">&nbsp;*</span>');
    });
    // for the donation form
    self.$el.find('tr.required').each(function(){
      $j(this).find('label').append('<span class="required">&nbsp;*</span>');
    });
  },
  attachFrmHandler: function()
  {
    var self = this;
    self.$el.submit(function(){
      return self.process();
    });
    self.$el.find(':input[required="yes"],:checkbox[required="yes"]').bind('blur', function(){  
      if($j(this).val() != ''||$j(this).val() != 0||this.checked){
        self.passInput( $j(this) );
      }
    });
    self.$el.find(':text[validate],:password[validate]').bind('blur', function(){  
      var tvalidates = true;
      if( $j(this).attr('validate') != undefined && $j(this).val() != '' ){
        tvalidates = self.validateType( $j(this) );
      } else {
        tvalidates = true;
      }
      if( tvalidates ){
        self.passInput( $j(this) );
      }
    });
  },
  process: function()
  {
    var self = this;
    if( self.validateFrm() ){
      if( self.callback ){
        eval( self.callback+'()' );
        return false;
      } else {
        return true;
      }
    }
    return false;
  },
  validateFrm: function()
  {
    var self = this;
    var proceed = true;
    var frm = self.$el;
    
    // text fields
    // for text fields we need to catch fields that are not required but still need to validate as a type
    // we also need the returned array to be unique and the following line does that
    $j( $j.unique( self.$el.find(':text[required="yes"],:password[required="yes"],:text[validate],:password[validate]') ) )
      .each(
        function()
        {
          if($j(this).is('[required]') && $j(this).val() == ''){
            self.failInput( $j(this) );
            proceed = false;
          } else {
            var tvalidates = true;
            if( $j(this).attr('validate') != undefined && $j(this).val() != '' ){
              tvalidates = self.validateType( $j(this) );
            }
            if( tvalidates ){
              self.passInput( $j(this) );
            } else {
              self.failInput( $j(this) );
              proceed = tvalidates;
            }
          }
        }
      );
    
    // select lists
    frm.find('select[required="yes"]').each(function(){
      if($j(this).val() == ''||$j(this).val() == 0){
        self.failInput( $j(this) );
        proceed = false;
      } else {
        self.passInput( $j(this) );
      }
    });
    
    // radio options
    frm.find('.radio-wrapper').each(function(){
      var pass = false;
      $j(this).find('input[type="radio"]').each(function(){
        if(this.checked == true){
          pass = true;
        }
      });
      if(!pass){
        self.failInput( $j(this) );
        proceed = false;
      } else {
        self.passInput( $j(this) );
      }
    });

    // checkboxes
    frm.find(':checkbox[required="yes"]').each(function(){
      if(this.checked == false){
        self.failInput( $j(this) );
        proceed = false;
      } else {
        self.passInput( $j(this) );
      }
    });
    
    if(!proceed){
      alrt('You must properly fill out the highlighted fields in order to continue.');
      self.validationMsging();
    }
    return proceed;
  },
  validateType: function( $input )
  {
    var self = this;
    var proceed = true;
    var t = $input.attr('validate');
    var v = $input.val();
    switch( t )
    {
      case 'zip':
        var regx = /^\d{5}([\-]\d{4})?$/;
        proceed = (regx.test(v));
        break;   
      case 'email':
        var regx = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
        proceed = (regx.test(v));
        break;   
      case 'phone':
        var regx = /^\(?\d{3}(\)|\.|-|\s)?\d{3}(-|\.|\s)?\d{4}/;
        proceed = (regx.test(v));
        break;  
      case 'float':
        var regx = /^\d*(\.)?(\d){0,2}$/;
        proceed = (regx.test(v));
        break;
      case 'uint':
        var regx = /^\d+$/;
        proceed = (regx.test(v));
        break;
      case 'currency':
        var regx = /^[$]?[\d]+([\.]?[\d]{1,2}|)$/;
        // if the regex matches we should format the input to match our currency
        if( regx.test(v) ){
          var nval = v.replace('$','');
          if( v.match('.') ) nval = nval.split('.')[0];
          $input.val( nval );
        }
        proceed = (regx.test(v));
        break;
      case 'ccnum':
        var regx = /^[X]{4,}[\d]{4}$/;
        if( regx.test(v) ){
          // skip ccnum validation if the user has XXXXXXXXXXXX1234 in the field.
          proceed = true;
        } else {
          proceed = self.validateCCNum( v );
        }
        break;
      case 'address':
        var regx = /^[A-Za-z.#\d\s_-]+$/;
        proceed = (regx.test(v));
        break;
      case 'state':
        var regx = /^[A-Za-z]+$/;
        proceed = (regx.test(v));
        break;
      case 'date':
        // accepts XX/XX/XXXX format 
        var regx = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{4}$/;
        if( regx.test(v) ){
          proceed = self.validateDate( v );
        } else {
          proceed = false
        }
        break;
      case 'time':
        var regx = /^([1-9]|1[0-2]):[0-5]\d(:[0-5]\d(\.\d{1,3})?)?$/;
        proceed = (regx.test(v));
        break;
      default:
        
    }
    return proceed;
  },
  validationMsging: function()
  {
    var self = this;
    var frm = self.$el;
    frm.find(':input.failed').each( function(){
      $j(this).attr('title', $j(this).attr('message') );
      $j(this).tooltip({
      	track: true,
      	delay: 0,
      	showURL: false,
      	showBody: " - ",
        extraClass: "failed",
      	fade: 250
      });
    });
  },
  failInput: function( $input )
  {
    // mark the field as failed
    // if the element is invisible do NOT mark it as failed
    // just skip it and let the server handle the validation
    var vis = $input.is(':visible');
    $input.parents().each(function(){
      if( ! $j(this).is(':visible') ){
        vis = false;
        return false;
      }
    })
    if( vis ){
      $input.addClass('failed');
    }
    return vis;
  },
  passInput: function( $input )
  {
    $input.removeClass('failed');
    $input.parent().removeClass('failed');
  },
  validateCCNum: function( s )
  {
    // replace any white space in the card number
  	var number = s.replace(/\s+/g, '') ;
    //replace if contains non-numbers
  	if(number.match(/\D/)) return false;
  	// convert to array and reverse the number
  	number = number.split('').reverse().join('') ;
  	// loop through the number one digit at a time
  	// double the value of every second digit starting
  	// from the right, and concatenate the new values 
  	// with the unaffected digits
  	var digits = '';
  	for(var i = 0; i < number.length; i++){
  		digits += '' + ((i%2) ?
  			number.charAt(i) * 2 :
  			number.charAt(i)) ;
  	}
  	// add all of the single digits together
  	var sum = 0 ;
  	for (var i = 0; i < digits.length; i++){
  		sum += (digits.charAt(i) * 1) ;
  	}
  	// valid card numbers will be transformed into
  	// a multiple of 10
    return (sum % 10) ? false : true ;
  },
  validateDate: function( d )
  {
    var strSeparator = d.substring(2,3) 
    var arrayDate = d.split(strSeparator); 
    //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}
    var intDay = parseInt(arrayDate[1],10); 
    //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 (bugfix 20050322)
    var intMonth = parseInt(arrayDate[0],10);
    if (intMonth == 2) { 
       var intYear = parseInt(arrayDate[2]);
       if (intDay > 0 && intDay < 29) {
           return true;
       }
       else if (intDay == 29) {
         if ((intYear % 4 == 0) && (intYear % 100 != 0) || 
             (intYear % 400 == 0)) {
              // year div by 4 and ((not div by 100) or div by 400) ->ok
             return true;
         }   
       }
    }
  }
}