  // Rules
  //  -----
  //  not-null
  //  email
  //  allow-bad
  //  numeric
  //  match
  //  date

  // Languages
  var strInvalidCharacters  = "The following character(s) are not allowed: '%1'";
  var strFieldRequired      = "This field must be completed.";
  var strDoesNotMatch       = "This field does not match.";
  var strThereWereProblems  = "There was a problem with some of the data you entered. Please see the red boxes.";
  var strVdInvalidEmail	    = "The email address is not valid.";
  var strVdInvalidDate	    = "The date is not in a valid format: ";
  var strVdInvalidNumber    = "This is not a valid number.";
  var strVdDateTooLarge     = "There are less than %1 days in %2 this year."
  var strSelectValue        = "You must select a value"
  var strSelectMod				  = "You must select at least one module";
  var strOverMaxLength      = "The max length for this field is %1 characters, you used %2."
  // Typo fixed in following on 12/1/05
  var arrMonthsNames = new Array(0, "January", "February", "March",
      "April", "May", "June", "July", "August", "September", "October", "November", "December");

  function did(str)
  {
    return document.getElementById(str);
  }

  // This class holds an error.
  function CError(element, strErrorSpanId, strError)
  {
    this.element = element;
    this.strErrorSpanId = strErrorSpanId;
    this.strError = strError;
  }

  // A little function to ensure when an id for an error span is created, a
  // given input will always get the same id.
  function makeErrorId(elForm, elInput)
  {
    if(elForm && elInput)
      return "formValidate_"+elForm.name+"_"+elInput.name;
    else
      return "ggg";
  }

  /**
   * Cross browser function to get the position of an element reletive to the
   * top left of the page.
   *
   * param: el - The element we are looking to locate. Must be element object.
   * param: strAxis - The axis we want to know. Valid value are left or top
   */
  function getRealPos(el, strAxis)
  {
    iPos = 0;
    while(el!=null)
    {
      iPos += el["offset" + strAxis];
      el = el.offsetParent;
    }
    return iPos
  }

  /**
   * Cross browser function to get far edge of an element
   *
   * param: el - The element we are looking to locate. Must be element object.
   * param: strAxis - The axis we want to know. Valid value are left or top
   */
  function getFarPos(el, strAxis)
  {
    elOld = el;
    iPos = 0;
    while(el!=null)
    {
      iPos += el["offset" + strAxis];
      el = el.offsetParent;
    }
    el = elOld;

    if(strAxis == "Left" || strAxis == "left" || strAxis == "LEFT")
      iPos += el.offsetWidth;

    if(strAxis == "Top" || strAxis == "top" || strAxis == "TOP")
      iPos += el.offsetHeight;
    //alert(iPos);
    return iPos
  }



  /**
   *  Trim function taken from:
   *  http://www.vermontsoftware.com/Javascript/trim.html
   */
  function Trim(s)
  {
    // Remove leading spaces and carriage returns
    while ((s.substring(0,1) == ' ') || (s.substring(0,1) == '\n') || (s.substring(0,1) == '\r'))
      s = s.substring(1,s.length);

    // Remove trailing spaces and carriage returns
    while ((s.substring(s.length-1,s.length) == ' ') || (s.substring(s.length-1,s.length) == '\n') || (s.substring(s.length-1,s.length) == '\r'))
      s = s.substring(0,s.length-1);

    return s;
  }

  /**
   * Cross browser method of changing to class of an element.
   */
  function isElementEmpty(el)
  {
    strValue = Trim(el.value);
    return strValue=='';
  }

  function IsNumeric(strText)
  {
    var strValidChars = "0123456789";
    var blIsNumber = true;
    var blDecimalPointFound = false;
    var cChar;

    for(i=0; i<strText.length; i++)
    {
      cChar = strText.charAt(i);
      if(strValidChars.indexOf(cChar) == -1)
      {
        if(!blDecimalPointFound && cChar == '.')
        {
          blDecimalPointFound = true;
        }
        else
          return false;
      }
    }
    return blIsNumber;
  }

  /**
   *  GetRule()
   */
  function getRule(el, strRule)
  {
    if(el)
    {
      if(el.getAttribute('rules'))
        strRules = (el.getAttribute('rules'));
      else
        return false;

      iIndex = strRules.indexOf(strRule);

      if(iIndex == -1)
        return false;

      if(strRules.charAt(iIndex+strRule.length) == '[' && strRules.indexOf(']', iIndex+strRule.length+1) != -1)
      {
        return strRules.substring(iIndex+strRule.length+1, strRules.indexOf(']', iIndex+strRule.length+1));
      }
      else
      {
        return true;
      }
    }
    else
    {
      return false;
    }

  }

  /**
   * Cross browser method of changing to class of an element.
   */
  function setElementClass(el, strClass)
  {
    if(el && el.setAttribute)
    {
      el.setAttribute('classname', strClass);
      el.setAttribute('class', strClass);
      el.className = strClass;
    }

  }

  /**
   * Cross browser method of changing to class of an element.
   */
  function getElementClass(el)
  {
    str1 = el.getAttribute('class');
    str2 = el.getAttribute('classname')
    str3 = el.className;

    if(str1) return str1;
    if(str2) return str2;
    if(str3) return str3;
    return '';

  }

  // Sets the class of the input field depending on success of validation and
  // the base class of the input.
  function setElementErrorStatus(el, blOk)
  {
    strClass = getElementClass(el);

    if(strClass.substring(0, 25) == 'mainFormInputValidationOk')
    {
      if(blOk)
        return;
      else
      {
        setElementClass(el, 'formValidationInputError '+strClass.substring(25));
      }
    }

    if(strClass.substring(0, 28) == 'formValidationInputError')
    {
      if(!blOk)
        return;
      else
      {
        setElementClass(el, 'formValidationInputOk '+strClass.substring(28));
      }
    }

    if(blOk)
      setElementClass(el, 'formValidationInputOk '+strClass);
    else
      setElementClass(el, 'formValidationInputError '+strClass);

  }

  // Function to validate an email address
  function emailInputValidation(elInput, elForm, arrErrorList)
  {
    strValue = elInput.value;

    if(!getRule(elInput, 'not-null') && isElementEmpty(elInput))
      return true;

    strErrorSpanId = makeErrorId(elForm, elInput);

    var emailReg = "^[\\w'-_\.]*[\\w-_\.]\@[\\w]\.+[\\w]+[\\w]$";
    //var emailReg = "^((?>[a-zA-Z\d!#$%&'*+\-/=?^_`{|}~]+\x20*|"((?=[\x01-\x7f])[^"\\]|\\[\x01-\x7f])*"\x20*)*(?<angle><))?((?!\.)(?>\.?[a-zA-Z\d!#$%&'*+\-/=?^_`{|}~]+)+|"((?=[\x01-\x7f])[^"\\]|\\[\x01-\x7f])*")@(((?!-)[a-zA-Z\d\-]+(?<!-)\.)+[a-zA-Z]{2,}|\[(((?(?<!\[)\.)(25[0-5]|2[0-4]\d|[01]?\d?\d)){4}|[a-zA-Z\d\-]*[a-zA-Z\d]:((?=[\x01-\x7f])[^\\\[\]]|\\[\x01-\x7f])+)\])(?(angle)>)$";
    //var emailReg = "^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$"
    var regex = new RegExp(emailReg);
//alert(regex.test(strValue));
    if(!regex.test(strValue))
    {
      arrErrorList[arrErrorList.length] = (new CError(elInput, strErrorSpanId, strVdInvalidEmail));
      return false;
    }

    //if(strValue.indexOf("'") != -1)
    //{
    //  arrErrorList[arrErrorList.length] = (new CError(elInput, strErrorSpanId, strVdInvalidEmail));
    //  return false;
    //}

    if(document.getElementById(strErrorSpanId))
    {
      document.getElementById(strErrorSpanId).innerHTML = '';
      document.getElementById(strErrorSpanId).style['display'] = 'none';
    }

    return true;
  }

  /**
   * Function to validate a date address
   *
   * Acceptable formats are:
   *   dd/mm/yy
   *   dd/mm/yyyy
   *   mm/dd/yy
   *   mm/dd/yyyy
   */
  function dateInputValidation(elInput, elForm, strFormat, arrErrorList)
  {
    strValue = elInput.value;

    if(!getRule(elInput, 'not-null') && isElementEmpty(elInput))
    {
      return true;
    }

    switch(strFormat)
    {
      case "dd/mm/yyyy":
        regex = /^(3[01]|0[1-9]|[12]\d)\/(0[1-9]|1[012])\/(\d{4})$/;
        break;
      case "mm/dd/yyyy":
        regex = /^(0[1-9]|1[012])\/(3[01]|0[1-9]|[12]\d)\/(\d{4})$/;
        break;
      case "dd/mm/yy":
        regex = /^(3[01]|0[1-9]|[12]\d)\/(0[1-9]|1[012])\/(\d{2})$/;
        break;
      case "mm/dd/yy":
        regex = /^(0[1-9]|1[012])\/(3[01]|0[1-9]|[12]\d)\/(\d{2})$/;
        break;
      default:
        regex = /^(3[01]|0[1-9]|[12]\d)\/(0[1-9]|1[012])\/(\d{4})$/;
        strFormat = "dd/mm/yyyy";
        break;

    }
    strErrorSpanId = makeErrorId(elForm, elInput);

    blFail = false;

    blFail = (!regex.test(strValue))

    if(blFail)
    {
      arrErrorList[arrErrorList.length] = (new CError(elInput, strErrorSpanId, strVdInvalidDate+strFormat));
      return false;
    }

    if(!blFail)
    {

      switch(strFormat)
      {
        case "dd/mm/yy":
        case "dd/mm/yyyy":
          iDay    = new Number(RegExp.$1);
          iMonth  = new Number(RegExp.$2);
          iYear   = new Number(RegExp.$3);
          break;
        case "mm/dd/yy":
        case "mm/dd/yyyy":
          iMonth  = new Number(RegExp.$1);
          iDay    = new Number(RegExp.$2);
          iYear   = new Number(RegExp.$3);
          break;
      }

      switch(strFormat)
      {
        case "dd/mm/yy":
        case "mm/dd/yy":
          if(iYear < 30)
            iYear += 2000;
          else
            iYear += 1900;
      }

      arrMonths = new Array(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

      // Bloody Feburary
      //if(iMonth == 2 && iYear%4 == 0 && iDay != 1) // old code
      if(iMonth == 2 && iYear >= 1582) // unnecessarily accurate, but accurate all the same
      	if( (((iYear % 4 == 0) && (iYear % 100 != 0)) || (iYear % 400 == 0)) && iDay != 1)
              iDay--;


      if(iDay > arrMonths[iMonth])
        blFail = true;
    }

    if(blFail)
    {
      strError = strVdDateTooLarge.replace("%1", iDay);
      strError = strError.replace("%2", arrMonthsNames[iMonth]);
      arrErrorList[arrErrorList.length] = new CError(elInput, strErrorSpanId, strError);
      return false;
    }

    if(document.getElementById(strErrorSpanId))
    {
      document.getElementById(strErrorSpanId).innerHTML = '';
      document.getElementById(strErrorSpanId).style['display'] = 'none';
    }

    return true;
  }

  // Perform character validation on a form element
  function basicCharacterValidation(elInput, elForm, arrIlligalList, arrErrorList)
  {
    //if (elInput.name == "yourquestion")
    	//alert (arrIlligalList);

    strValue = elInput.value;
    strErrorSpanId = makeErrorId(elForm, elInput);
    arrBadCharList = new Array();

    for(var j=0; j < arrIlligalList.length; j++)
    {
      if(strValue.indexOf(arrIlligalList[j]) != -1)
      {
        arrBadCharList[arrBadCharList.length] = (arrIlligalList[j]);
      }
    }

    if(arrBadCharList.length > 0)
    {
      strError = strInvalidCharacters.replace("%1", arrBadCharList.join(','))
      arrErrorList[arrErrorList.length] = (new CError(elInput, strErrorSpanId, strError));
      return false;
    }

    if(document.getElementById(strErrorSpanId))
    {
      document.getElementById(strErrorSpanId).innerHTML = '';
      document.getElementById(strErrorSpanId).style['display'] = 'none';
    }

    return true;
  }

  function basicFormValidation(elForm)
  {
    return internalFormValidationTryWraper(elForm, Array('"', ',', '\\', '/', '#', '<', '*', '>', "''", '`', '~', '$'));
  }

  function nonstrictFormValidation(elForm)
  {
     return internalFormValidationTryWraper(elForm, Array('"', ',' , '<', '>', "''", '`', '~', '$'));
  }

  function internalFormValidationTryWraper(elForm, arrIlligalList)
  {
    try
    {
      return internalFormValidation(elForm, arrIlligalList);
    }
    catch(error)
    {
			//alert("hello error!");
      blResponse = confirm("Could not validate page. If you are sure your data is correct, press 'OK' otherwise press 'Cancel' to change your data.");

      strAddress = new String(window.location);
      if(strAddress.substr(7, 6) == 'srvdev')
      {
        strErr = "";
        for(i in error)
        {
          if(i == 'number')
          {
            strErr += i+"="+(error[i] & 0xffff)+"\n";
          }
          else
          {
            strErr += i+"="+error[i]+"\n";
          }
        }
        alert(strErr);
        setTimeout(function(){throw(error);}, 200);
      }

      return blResponse;
    }
  }

  function internalFormValidation(elForm, arrIlligalList)
  {
    if(!elForm)
      return true;

    var iNumElements = elForm.elements.length;
    var arrErrorList = new Array();

    for(var iElementIndex = 0; iElementIndex < iNumElements; iElementIndex++)
    //for(iElementIndex in elForm.elements)
    {
      element = elForm.elements[iElementIndex];

      if(element && element.getAttribute)
      {
        blNotNull   = getRule(element, 'not-null');
        blEmail     = getRule(element, 'email');
        blAllowBad  = getRule(element, 'allow-bad');
        strExtraBad = getRule(element, 'extra-bad');
        blNumeric   = getRule(element, 'numeric');
        strMatch    = getRule(element, 'match');
        strDate     = getRule(element, 'date');
        strNotSel   = getRule(element, 'notsel');
        multiNotSel = getRule(element, 'notmultisel');
        strMaxLen   = getRule(element, 'maxlen');

        switch(element.type)
        {
          case "text":
          case "password":
          case "textarea":
          {

            // Our first test is that the field is not empty.
            // This is applied by the rule 'not-null'
            if(blNotNull && element.value == '')
            {
              strErrorSpanId = makeErrorId(elForm, element);
              arrErrorList[arrErrorList.length] = (new CError(element, strErrorSpanId, strFieldRequired));
              blReturn = false;
              break;
            }

            // Our first test is that the field is not empty.
            // This is applied by the rule 'not-null'
            if(strMaxLen !== false)
            {
              iStrLen = element.value.length;
              if(iStrLen > strMaxLen)
              {
                strErrorMessage = strOverMaxLength;
                strErrorMessage = strErrorMessage.replace('%1', strMaxLen);
                strErrorMessage = strErrorMessage.replace('%2', iStrLen);

                strErrorSpanId = makeErrorId(elForm, element);
                arrErrorList[arrErrorList.length] = (new CError(element, strErrorSpanId, strErrorMessage));
                blReturn = false;
                break;
              }
            }

            // Next we run a function to test that the field contains no illigal
            // characters as defined by the arrIlligalList array. If the rule
            // 'allow-bad' is set then this test is *not* run.
            if(!blAllowBad)
            {
              arrMyIlligalList = arrCopy(arrIlligalList); //alert(arrIlligalList);
            }
            else
              arrMyIlligalList = [];

            if(strExtraBad !== false)
            {
              for(i= 0; i < strExtraBad.length; i++)
                arrMyIlligalList[arrMyIlligalList.length] = strExtraBad.charAt(i);
            }

            if(!basicCharacterValidation(element, elForm, arrMyIlligalList, arrErrorList))
              break;

            // This test is run on email fields to validate them.
            if(blEmail)
            {
              if(!emailInputValidation(element, elForm, arrErrorList))
                break;
            }

            // This test is run on date fields to validate them.
            if(strDate != false)
            {
              if(!dateInputValidation(element, elForm, strDate, arrErrorList))
                break;
            }

            // This test is run on numeric fields to validate them.
            if(blNumeric && !IsNumeric(element.value))
            {
              strErrorSpanId = makeErrorId(elForm, element);
              arrErrorList[arrErrorList.length] = (new CError(element, strErrorSpanId, strVdInvalidNumber));
              blReturn = false;
              break;
            }

            if(strMatch != false)
            {
              elMatch = elForm.elements[strMatch];
              if(!elMatch)
                elMatch = document.getElementById(strMatch);

              if(elMatch)
              {
                if(elMatch.value != element.value)
                {
                  strErrorSpanId = makeErrorId(elForm, element);
                  arrErrorList[arrErrorList.length] = (new CError(element, strErrorSpanId, strDoesNotMatch));
                  blReturn = false;
                  break;
                }
              }
            }

            // If we got here then all the tests passed!
            setElementErrorStatus(element, true);
            strErrorSpanId = makeErrorId(elForm, element);
            if(did(strErrorSpanId)) did(strErrorSpanId).style.display='none';
            break;
          }
          break;
          case "select-one":
          {
            if(strNotSel != false || typeof(strNotSel) == 'string')
            {

              if(element.value == strNotSel)
              {

                strErrorSpanId = makeErrorId(elForm, element);
                arrErrorList[arrErrorList.length] = (new CError(element, strErrorSpanId, strSelectValue));
                element.style.visibility = 'hidden';
                blReturn = false;
                break;
              }
              else
              {
                strErrorSpanId = makeErrorId(elForm, element);
                if(did(strErrorSpanId)) did(strErrorSpanId).style.display='none';
                setElementErrorStatus(element, true);
                break;
              }
            }
          }
          case "select-multiple": {
          	//alert("hello from select multiple!");
          	if (element.selectedIndex == -1 && multiNotSel) {
          		//alert ("no multi select");
          		strErrorSpanId = makeErrorId(elForm, element);
                arrErrorList[arrErrorList.length] = (new CError(element, strErrorSpanId, strSelectMod));
                element.style.visibility = 'hidden';
                blReturn = false;
                break;
          		//return false;
          	}
          	else {
          		strErrorSpanId = makeErrorId(elForm, element);
          		setElementErrorStatus(element, true);
          		//alert ("multi-select stuff selected");
          	}
          }
          case "button":
          case "submit":
          case "reset":
          case "checkbox":
          case "radio":
             break; // do nothing
          default:
          {
            // If it isn't something we can validate, just make it green :)
            //setElementErrorStatus(element, true);
            break;
          }
        }
      }
    }

    if(arrErrorList.length > 0)
    {
      for(var i=0; i < arrErrorList.length; i++)
      {
        if(arrErrorList[i] && arrErrorList[i].element)
        {
          el = arrErrorList[i].element;


          strId = makeErrorId(elForm, el);
          el.onfocus = closeErrorElementsMessage;

          setElementErrorStatus(el, false);

          strMessage = arrErrorList[i].strError +
            '<a href="javascript: closeErrorMessage(\''+strId+'\');">'+
              '[close]'+
            '</a>';



          if(document.getElementById(strId))
          {
            elSpan = document.getElementById(strId);
            elSpan.style['display'] = '';
            elSpan.innerHTML = strMessage;
          }
          else
          {
            elSpan = document.createElement('span');
            setElementClass(elSpan, 'formValidationErrorArea');
            elSpan.id = strId;

            elSpan.setAttribute('elTarget', el.name);
            if(elSpan.getAttribute('elTarget') == el.name && elSpan.tagName == "select-one")
            {
              el.style.visibility = 'hidden';
            }

            iInputHeight = el.clientHeight + 3;

            iXPos = getRealPos(el,'Left');
            iYPos = getRealPos(el,'Top');
            elSpan.style['left'] = iXPos;
            elSpan.style['top'] = iYPos;
            elSpan.style['height'] = iInputHeight+'px';

            elSpan.innerHTML = strMessage;

            elBody = document.getElementsByTagName('body')[0];
            elBody.appendChild(elSpan);

            iFarEdgeOfInput = getFarPos(el, "Left");
            iFarEdgeOfSpan = getFarPos(elSpan, "Left");

            if(iFarEdgeOfInput < iFarEdgeOfSpan)
            {
              iNew = iXPos - (iFarEdgeOfSpan-iFarEdgeOfInput);
              if(iNew < 0)
                iNew = 0;
              elSpan.style['left'] = iNew;
            }
          }
        }
      }


      iHeight = 0;
      if(window.innerHeight) iHeight = window.innerHeight;
      if(document.body && document.body.clientHeight) iHeight = document.body.clientHeight;
      if(document.documentElement && document.documentElement.clientHeight) iHeight = document.documentElement.clientHeight;
      iYPos = getRealPos(arrErrorList[0].element, 'Top') - (iHeight/2);

      scroll(0, iYPos);
      alert(strThereWereProblems);
      return false;
    }

    return true;

  }



  function arrCopy(arr)
  {
    var arrNew = [];
    for(var i in arr)
    {
      //alert(i);
      if(typeof(arr[i]) == 'object' || typeof(arr[i]) == 'array')
        arrNew[i] = arrCopy(arr[i])
      else
        arrNew[i] = arr[i];
    }

    return arrNew;
  }

  function closeErrorElementsMessage(el)
  {
    if(!el)
      el = this;
    if(el)
    {
      elForm  = el;
      while(elForm != null && elForm.tagName != 'FORM')
        elForm = elForm.parentNode;

      strId = makeErrorId(elForm, el);

      closeErrorMessage(strId);
    }
  }

  function closeErrorMessage(strId)
  {
    elSpan = did(strId)
    if(elSpan)
    {
      elSpan.style.display = 'none';


      strFormElemene = elSpan.getAttribute('elTarget');


      arrElements = document.getElementsByName(strFormElemene);


      for(i in arrElements)
      {
        if(arrElements[i] && arrElements[i].style)
          arrElements[i].style.visibility = '';
      }
    }
  }
