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(); // this is scrolling the page down to the footer since we're using it on the subscribers field
  },
  addRequired: function()
  {
    var self = this;
    self.$el.find('.group h2.required').each(function(){
      $j(this).append('<span class="required">&nbsp;*</span>');
    });
  },
  attachFrmHandler: function()
  {
    var self = this;
    
    self.$el.submit(function(e){
      return self.sendFrm();
    });
    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) );
      }
    });
    self.$el.find(':input').bind('focus',function(){ $j(this).addClass('active') });
    self.$el.find(':input').bind('blur',function(){ $j(this).removeClass('active') });
  },
  sendFrm: 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 '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;
      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');
  }
}