/*-- 
Description: Object of regular expressions for use with validation. 
Refactored from: /J/js/valid.js
note: These were existing in our code. may need factoring
--*/
//Email regex must be the same as the one in SimplyHiredCommon public config.
function JValidBase(){
  this.errors   = {};
  this.handlers ={};//store association between form element and its handler
  this.error_descriptions = {};
  this.valid    = {
    address_max: 254,
    url_max: 1024,
    salary_max: 24,
    username_min: 1,
    username_max: 25,
    username: /(?!^[\.\_\-\']*$)^([a-z0-9\.\_\-\']{1,25})$/i,
    username_inv: /^[a-z0-9\.\_\-\']*$/i,

    password_min: 6,
    password_max: 15,
    password: /(?!^[a-z]*$)(?!^[\x21-\x40\x5B-\x60\x7B-\x7E]*$)^([\x21-\x7E]{6,15})$/i,
    password_inv: /^[\x21-\x7E]*$/,

    email_max: 100,
    email:/^[a-z0-9\-\_\+]+(\.[a-z0-9\-\_\+]+)*\@(([a-z0-9\-\_\+]+(\.[a-z0-9\-\_\+]+)*){1,}\.[a-z]{2,}|([0-9]+\.){3}[0-9]+)$/i, 

    url_protocol:/^(http|https):\/\//,
    url:/(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/,

    zip: /(^\d{5}$)|(^\d{5}-\d{4}$)/,

    name_min: 1,
    name_max: 25,
    name: /^[a-z\.\-\'\s]{1,25}$/i,

    valid_keyboard: /^[\x20-\x7E]*$/,
    valid_keyboard_anyspace: /^[\s\x20-\x7E]*$/,
    phone: /^\d[\d-\s]+(( x| ext)\d{1,5}){0,1}$/i,
    pay: /[^\d{1-6}.,]/,
    yrsexpnce: /^\d{1,2}$/,
    textarea_max: 500,
    text_max: 50,
    cc_verification: /^\d{3,4}$/,
    hexadecimal: /^#([0-9a-f]{3}){1,2}$/i,
    ssn: /^([0-6]\d{2}|7[0-6]\d|77[0-2])([ \-]?)(\d{2})\2(\d{4})$/,
    ein: /^(\d{2})([ \-]?)(\d{7})$/,

    end:0
  };

  this.invalid  = {
    phone555: /^([^\d]*)(\d{3})([^\d]*)(555)([^\d]*)(\d{4}).*?$/
  }
  
  //legacy hash supporting validateCreditCardNumber from validcc.js
  this.valid_cc = {
    all:  {'valid':/^(\d{13}|\d{15,16})$/, //total 13,15,16 digits
     'checkdigit':false },
    mc:   {'name':'MasterCard',
     'valid':/^(51|52|53|54|55)\d{14}$/, //total 16 digits, valid prefixes 51...555
     'checkdigit':true },
    visa: {'name':'Visa',
           'valid':/^(4)(\d{12}|\d{15})$/, //total 13,16 digits, valid prefix 4
           'checkdigit':true },
    amex: {'name':'American Express',
           'valid':/^(34|37)\d{13}$/, //total 15 digits, valid prefixes 34,37
           'checkdigit':true },
    disc: {'name':'Discover',
           'valid':/^(6011)\d{12}$/, //total 16 digits, valid prefix 6011
           'checkdigit':true }
  };  
};

//Check if form currently has any errors
JValidBase.prototype.hasErrors = function(){
  var _r = false;for(var i in this.errors){_r = true;break;}return _r;
}

//*-----isvalid?-----------------------------*/
//returns true if valid

//Description
JValidBase.prototype.isValidHexadecimal   = function(hex_value) {
  return !!this.valid.hexadecimal.exec(hex_value);
}

//Is this a valid number?
JValidBase.prototype.isValidNumber           = function(value) {
  return !isNaN(value);
}

//Description
JValidBase.prototype.isEmpty           = function(value) {
  return (!value || value==null || !value.length);
}

//Description
JValidBase.prototype.isValidKeyBoardOrSpace   = function(user_str) {
  return !!this.valid.valid_keyboard_anyspace.exec(user_str);
}

//Description
JValidBase.prototype.isValidUsername   = function(username,typeinv) {
  if (typeinv)
    return !!this.valid.username_inv.exec(username);
  else
    return !!this.valid.username.exec(username);
}

//Description
JValidBase.prototype.isValidName   = function(name) {
  return !!this.valid.name.exec(name);
}

//Description
JValidBase.prototype.isValidPassword   = function(password,typeinv) {
  if (typeinv)
    return !!this.valid.password_inv.exec(password);
  else
    return !!this.valid.password.exec(password);
}

//Description
JValidBase.prototype.isValidEmail      = function(email) {
  return !!this.valid.email.exec(email);
}

//checks format
JValidBase.prototype.isValidPhone      = function(phone) { 
  return !!this.valid.phone.exec(phone);
}

//checks url for protocol
JValidBase.prototype.isValidUrlProtocol      = function(url) { 
  return !!this.valid.url_protocol.exec(url);
}

//checks url format
JValidBase.prototype.isValidUrl      = function(url) { 
  return !!this.valid.url.exec(url);
}

//Phone cant be all same digits
//phone number is all the same digit
JValidBase.prototype.isPhoneAllSameDigit = function(phone){
  return phone.match(/^(0+|1+|2+|3+|4+|5+|6+|7+|8+|9+)$/);
}

//US phone must have valid prefix
//prefix starts with 0 or 1, ends in 11, or is 555
JValidBase.prototype.isValidUSPhonePrefix = function(phone){
  var exchange = phone.substr(3,3);
  return exchange.match(/^(0..|1..|.11|555)$/);
}

//US phone number must have a valid area code
//area code may not start with 0 or 1, end in 11, or be 700 or 900 or 555
JValidBase.prototype.isValidUSPhoneAreaCode = function(phone){
  var areacode = phone.substr(0,3);
  return areacode.match(/^(0..|1..|.11|700|900|555)$/);
}

//US phone number must be 10 digits long
JValidBase.prototype.isValidUSPhoneLength    = function(phone) {
  return (phone.length == 10);
}

//checks ZipCode format
JValidBase.prototype.isValidZip      = function(_zip) { 
  _zip = this.trim(_zip);
  return !!this.valid.zip.exec(_zip);
}

//checks Credit Card Verification Number
JValidBase.prototype.isValidCCVerificationNum      = function(_ccv) { 
  return !!this.valid.cc_verification.exec(_ccv);
}

//checks Social Security Number
JValidBase.prototype.isValidSSN      = function(_ssn) { 
  return !!this.valid.ssn.exec(_ssn);
}

//checks Employer Identification Number
JValidBase.prototype.isValidEIN      = function(_ein) { 
  return !!this.valid.ein.exec(_ein);
}


/*-----validate-----------------------------*/
//returns error code or false if valid

//Must contain only keyboard chars, be greater in character length than the min (unless length is 0), 
//and smaller than the max char length
//if _args contains an argument named optional and it equates to false than address must not be an empty string
//_args passed in as either null or an associative array of name/val pairs
JValidBase.prototype.validateAddress  = function(_address,_args) {
  var _optional = true;if(_args){_optional=_args.optional;}
  var error = false;

  _address = this.trim(_address);
  
  if(!_optional && _address.length==0)
    error = 'blank_addr';
  else if (!this.isValidKeyBoardOrSpace(_address))
    error = 'invalid_addr';          
  else if (_address.length > 0 && _address.length < this.valid.string_min)
    error = 'short_addr';    
  else if (_address.length > this.valid.address_max)
    error = 'long_addr';    
    
  return error;  
}

//Description
JValidBase.prototype.validateUsername  = function(username) {
  var error = false;
  username = this.trim(username);
  
  if (username == '')
    error = 'blank_un';
  else if (!this.isValidKeyBoardOrSpace(username))
    error = 'invalid_chars'; 
  else if (username.length < this.valid.username_min)
    error = 'short_un';
  else if (username.length > this.valid.username_max)
    error = 'long_un';
  else if (!this.isValidUsername(username))
    error = 'invalid_un';

  return error;
}

//name
JValidBase.prototype.validateName  = function(name) {
  var error = false;
  name = this.trim(name);
  
  if (name == '')
    error = 'blank_un';
  else if (!this.isValidKeyBoardOrSpace(name))
    error = 'invalid_chars'; 
  else if (name.length < this.valid.name_min)
    error = 'short_un';
  else if (name.length > this.valid.name_max)
    error = 'long_un';
  else if (!this.isValidName(name))
    error = 'invalid_un';

  return error;
}

//business name
JValidBase.prototype.validateBusinessName = function(name) {
  var error = false;
  name = this.trim(name);

  if (name == '') {
    error = 'blank_field';
  }

  return error;
}


//if _args contains an argument named optional and it equates to true than url can be empty string
//_args passed in as either null or an associative array of name/val pairs
JValidBase.prototype.validateUrl  = function(url,_args) {
  var _optional = false;if(_args){_optional=_args.optional;}
  var error = false;
  url=this.trim(url);

  if (!_optional && url == '')
    error = 'blank_url';
  else if (!this.isValidKeyBoardOrSpace(url))
    error = 'invalid_chars';   
  else if (!(_optional && url == '') && !this.isValidUrlProtocol(url))
    error = 'invalid_url_protocol';
  else if (!(_optional && url == '') && !this.isValidUrl(url))
    error = 'invalid_url';
  else if (!(_optional && url == '') && url.length>this.valid.url_max)
    error = 'long_url';
    

  return error;
}

//Description
JValidBase.prototype.validateZip  = function(_zip) {
  var error = false;

  if (_zip == '')
    error = 'blank_zip';
  else if (!this.isValidZip(_zip))
    error = 'invalid_zip';

  return error;
}

//Description
JValidBase.prototype.validateSalary  = function(_salary) {
  var error = false;

  if (!this.isValidKeyBoardOrSpace(_salary))
    error = 'invalid_salary';
  else if (_salary.length > this.valid.salary_max)
    error = 'long_salary';

  return error;
}

//Description
JValidBase.prototype.availableUsername = function(ok) {
  return ok ? 'un_available' : 'un_taken';
}

//Validate a Password field
JValidBase.prototype.validatePassword  = function(password,_args) {
  //If this is validation onsubmit, the confirm password is blank and this password is blank
  //then we can skip all validation.
  var okToSkip = false;
  if(_args){
    okToSkip = (_args.passwordconfirm.value=='' && password=='' && _args.event=='submit')?true:false;
  }
  var error = false;
  
  if(!okToSkip){
  //ToDo: this needs to be factored out
    if (password == '')
      error = 'blank_pswd';
    else if (!this.isValidPassword(password,1))
      error = 'bad_pswd';
    else if (password.length < this.valid.password_min)
      error = 'short_pswd';
    else if (password.length > this.valid.password_max)
      error = 'long_pswd';
    else if (password.search(/[a-z]/i) == -1)
      error = 'invalid_pswd_letter';
    else if (password.search(/[\x21-\x40\x5B-\x60\x7B-\x7E]/) == -1)
      error = 'invalid_pswd_special';
    else if (!this.isValidPassword(password))
      error = 'invalid_pswd';
  }
  
  return error;
}

//Confirm that passwords match.
//_args expected in format of {passwordnew:a input DOM object reference}
JValidBase.prototype.validatePasswordMatch   = function(password,_args) {
  var _passwordToMatch = (_args)?_args.passwordnew.value:_args;
  var error = false;
  //only confirm if both fields contains data - otherwise skip
  if(password != '' && _passwordToMatch != ''){
    if (password == '')
      error = 'blank_pswd2';
    else if (password != _passwordToMatch)
      error = 'mismatched_pswds';
  }
  return error;
}

//Validate a Password field
JValidBase.prototype.validatePasswordCurrent  = function(password,_args) {
  //If the current password, the new password, and the confirm password are all blank...
  //then we can skip all validation.
  
  var okToSkip = false;
  if(_args){
    okToSkip = (_args.passwordconfirm.value=='' && password=='' && _args.passwordnew.value=='')?true:false;
  }
  var error = false;
  
  if(!okToSkip){
    //ToDo: this needs to be factored out
    if (password == '')
      error = 'blank_pswd';
    else if (!this.isValidPassword(password,1))
      error = 'bad_pswd';
    else if (password.length < this.valid.password_min)
      error = 'short_pswd';
    else if (password.length > this.valid.password_max)
      error = 'long_pswd';
    else if (password.search(/[a-z]/i) == -1)
      error = 'invalid_pswd_letter';
    else if (password.search(/[\x21-\x40\x5B-\x60\x7B-\x7E]/) == -1)
      error = 'invalid_pswd_special';
    else if (!this.isValidPassword(password))
      error = 'invalid_pswd';
  }
  
  return error;
}

//Confirm that email entires match.
//
JValidBase.prototype.validateEmailMatch   = function(email,_args) {
  var _emailToMatch = (_args)?_args.email.value:_args;
  var error = false;

  if (email == '')
    error = 'blank_email2';
  else if (email != _emailToMatch)
    error = 'mismatched_email';

  return error;
}

//if _args contains an argument named optional and it equates to true than email can be empty string
//args passed in as either null or an associative array of name/val pairs
JValidBase.prototype.validateEmail       = function(email,_args) {
  //use more arguments if they exist.
  var _optional = false;if(_args){_optional=_args.optional;}
  var error = false;
  email = this.trim(email);

  if (!_optional && email == '')
    error = 'blank_email';
  else if (!this.isValidKeyBoardOrSpace(email))
    error = 'invalid_chars';     
  else if (email.length > this.valid.email_max)
    error = 'email_too_long';

  //check if invalid but ignore empty strings when the field is optional
  else if (!(_optional && email == '') && !this.isValidEmail(email))
    error = 'invalid_email';

  return error;
}

//Is the checkbox checked?
//arg 'check' expected to be checkbox dom object
//for backwards compatibility if it is not then it checks the optional argument for an object
JValidBase.prototype.validateCheckbox    = function(check,_args) {
  //find a valid object reference
  var _checkboxObj = (typeof(check)=='object')?check:(_args)?_args.checkbox:_args;
  var error = false;

  if (!_checkboxObj.checked)
    error = 'blank_'+_checkboxObj.name;

  return error;
}

//Is atleast one of the checkboxes checked within a given group? 
//arg 'checkboxNodeList' expected to be an associative array of checkbox dom objects
JValidBase.prototype.validateCheckboxGroup    = function(checkboxNodeList) {
  var error = 'not_checked';
  for (var i in checkboxNodeList){
    //is anything checked?
    if(checkboxNodeList[i].checked){
      error = false;
      break;
    }
  }

  return error;
}

//at least (only one) of the radio button is selected
JValidBase.prototype.validateRadioGroup = function(radioNodeList) {
  var error = 'not_selected';

  for (var i in radioNodeList) {
    if (radioNodeList[i].checked) {
      error = false;
      break;
    }
  }

  return error;
}


//Description
//if _args contains an argument named optional and it equates to false than phone must not be an empty string
JValidBase.prototype.validatePhone    = function(phone,_args) {
  var _optional = false;
  if(_args && typeof(_args.optional) != "undefined") {
    _optional=_args.optional;
  }
  var error = false;
  phone = this.trim(phone);
  
  //catch empty field when phone is not optional  
  if (!_optional && phone == ''){
    error = 'blank_phone';
    return error;//todo:hate having 2 returns
  }else if(_optional && phone == ''){
    return error;
  }

  if (!this.isValidPhone(phone))
    error = 'bad_phone';
    
  //normalize phone# if it's not an empty string
  var phone = phone.replace(/^(.*?)((ext|x).*?)?$/i,'$1'); //phone number only
  phone = phone.replace(/[^\d]/g,''); //remove all nondigits
 
  if (this.isPhoneAllSameDigit(phone))
    error = 'phone_num_same';
  
  return error;
}

//if args contains an argument named optional and it equates to false than fax must not be an empty string
//similiar to validatePhone except contains less descriptive error messages
JValidBase.prototype.validateFax = function(fax, args) {
  var optional = args ? args.optional : true;
  var error = false;
  
  fax = this.trim(fax);

  if (!optional && fax == '') {
    return 'blank_fax';
  }
  else if (optional && fax == '') {
    return false;
  }

  //generic fax validation
  if (!this.isValidPhone(fax)) {
    error = 'bad_fax';
  }

  //normalize fax # if it's not an empty string
  var mod_fax = fax.replace(/^(.*?)((ext|x).*?)?$/i,'$1'); //fax number only
  mod_fax = mod_fax.replace(/[^\d]/g,''); //remove all nondigits

  //more descriptive validation
  if (!this.isValidUSPhoneLength(mod_fax)) {
    error = 'bad_fax';
  }
  else if (this.isValidUSPhoneAreaCode(mod_fax)) {
    error = 'bad_fax';
  }
  else if (this.isValidUSPhonePrefix(mod_fax)) {
    error = 'bad_fax';
  }
  else if (this.isPhoneAllSameDigit(mod_fax)) {
    error = 'bad_fax';
  }

  return error;
}
  
//Determine if a string is blank. return error based on type of field
//for example a select element gets a different error string than an input
JValidBase.prototype.validateNotBlank    = function(user_str,args) {
  var type = (_args)?_args.type:_args;
  var error = false;

  user_str = this.trim(user_str);
  
  if (user_str == '' || user_str == null){
    error = (type=='field')?'blank_field':'blank_str';
  }
  
  return error;
}

//Description
JValidBase.prototype.validateForm    = function(user_str) {
  var error = false;

  if (user_str == '' || user_str == null)
    error = 'blank_str';
  
  return error;
}

//Validate the Credit Card Verification Number
JValidBase.prototype.validateCCVerificationNum = function(ccv) {
  var error = false;

  if (!this.isValidCCVerificationNum(ccv))
    error = 'invalid_ccv';
  
  return error;
}

//Based on legacy function from validcc.js - do for factoring
JValidBase.prototype.validateCreditCardNumber = function(number,_args) {
  var type = false;if(_args){type=_args.type;}
  var error = false;
  
  if (!type){
    error = 'cc_no_type';
  }else{
    if (type && !this.valid_cc[type]) 
      error = 'cc_type';

    var validnum   = this.valid_cc[type].valid; //regexp
    var checkdigit = this.valid_cc[type].checkdigit; //checkdigit

    var num = number.replace(/(\s+)/g,''); //remove all spaces

    if (!num.match(/^[\d]+$/)) //non-digits present?
      error = 'cc_characters';
    else if (!num.match(validnum)) //valid number?  length and prefix
       error = 'invalid_cc';
    else if (checkdigit && !checkLuhnAlgorithm(num)) //checkdigit routine
      error = 'cc_checkdigit';
  }
  return error;
}

//Validate expiration date
JValidBase.prototype.validateCreditCardExpDate = function(_month,_year) {
  var error = false;
  //both year and month args needed so do nothing if both not present
  //if(_month,_year){
    var now = new Date();
    var thismonth = now.getMonth()+1;
    var thisyear  = now.getFullYear();
    if (_year < thisyear || (_year == thisyear && _month < thismonth)){ 
      error = 'cc_expired';
    }
    if (_year > thisyear+10){
      error = 'cc_yy_toofar';
    }
  //}
  return error;
}
  
//legacy function supporting validateCreditCardNumber
function checkLuhnAlgorithm(number) {
  var num = number.split('');
  var len = number.length;

  var sum = 0;

  for (var i=0; i<len; i++) {
    var digit = parseInt(num.pop()); //get last digit
    if (i%2) {
      digit *= 2;
      if (digit > 9) digit -= 9;
    }
    sum += digit;
  }

  return (sum % 10) == 0;
}

//Validate entry is a number
JValidBase.prototype.validateNumber = function(value) {
  var error = false;

  if (!this.isValidNumber(value) || value == "")
    error = 'invalid_number';
  
  return error;
}

//Validate entry is a whole (natural / counting) number
JValidBase.prototype.validateWholeNumber = function(value) {
  var error = false;

  if (isNaN(value) || value % 1 != 0)
    error = 'invalid_whole_number';
  
  return error;
}

JValidBase.prototype.isValidPrice = function(value) {
  if(value.match(/^\d*(\.\d{1,2})?$/)) {
    return true;
  }
  return false;
}

JValidBase.prototype.validateHexadecimal = function(value) {
  var error = false;
  
  if (!this.isValidHexadecimal(value))
    error = 'invalid_hexadecimal';
    
  return error;
}

//Validate entry is a social security number
JValidBase.prototype.validateSSN = function(value) {
  var error = false;
  if (!this.isValidSSN(value))
    error = 'invalid_ssn';
    
  return error;
}

//Validate entry is a social security number
JValidBase.prototype.validateEIN = function(value) {
  var error = false;
  
  if (!this.isValidEIN(value))
    error = 'invalid_ein';
    
  return error;
}

/**  
*Validate that value of associated text field is not empty when a given checkbox is selected.
*@param checkboxNodeList hash of checkbox object references
*@param checkValue checkbox value indicating an associated textfield
*@param otherField dom node reference to the associated text field
*@returns boolean false if no error found, error string if problem exists
*/
JValidBase.prototype.validateOtherField = function(checkboxNodeList,checkValue,otherField) {
  var error = false;
  for (var i in checkboxNodeList){
   if( checkboxNodeList[i].checked &&  checkboxNodeList[i].value==checkValue){
     if(otherField.value == ''){
       error = 'blank_other';
       break;
     }
   }
  }
  return error;
}

/* ----- General Purpose Methods ------ */

//Trim whitespace
JValidBase.prototype.trim= function(s) {  
  return s ? s.replace( /^\s+/g, "" ).replace( /\s+$/g, "" ) : "";
}

//Add errors organized by nodeId
JValidBase.prototype.manageErrors = function (nodeId,err){
  if(err){
    this.errors[nodeId] = err;
  }else{
    delete this.errors[nodeId];
  } 
}

//Clear all errors. 
JValidBase.prototype.clear = function(){
  this.errors = {};
}

//Add a new regular expression to the list of validation rules
JValidBase.prototype.addValidRule = function(_label,_rule){
  var _r = false;
  if(!this.valid[_label]){
    this.valid[_label]=_rule;
    _r = true;
  }
  return _r;
}

//Add a new handler method that calls one or more validation rules
JValidBase.prototype.addValidHandler = function(_name,_func){
  this[_name] = _func;
}

//Create and association between an event types and their validation handlers
JValidBase.prototype.mapEvtsToHandlers = function(evt_handler_list){
  try{
    for(var i=0;i<evt_handler_list.length;i++){
      var _evt_label = evt_handler_list[i][0];
      var _func = evt_handler_list[i][1];
      var _args = (typeof evt_handler_list[i][2]=='undefined')?null:evt_handler_list[i][2];

      var _handler = {'func':_func,'args':_args};
      this.handlers[_evt_label]=_handler;  
    } 
  }catch(e){}
}

//Get validation handler name by the event type
JValidBase.prototype.getHandlerByEvtType = function(_type){
  return this.handlers[_type].func;
}

//Get arguments for validation handler name by the event type
JValidBase.prototype.getHandlerArgsByEvtType = function(_type){
  return this.handlers[_type].args;
}

//Set arguments for validation handler name by the event type
//handy for inserting arguments dynamically into a handler function 
//ex: after page loads in response to some user action set a handlers args
JValidBase.prototype.setHandlerArgsByEvtType = function(_type,_args){
  this.handlers[_type].args = _args;
}

//Get long description associated with an error code
JValidBase.prototype.getErrorDescription = function(_code){
  return this.error_descriptions[_code];
}

//Set long description associated with an error code
JValidBase.prototype.setErrorDescription = function(_code,_description){
  this.error_descriptions[_code] = _description;
}
