/**
 * Draftfcb JavaScript Form Validation Class
 * Author: D. Liao <daniel.liao@draftfcb.com>
 */

function DraftfcbForm(objForm) {
  //constants
  this.BAD_DATES = new Array();
  this.BAD_DATES.push(
    { M: '02', D: '30' },
    { M: '02', D: '31' },
    { M: '04', D: '31' },
    { M: '06', D: '31' },
    { M: '09', D: '31' },
    { M: '11', D: '31' }
  );

  //properties
  this.FormHandle = objForm;
  this.ErrorPrefix = null;
  this.SixteenBefore = null;
  this.Rules = new Array();
  this.EligibleStates = new Array();
  
  //methods
  this.Init = init;
  this.Validate = validate;
  this.Required = required;
  this.EmailFormat = emailFormat;
  this.Matching = matching;
  this.PhoneTenDigit = phoneTenDigit;
  this.PhoneTenDashes = phoneTenDashes;
  this.PhoneTenParen = phoneTenParen;
  this.ZipFivePlusFour = zipFivePlusFour;
  this.ValidDateYMD = validDateYMD;
  this.IsLeapYear = isLeapYear;
  this.OverSixteenYMD = overSixteenYMD;
  this.StateRestrict = stateRestrict;

  //call the initialization method
  this.Init();
  
  //initialization: set up event handlers in the form
  function init()
  {
    this.FormHandle.onsubmit = function() {
      try
      {
        handleSubmit();
      } catch(e) { }
      return formValidate();
    }
  }

  //the main validation method
  function validate(arrRules)
  {
    var formPassed = true;
    var errorMessage = "";
    for (var i = 0; i < arrRules.length; i++)
    {
      var currRule = arrRules[i];
      if (this.FormHandle.elements[currRule.Field] !== undefined)
      {
       var elementPassed = eval("this." + currRule.Method + "(this.FormHandle.elements['" + currRule.Field + "'])");
        if (!elementPassed)
        {
          errorMessage += "\n" + "- " + currRule.Message;
          formPassed = false;
        }
      } else {
        alert("debug: field '" + currRule.Field + "' not found in page.");
      }
    }
    if (!formPassed)
    {
      alert(this.ErrorPrefix + errorMessage);
    }
    
    return formPassed;
  }

  //method to test required fields of various types for non-empty values
  function required(objElement)
  {
    switch(objElement.type)
    {
      case "text":
        if (objElement.value == "")
        {
          return false;
        } else {
          return true;
        }
        break;
      
      case "checkbox":
        return objElement.checked;
        break;

      case "select-one":
        if (objElement[objElement.selectedIndex].value == "")
        {
          return false;
        } else {
          return true;
        }
        break;
      
      default:
        return false;
    }
  }

  //method to test correct formatting of e-mail addresses
  function emailFormat(objElement)
  {
    if (objElement.value !== "")
    {
      var emailPattern = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i;
      return emailPattern.test(objElement.value);
    } else {
      return true;
    }
  }
  
  //method to test two fields for equal values
  //the field name to confirm must be the first field with "_confirm" appended
  function matching(objElement)
  {
    var objConfirmElement = this.FormHandle.elements[objElement.name + "_confirm"];
    return (objElement.value == objConfirmElement.value);
  }
  
  //method to test ten-digit formatting of a phone number, e.g., 2125551212
  function phoneTenDigit(objElement)
  {
    if (objElement.value !== "")
    {
      var phonePattern = /^[2-9]\d{9}$/;
      return phonePattern.test(objElement.value);
    } else {
      return true;
    }
  }
  
  //method to test ten-digit formatting of a phone number, e.g., 212-555-1212
  function phoneTenDashes(objElement)
  {
    if (objElement.value !== "")
    {
      var phonePattern = /^[2-9]\d{2}-\d{3}-\d{4}$/;
      return phonePattern.test(objElement.value);
    } else {
      return true;
    }
  }
  
  //method to test ten-digit formatting of a phone number, e.g., (212)555-1212
  function phoneTenParen(objElement)
  {
    if (objElement.value !== "")
    {
      var phonePattern = /^\([2-9]\d{2}\)\d{3}-\d{4}$/;
      return phonePattern.test(objElement.value);
    } else {
      return true;
    }
  }
  
  //method to test for valid 5 digit ZIP code, with optional ZIP+4
  function zipFivePlusFour(objElement)
  {
    if (objElement.value !== "")
    {
      var codePattern = /^\d{5}$|^\d{5}-\d{4}$/;
      return codePattern.test(objElement.value);
    } else {
      return true;
    }
  }

  function validDateYMD(objElement)
  {
    var arrDate = objElement.value.split("/");
    var strYear = arrDate[0];
    var strMonth = arrDate[1];
    var strDay = arrDate[2];
    var datePassed = true;

    //test for empty date values
    if ((strYear == "") || (strMonth == "") || (strDay == ""))
    {
      datePassed = false;
    }

    //test for 30th and 31st of February plus 31st of other 30-day months
    for (var i=0; i<this.BAD_DATES.length; i++)
    {
      var currBadDate = this.BAD_DATES[i];
      if ((strMonth == currBadDate.M) && (strDay == currBadDate.D))
      {
        datePassed = false;
      }
    }

    //test for Feb 29th in non leap year
    if ((this.IsLeapYear(strYear) == false) && (strMonth == "02") && (strDay == "29"))
    {
      datePassed = false;
    }
    
    return datePassed;
  }

  //method to test for leap years
  function isLeapYear(intYear)
  {
    var isLeap = false;
    if ((intYear % 4) == 0)
    {
      if ((intYear % 100) == 0)
      {
        isLeap = ((intYear % 400) == 0);
      } else {
        isLeap = true;
      }
    }

    return isLeap;
  }

  //method to verify a minimum age
  function overSixteenYMD(objElement)
  {
    var arrDate = objElement.value.split("/");
    var userDate = new Date();
    userDate.setYear(arrDate[0]);
    userDate.setMonth(arrDate[1] - 1, arrDate[2]);

    return (userDate < this.SixteenBefore);
  }

  //method to filter for specific state values
  function stateRestrict(objElement)
  {
    var statePassed = false;
    if (objElement.options[objElement.selectedIndex].value == "")
    {
      statePassed = true;
    }
    for (var i=0; i<this.EligibleStates.length; i++)
    {
      if (objElement.options[objElement.selectedIndex].value == this.EligibleStates[i])
      {
        statePassed = true;
      }
    }
    return statePassed;
  }
}
