/* 
* Smart Form Validation 2.32 (John.Smart at GreyDuck.com) http://www.GreyDuck.com 
* 
* Includes publicly available code from 
*     Jake Howlett, http://codestore.net 
*     Cyanide_7, ( web site off line ) 
*     Email validation from http://www.perlscriptsjavascripts.com/js/check_email.html 
*     
* You may use and distribute provided that this line and above lines are not removed. */
 
var gbValiAllowTodayString = true; // set to false in your scripts if you don't want user to be able to enter "today", "tomorrow", or "yesterday" as date values. 
var goValiFirstInvalid = false; // cleared (set to False) by clearValidationMsgs, set by setValidationMsg, used by standardValidation 
var gsValiPrefix = "\n - "; // constant 
var gsValiEmpty; // list of field names that are required but empty, each prefixed by gsValiPrefix 
var gsValiNaN; // list of numeric field names that don't have valid numbers. 
var gsValiNotDate; 
var gsValiNotDateTime; 
var gsValiOther; // list of field names and why they are invalid 
var gaoValiElements = new Array(); // array of elements registered via registerValidationElement. 
var gsDateFormat = "mm/dd/yyyy"; // change this to dd/mm/yyyy as your location requires 

function isNumOutOfRange(num, min, max) { 
     if (min != null && num < min) return -1; 
     if (max != null && num > max) return 1; 
     return 0; 
} 

function inputToNumber(o) { // given an input field, returns the numeric value, or zero if isNaN. 
     var r = /[^\-0-9.]/g; 
     var n = o.value.replace(r, ""); 
     return (isNaN(n) ? 0 : Number(n)); 
} 

function formatPercent(n) { 
     return (n < 1 ? (n * 100) : n ) + "%"; 
} 

function formatCurrency(n, iDecimals, sSymbol) { 
/*     Author: Cyanide_7 (email and web address defunct) 
     Found on http://javascript.internet.com/forms/currency-format.html 
     altered by adding optional iDecimals argument. default of 2 rounds to nearest hundredth (i.e. penny), 3 = nearest thousandth, -3 = nearest thousand 
     also added optional third argument of denomination symbol. Default is '$'. Use '' for no symbol. 
*/ 
     var cents 
     var i 
     var sign 
     var tens 

     if (sSymbol === undefined || sSymbol === null) sSymbol = '$' 
      
     if (isNaN(n)) n = '0'; else n = n.toString().replace(/\$|\,/g,'') 
     sign = (n == (n = Math.abs(n))) 
     if (iDecimals == null) iDecimals = 2 
      

     var tens = Math.pow(10,iDecimals) 
     n = Math.floor(n*tens+0.50000000001) 
     if (iDecimals <= 0) cents = '' 
     else { 
          cents = n%tens 
          cents = tens.toString() + cents.toString() 
          cents = '.' + cents.substr(cents.length-iDecimals) 
     } 
     n = Math.floor(n/tens) 
     n = n.toString() 
     for (var i = 0; i < Math.floor((n.length-(1+i))/3); i++) 
          n = n.substring(0, n.length - (4 * i + 3)) + ',' + n.substring(n.length - (4 * i + 3)) 
     return (((sign)?'':'-') + sSymbol + n + cents) 
} 

function timeComponents(sTime) { 
     var aTime = sTime.match(/^(\d{1,2}):(\d{2})\s{1}([AP]M)$/i); 
      
     if (aTime == null) return null; 
      
     return(new Array(aTime[1], aTime[2], aTime[3])); 
} 

function valiTime(timeBits) { 
     if (timeBits == null) return false; 

     var iHour = Number(timeBits[0]); 
     var iMinute = Number(timeBits[1]); 
     var sAMPM = timeBits[2]; 
      
     if (iHour < 1 || iHour > 12) return false; 
     if (iMinute < 0 || iMinute > 59) return false; 
     return true; 
} 

function dateComponents(dateStr) { 
     var results = new Array(); 

     if (gbValiAllowTodayString && dateStr.toLowerCase) { 
          var bDateString; 
          var dt; 
          switch (dateStr.toLowerCase()) { 
          case "today" : 
               bDateString = true 
               dt = new Date() 
               break; 
          case "yesterday" : 
               bDateString = true 
               dt = new Date() 
               dt.setDate(dt.getDate() - 1) 
               break; 
          case "tomorrow" : 
               bDateString = true 
               dt = new Date() 
               dt.setDate(dt.getDate() + 1) 
               break; 
          default : 
               bDateString = false 
          } 
          if (bDateString) { // This is a date instead of a string 
               results[0] = dt.getDate(); 
               results[1] = dt.getMonth() + 1; 
               results[2] = dt.getFullYear(); 
               return results; 
          } 
     } 
      
/*     Added functionality for if dateStr is actually a date, not a string */ 
     if (dateStr.getDate) { // This is a date instead of a string 
          results[0] = dateStr.getDate(); 
          results[1] = dateStr.getMonth() + 1; 
          results[2] = dateStr.getFullYear(); 
          return results; 
     } 

/*     The following splits a date into day, month and year components 
     Used with persmission from Jake Holwett, http://www.codestore.org */ 

     var datePat = /^(\d{1,2})([\-\/\.])(\d{1,2})\2(\d{2}|\d{4})$/; 
     var matchArray = dateStr.match(datePat); 

     if (matchArray == null) { 
          return null; 
     } 
     //check for two digit years and prepend 20. 
     matchArray[4] = (matchArray[4].length == 2) ? '20' + matchArray[4] : matchArray[4]; 

     // parse date into variables 
     if (gsDateFormat.charAt(0)=="d"){ //what format does the server use for dates? 
          results[0] = Number(matchArray[1]); 
          results[1] = Number(matchArray[3]); 
     } else { 
          results[1] = Number(matchArray[1]); 
          results[0] = Number(matchArray[3]); } 
     results[2] = Number(matchArray[4]); 
     return results; 
} 

function valiDate(dateBits) { // example usage: if (!valiDate(dateComponents(obj.value))) alert("Bad Date!") 
     if (dateBits == null) return false; 

     day = dateBits[0]; 
     month = dateBits[1]; 
     year = dateBits[2]; 

     if ((month < 1 || month > 12) || (day < 1 || day > 31)) { // check month range 
          return false; 
     } 
     if ((month==4 || month==6 || month==9 || month==11) && day==31) { 
          return false; 
     } 
     if (month == 2) { 
          // check for february 29th 
          var isleap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); 
          if (day>29 || (day==29 && !isleap)) { 
          return false; 
          } 
     } 
     return true; 
} 

function valiEmail(e) { 
// Email validation from http://www.perlscriptsjavascripts.com/js/check_email.html 
     ok = "1234567890qwertyuiop[]asdfghjklzxcvbnm.@-_QWERTYUIOPASDFGHJKLZXCVBNM"; 

     for(i=0; i < e.length ;i++) if(ok.indexOf(e.charAt(i))<0)return (false) 

     re = /(@.*@)|(\.\.)|(^\.)|(^@)|(@$)|(\.$)|(@\.)/; 
     re_two = /^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/; 
     if (!e.match(re) && e.match(re_two)) return (true); 
     return (false) 
} 

function leading0s(iValue, iMinDigits) { 
     var s = iValue.toString(); 
     while (s.length < iMinDigits) s = '0' + s; 
     return s 
} 

function formatMMDDYYYY(aiDateBits) { 
          if (gsDateFormat.charAt(0)=="d") //what format does the server use for dates? 
               return leading0s(aiDateBits[0], 2) + "/" + leading0s(aiDateBits[1], 2) + "/" + aiDateBits[2] 
          else 
               return leading0s(aiDateBits[1], 2) + "/" + leading0s(aiDateBits[0], 2) + "/" + aiDateBits[2] 
} 

function registerValidationElement(o, sName, bRequired, nMax, nMin, sType) { 
/* adds properties to o for later use in validateElement(), then adds o to the gaoValiElements array */ 
	var undefined;
	
     if (o === undefined) { 
          alert("Error: object argument passed to registerValidationElement is undefined.\n(" + sName + ")"); 
          return false; 
     }

     o.valiName = (sName ? sName : o.name); 
     o.valiType = (sType ? sType : o.type); 
     o.valiMin = nMin; 
     o.valiMax = nMax; 
     if (bRequired) o.valiRequired = true; 
     gaoValiElements[gaoValiElements.length] = o; 
} 

function goToFirstInvalid() { 
/* deprecated.  function existed in lost codestream of 2.21.  Here only for backwards compatibility.  Use focusOnFirstInvalid instead */ 
} 

function focusOnFirstInvalid() { 
     if (goValiFirstInvalid) { 
          var o = goValiFirstInvalid.onInvalid; 
          if (!o) o = goValiFirstInvalid; 
          if (o.focus) { 
               o.focus() 
               if (o.select) o.select() 
          } 
          return true 
     } else return false 
} 

function standardValidation() { 
     clearValidationMsgs(); 
     if (setValidationMsgs()) return true 
     else { 
          return returnValidationResults() 
     } 
} 

function returnValidationResults() { 
     var s = validationMsg() 
     if (s.length == 0) return true 
     else { 
          alert(validationMsg()) 
          focusOnFirstInvalid() 
          return false 
     } 
} 

function clearValidationMsgs() { 
     gsValiEmpty = ""; 
     gsValiNaN = ""; 
     gsValiNotDate = ""; 
     gsValiNotDateTime = ""; 
     gsValiOther = ""; 
     goValiFirstInvalid = false 
} 

function setValidationMsgs() { 
     var undefined; 
     // don't forget to call clearValidationMsgs first! 
     if (gaoValiElements.length == 0) registerValidationElements(); //if gaoValiElements not populated, populate it. 
      
     for (var i=0; i < gaoValiElements.length; i++) if (!validateElement(gaoValiElements[i]) && !goValiFirstInvalid) 
          goValiFirstInvalid = gaoValiElements[i]; 
     if (gsValiEmpty) return false; 
     if (gsValiNaN) return false; 
     if (gsValiNotDate) return false; 
     if (gsValiNotDateTime) return false; 
     if (gsValiOther) return false; 
     return true; 
} 

function validationMsg() { 
     var sMsg = ""; 
      
     if (gsValiEmpty) sMsg = "The following fields are required but empty:" + gsValiEmpty + "\n\n"; 
     if (gsValiNaN) sMsg += "The following numeric fields have invalid values:" + gsValiNaN + "\n\n"; 
     if (gsValiNotDate) sMsg += "The following date fields have invalid values:" + gsValiNotDate + "\n\n"; 
     if (gsValiNotDateTime) sMsg += 'The following date-time fields have invalid values:' + gsValiNotDateTime + '\n(valid date-time format is "' + gsDateFormat + ' hh:mm AM")\n\n'; 
     if (gsValiOther) sMsg += "The following fields are invalid:" + gsValiOther; 

     return sMsg; 
} 

function validateElementFI(o, sName, bRequired, vMax, vMin, sType) { 
// sets goValiFirstInvalid if validateElement is false and goValiFirstInvalid isn't already set 
     if (validateElement(o, sName, bRequired, vMax, vMin, sType)) return true 
     else { 
          if (!goValiFirstInvalid) goValiFirstInvalid = o 
          return false 
     } 
} 

function validateElement(o, sName, bRequired, vMax, vMin, sType) { 
// only the first argument is required if registerValidationElement was called. 
     var r; // regular expression 
     var val; // o.value 
     var i; //counter 
     var iLen; //val.length 
     var undefined; 
     var sTime = undefined; // time portion of val, used for datetime types of elements 
     var sTimeStartMsg = ''; // part of the error message if invalid datetime value 
     var sTimeEndMsg = ''; 
     
     if (o === undefined) { 
          alert("Error: object argument passed to validateElement is undefined.\n(" + sName + ")"); 
          return false; 
     } 

// if any parameters aren't supplied, check for the properties that should have been set during registerValidationElement 
     if (sName === undefined) sName = (o.valiName) ? o.valiName : o.name; 
     if (bRequired === undefined) bRequired = o.valiRequired; 
     if (vMin === undefined) vMin = o.valiMin; 
     if (vMax === undefined) vMax = o.valiMax; 
     if (sType === undefined) sType = (o.valiType) ? o.valiType : o.type; 

// set val and bEmpty     
     switch(sType) { 
          case "select-one" : 
               val = o.selectedIndex; // use selectedIndex property, if available 
               bEmpty = (val < 1); // 0 or -1, with assumption that first choice is blank. 
               break; 
          case "select-multiple" : 
               val = o.selectedIndex; // use selectedIndex property, if available 
               bEmpty = (val == -1); 
               break; 
          case "text" : 
          case "number" : 
          case "integer" : 
          case "percent" : 
          case "date" : 
          case "datetime" : 
          case "email" : 
          case "textarea" : 
               val = o.value 
               bEmpty = (val.length == 0); 
               break; 
          case "radio" : 
          case "checkbox" : 
               bEmpty = true; 
               for (i = 0; i < o.length; i++) { 
                    if (o[i].checked) { 
                         val = i; 
                         bEmpty = false; 
                         break; 
                    } 
               } 
               break; 
          default : 
               alert("Error: validateElement doesn't know how to deal with valiType = " + sType + "\n(" + sName + ")"); 
               return false; 
     } 
// if bEmpty is true, immediately return false or true, depending on if it's required or not. 
     if (bEmpty) { 
          if (bRequired) { 
               gsValiEmpty += gsValiPrefix + sName; 
               return false; 
          } else return true 
     } 
// now for the real validation     
     switch (sType) { 
          case "text" : 
          case "textarea" : 
               iLen = val.length; 
               if (isNumOutOfRange(iLen, vMin, vMax)) { 
                
                    if (vMin == null) 
                         gsValiOther += gsValiPrefix + sName + " must be less than " + vMax + " characters long." 
                    else if (vMax == null) 
                         gsValiOther += gsValiPrefix + sName + " must be at least " + vMin + " characters long." 
                    else if (vMax == vMin) 
                         gsValiOther += gsValiPrefix + sName + " must be " + vMin + " characters long." 
                    else 
                         gsValiOther += gsValiPrefix + sName + " must be between " + vMin + " and " + vMax + " characters long."; 

                    return false; 
               } 
               return true; 

          case "email" : 
               if (valiEmail(o.value)) return true 
               else { 
                    gsValiOther += gsValiPrefix + sName + " requires a valid email address."; 
                    return false 
               } 

          case "percent" : 
          case "number" : 
          case "integer" : 
               if (sType == "integer")     r = /[, ]/g; // regexp set to look for all commas and spaces. 
               else r = /[$,% ]/g; // regexp set to look for all dollar signs, commas, percent signs, and spaces. 
               val = val.replace(r,"") // strip them out 
               if (isNaN( val )) { 
                    gsValiNaN += "\n -- " + sName; 
                    return false 
               } else if (isNumOutOfRange(val, vMin, vMax)) { 
                    if (vMin == null)               gsValiOther += gsValiPrefix + sName + " must be less than or equal to " + vMax 
                    else if (vMax == null)     gsValiOther += gsValiPrefix + sName + " must be at least " + vMin 
                    else                               gsValiOther += gsValiPrefix + sName + " must be between " + vMin + " and " + vMax; 
                    
                    return false 
               } else if (sType == "integer" && val != parseInt(val)) { 
                    gsValiOther += gsValiPrefix + sName + " must be an integer."; 
                    return false; 
               } 
               if (sType == "percent") o.value = formatPercent(val); // make sure a % is within the field. Domino will treat '5' as 500% 
               return true; 

          case "datetime" : 
               var i = val.indexOf(' '); 
               if (i != -1) { 
                    sTime = val.substr(i + 1); 
                    if (valiTime(timeComponents(sTime))) { 
                         val = val.substr(0, i) 
                    } else { 
                         gsValiNotDateTime += gsValiPrefix + sName; 
                         return false; 
                    } 
               } 
               sTimeStartMsg = (sType != 'datetime' ? '' : ' 12:00 AM'); 
               sTimeEndMsg = (sType != 'datetime' ? '' : ' 11:59 PM'); 
      
               // note: no break. code continues to validate the date as well 

          case "date" : 
               var aiDateBits = dateComponents(val); 
               if (aiDateBits == null) { 
                    if (sType=='date') gsValiNotDate += gsValiPrefix + sName 
                    else gsValiNotDateTime += gsValiPrefix + sName; 
                    return false; 
               } 
               //Check it is a valid date first 
               if (valiDate(aiDateBits) == false) { 
                    if (sType=='date') gsValiNotDate += gsValiPrefix + sName 
                    else gsValiNotDateTime += gsValiPrefix + sName; 
                    return false; 
               } 
               //Now check whether a range is specified and if in bounds 
               var theDate = new Date(aiDateBits[2], parseInt(aiDateBits[1]) - 1, aiDateBits[0]); 
               if ( vMin ) { 
                    var minBits = dateComponents (vMin); 
                    for (i = 2; i > -1; i--) { 
                         if (minBits[i] < aiDateBits[i]) break // if year is before, stop. Don't compare month numbers. 
                         else if (minBits[i] > aiDateBits[i]) { 
                              if ( vMax )     gsValiOther += gsValiPrefix + sName + " must be between " + formatMMDDYYYY(minBits) + sTimeStartMsg + " and " + formatMMDDYYYY(dateComponents(vMax)) + sTimeEndMsg 
                              else gsValiOther += gsValiPrefix + sName + " must be on or after " + formatMMDDYYYY(minBits) + sTimeStartMsg 
                              return false; 
                         } 
                    } 
               } 
               if ( vMax) { 
                    var maxBits = dateComponents (vMax); 
                    for (i = 2; i > -1; i--) { 
                         if (maxBits[i] > aiDateBits[i]) break 
                         else if (maxBits[i] < aiDateBits[i]) { 
                              if ( vMin )     gsValiOther += gsValiPrefix + sName + " must be between " + formatMMDDYYYY(minBits) + sTimeStartMsg + " and " + formatMMDDYYYY(maxBits) + sTimeEndMsg 
                              else          gsValiOther += gsValiPrefix + sName + " must be on or before " + formatMMDDYYYY(maxBits) + sTimeEndMsg 
                              return false; 
                         } 
                    } 
               } 
               o.value = formatMMDDYYYY(aiDateBits) 
               if (sTime !== undefined) o.value += ' ' + sTime 
               return true; 
          
          case "checkbox" : 
               if (vMin || vMax) { // don't bother checking if no need. 
                    var iChecked = 1; 
                    for (i = val + 1; i < o.length; i++) if (o[i].checked) iChecked++ 
                    if (isNumOutOfRange(iChecked, vMin, vMax)) { 
                         if (vMin == null) 
                              gsValiOther += gsValiPrefix + sName + " must have less than " + (vMax + 1) + " choices selected." 
                         else if (vMax == null) 
                              gsValiOther += gsValiPrefix + sName + " must have at least " + vMin + " choices selected." 
                         else if (vMax == vMin) 
                              gsValiOther += gsValiPrefix + sName + " must have " + vMin + " choices selected." 
                         else 
                              gsValiOther += gsValiPrefix + sName + " must have between " + vMin + " and " + vMax + " choices selected."; 

                         return false; 
                    } 
               } 
               return true; 

          default : // select, radio, etc 
               return true 
     } 
} 
