// ===== validated =====
// Author: Eli McAngus (eli.mcangus@gmail.com)
// Version: 0.6.5
// Last Updated: 10.28.2009
// License: MIT
// Requirements: Prototype >1.6
//
// Usage: 
// * give input elements a class validate_presence | validate_numeric | validate_email | validate_confirmation | validate_length
// * give input elements that use validate_length a class in one of the following forms:
//    validate_eq_xx | validate_lt_xx | validate_gt_xx
//    example: validate_lt_20
// * call Val.validate( _form_ , options) which will return true if all fields are valid
// * error messages will show next to every input field that has an issue (** unless told specifically to be grouped or alerted)
//   [DEPRECATED]** if a 'group_errors' class is added to the element a group_in_xxxx should also be added
//   [DEPRECATED]** xxxx should be the id of the target element where those errors will be shown
// * error messages can be re-styled as needed
// * a default value can be specified (optional) by an elements 'alt' attribute. validation will fail if the field value is the
//   same as the default value. this is useful for fields such as select-boxes where the first item in the drop down
//   is not blank and used as a label of sorts. 
//
// * validation classes are supported by the following elements
//   validate_presence ........ text, textarea, radio, select
//   validate_numeric ......... text, textarea
//   validate_email ........... text, textarea
//   validate_length .......... text, textarea
//   validate_phone ........... text, textarea
//   validate_confirmation  ... checkbox

//** can pass an options hash into the second param of the Validate call. right now only supported option is the messages type.
//   Validate(form, {messages:'short'}) will result in the short message beng used. supported are the following message types:
//      full  ............... default
//      short ............... message is as short as possible, only usable when rendering errors next to fields.
//      group ............... group all messages to some target area. targe area div should be give id {form_id}.error_group
//      alert ............... messages will all be grouped into an alert popup that fires at end of validation 

//err_display: short, full, none, group, alert

var Val = {
  
  valid_cls: $w('.validate_presence .validate_numeric .validate_email .validate_phone .validate_confirmation .validate_length'),
  regx_cls: /(validate_presence|validate_numeric|validate_email|validate_phone|validate_confirmation|validate_length)/g,  
  
  regx_email: /^.+@[^\.].*\.[a-z]{2,}$/,
  regx_phone: /^(\+\d{1}|\d){0,1}\W{0,1}\d{0,3}\W{0,1}\d{3}\W{0,1}\d{2}\W{0,1}\d{2}/,
  regx_eq: /(validate_eq_)[0-9]+/,
  regx_lt: /(validate_lt_)[0-9]+/,
  regx_gt: /(validate_gt_)[0-9]+/,  
  
  regx_err_group: /group_in_(.+ ?)/,

  error_msgs: [],
  options: {messages:'full', custom:false},

  validate:function(el, opts){
    this.clear_messages(el);  //clear out all old error messages    
    this.options = opts ? opts : this.options;  //set options
            
    var vrtn = true;
    $(el).select(this.valid_cls).each((function(field){
      if(this.is_displayed(field, el.id) && !field.disabled)      
        vrtn &= this.validate_field(field);
    }).bind(this));

    //if displaying errors in 'alert' mode
    if(!vrtn && this.options['messages'] == 'alert') 
      alert(this.error_msgs.join('\n'));

    return (vrtn ? true : false);
  },
  validate_field:function(el){
    var valid = true;
    
    $(el).className.match(this.regx_cls).each((function(type){        
      if(!eval('this.'+type+"(el)")) {
        valid &= false;        
        this.create_error_message(el, type);          
        this.set_error_styles(el);
      }
    }).bind(this));
            
    return valid;
  },
  
  validate_presence:function(el){
    
    //make sure field is NOT blank or only white space AND is different from the default value specified by ALT attribute
    if($(el).type == "text" || $(el).type == "textarea" || $(el).type == "select-one") 
// console.log($(el).identify() + ": " + $F(el) + " ...... " + $(el).readAttribute('alt'))
      return !(($F(el) == null) || $F(el).blank() || ($F(el) == $(el).readAttribute('alt')));
    else if($(el).type == "radio")
      return ($$('input[name="' + $(el).readAttribute('name') + '"]:checked').length >= 1)
    else
      return true;
  },
  validate_numeric:function(el){
    //make sure field is a number AND is different from the default value specified by ALT attribute
    if($(el).type == "text" || $(el).type == "textarea") 
      return !($F(el).blank() || isNaN($F(el).strip()) || ($F(el) == $(el).readAttribute('alt')));
    else
      return true;
  },
  validate_email:function(el){
    //make sure field is in a valid email format AND is different from the default value specified by ALT attribute
    if($(el).type == "text" || $(el).type == "textarea")
      return !( !this.regx_email.match($F(el).strip()) || ($F(el) == $(el).readAttribute('alt')) );
    else
      return true;
  },
  validate_phone:function(el){
    //make sure field is in a valid email format AND is different from the default value specified by ALT attribute
    if($(el).type == "text" || $(el).type == "textarea")
      return !( !this.regx_phone.match($F(el).strip()) || ($F(el) == $(el).readAttribute('alt')) );
    else
      return true;
  },  
  validate_confirmation:function(el){
    // make sure checkbox is checked
    return ($(el).type == "checkbox") ? $(el).checked : true;    
  },
  validate_length:function(el){
    //make sure field follows a second class rule
    //format: validate_eq_5, validate_lt_20, validate_gt_0
    //priority is eq, lt, gt...if eq will ignore both lt and gt
    //if both lt and gt are present will and/or logicially
    var valid = true

    if($(el).type == "text" || $(el).type == "textarea") {
      
      switch(true){
        case this.regx_eq.test($(el).className) :
          var parts = $(el).className.match(this.regx_eq)[0].split("_");
          valid = (parseInt(parts[2]) == $F(el).strip().length);
          break;
      
        case this.regx_lt.test($(el).className) :
          var parts = $(el).className.match(this.regx_lt)[0].split("_");
          var vlt = parseInt(parts[2]);
          var vgt = $(el).className.match(this.regx_gt) || null;
        
          if(vlt && vgt) {
            var len = $F(el).strip().length;
            vgt = parseInt(vgt[0].split("_")[2]);
                        
            if(vlt > vgt) 
              valid = (vgt < len) && (len < vlt);
            else 
              valid = (vgt < len) || (len < vlt);           
          }else { 
            valid = vlt > $F(el).length;
          }
          break;
        
        case this.regx_gt.test($(el).className) :
          var parts = $(el).className.match(this.regx_gt)[0].split("_");
          var vgt = parseInt(parts[2]);
          valid = vgt < $F(el).strip().length;          
          break;
      }
    } 
    
    return valid;
  },
  
  // create_error_message_old:function(el, type){
  //   //alert('this is: ' + $(el))
  //   //alert('next is: ' + $(el).next());
  //       
  //   if($(el).hasClassName('group_errors')) {
  //     var tar = null;
  //     
  //     if($(el).className.match(this.regx_err_group)) 
  //       tar = $(el).className.match(this.regx_err_group)[1];
  //     
  //     if($(tar)) {
  //       $(tar).insert( $(el).title + this.default_message(el, type) + '<br />' );
  //       $(tar).show();
  //     } else
  //       alert("error group does not exist: \n" + $(el).title + this.default_message(el, type) );
  //     
  //   } else if($(el).next() && ($(el).next().className == 'validate_error_msg_wrap')) {
  //     //already a message so append to it
  //     msg = $(el).next().down();
  //     msg.insert('<br />' + $(el).title + this.default_message(el, type));
  //     
  //   } else {              
  //     //wrapper
  //     var msgw = document.createElement('span');
  //     Element.addClassName(msgw, 'validate_error_msg_wrap');     //have to use generic for stupid IE
  //       
  //     //message          
  //     var msg = document.createElement('span');
  //     msg.innerHTML = $(el).title + this.default_message(el, type);
  //     Element.addClassName(msg, 'validate_error_msg');           //have to use generic for stupid IE
  // 
  //     msgw.insert(msg);      
  //     $(el).insert({'after':msgw});    
  //   }    
  //   
  // },
  create_error_message:function(el, type){
    //alert('this is: ' + $(el))
    //alert('next is: ' + $(el).next());
    
    this.error_msgs.push(this.default_message(el, type));
    
    switch(this.options['messages']){
      case "none", "alert" :  
        break;
            
      case "group" :
        var tar = null

        tar = $(el).up('form').id + "_error_group";
        
        if($(tar)){
          $(tar).insert( this.error_msgs.last() + '<br />' );
          $(tar).show();
        } 
        // else alert("error group does not exist: \n" + $(el).title + this.error_msgs.last() );
        break;
            
      default :
        if($(el).next() && ($(el).next().className == 'validate_error_msg_wrap')) {
          //already a message so append to it
          msg = $(el).next().down();
          msg.insert('<br />' + this.error_msgs.last());

        } else {              
          //wrapper
          var msgw = document.createElement('span');
          Element.addClassName(msgw, 'validate_error_msg_wrap');     //have to use generic for stupid IE

          //message          
          var msg = document.createElement('span');
          msg.innerHTML = this.error_msgs.last();
          Element.addClassName(msg, 'validate_error_msg');           //have to use generic for stupid IE

          msgw.insert(msg);      
          $(el).insert({'after':msgw});    
        }        
        break;      
    }    
  },
  
  set_error_styles:function(el){
    try{        
      $(el).addClassName('validate_error');
      //[el,el.up('div')].invoke('removeClassName','error');
    }catch(e){}
  },
  default_message:function(el, type){
    if(this.options["messages"] == 'short') 
      return ($(el).title + this.message_short(el, type));
    else if(this.options["custom"] == true)
      return (this.message_custom(el, type) + $(el).title);
    else
      return ($(el).title + this.message_full(el, type));
  },
  message_short:function(el, type){

    switch(type){
      case 'validate_presence' : return ' required'; break;
      case 'validate_numeric' : return ' number required'; break;
      case 'validate_email' : return ' email required'; break;
      case 'validate_phone' : return ' phone required'; break;
      case 'validate_confirmation' : return ' required'; break;
      
      case 'validate_length' : 
        var rtn = ' invalid length';
// console.log($(el).className)    
// console.log(this.regx_eq.test($(el).className))
        switch(true){
          case this.regx_eq.test($(el).className) :
            var parts = $(el).className.match(this.regx_eq)[0].split("_");            
            rtn = ' ' + parts[2] + ' char(s) required';
            break;

          case this.regx_lt.test($(el).className) :
            var parts = $(el).className.match(this.regx_lt)[0].split("_");
            var vlt = parseInt(parts[2]);
            var vgt = $(el).className.match(this.regx_gt) || null;

            if(vlt && vgt) {
              vgt = parseInt(vgt[0].split("_")[2]);
              if(vlt > vgt) 
                rtn = ' char(s) required &gt; ' + vgt + ' and &lt; ' + vlt;
                // rtn = ' field must be between ' + vgt + ' and ' + vlt + ' characters';
              else 
                rtn = ' char(s) required &lt; ' + vlt + ' or &gt; ' + vgt;
                // rtn = ' field must be less than ' + vlt + ' or greater than ' + vgt + ' characters';
            }else { 
              rtn = ' char(s) required &lt; ' + vlt;
              // rtn = ' field must be less than ' + vlt + ' character(s)';
            }
            break;

          case this.regx_gt.test($(el).className) :
            var parts = $(el).className.match(this.regx_gt)[0].split("_");
            var vgt = parseInt(parts[2]);
            rtn = ' char(s) required &gt; ' + vgt;
            // rtn = ' field must be greater than ' + vgt + ' characters';
            break;
        }
                  
        return rtn; 
        break;
        
      default : return 'field is invalid';
    }
  },  
  message_full:function(el, type){
    switch(type){
      case 'validate_presence' : return ' field can not be blank'; break;
      case 'validate_numeric' : return ' field must be a number'; break;
      case 'validate_email' : return ' field must be in a valid email format'; break;
      case 'validate_phone' : return ' field must be in a valid phone format'; break;
      case 'validate_confirmation' : return ' confirmation must be accepted'; break;
      
      case 'validate_length' : 
        var rtn = ' field has an invalid length';

        switch(true){
          case this.regx_eq.test($(el).className) :
            var parts = $(el).className.match(this.regx_eq)[0].split("_");            
            rtn = ' field must be exactly ' + parts[2] + ' character(s)';
            break;

          case this.regx_lt.test($(el).className) :
            var parts = $(el).className.match(this.regx_lt)[0].split("_");
            var vlt = parseInt(parts[2]);
            var vgt = $(el).className.match(this.regx_gt) || null;

            if(vlt && vgt) {
              vgt = parseInt(vgt[0].split("_")[2]);
              if(vlt > vgt) 
                rtn = ' field must be between ' + vgt + ' and ' + vlt + ' characters';
              else 
                rtn = ' field must be less than ' + vlt + ' or greater than ' + vgt + ' characters';
            }else { 
              rtn = ' field must be less than ' + vlt + ' character(s)';
            }
            break;

          case this.regx_gt.test($(el).className) :
            var parts = $(el).className.match(this.regx_gt)[0].split("_");
            var vgt = parseInt(parts[2]);
            rtn = ' field must be greater than ' + vgt + ' characters';
            break;
        }
                  
        return rtn; 
        break;
        
      default : return 'field is invalid';
    }
  },
  message_custom:function(el, type){
    return "Please enter your........";
  },
  is_displayed:function(el, el_limit){
    while(el){
      if(el.id == el_limit)
        break;
        
      if( $(el).getStyle('display') == 'none' ) // ... || $(el).getStyle('visbile') == 'hidden'
        return false;
        
      el = $(el).up();
    }
    
    return true;        
  },
  clear_messages:function(el){
    $(el).select('.validate_error_msg_wrap').invoke('remove');
    $(el).select('.validate_error').invoke('removeClassName', 'validate_error');
    
    // clear out error group if present
    var tar = $(el).id + "_error_group";    
    if($(tar)) $(tar).update('');
    
    this.error_msgs = [];
      
    // // clear out and hide error groups if any ... DEPRECATED
    // var err_groups = [];
    // $(el).select('.group_errors').each((function(elm){
    //   if(elm.className.match(this.regx_err_group))
    //     if($(elm.className.match(this.regx_err_group)[1]))
    //       err_groups.push( elm.className.match(this.regx_err_group)[1] )                
    // }).bind(this));
    // err_groups.uniq().each((function(id){
    //   $(id).update().hide();
    // }));
  }    
}




