//
//  ValidateField
//
//  Helper function that you can customize as you wish.  Basically shows calling
//  the validation routines and alerting the user if something is wrong.
//
function ValidateField(fldtype, field)
{
  if( fldtype == "Date" )
  {
    if( field.value == "" )
    {
      return;
    }

    var dtstr = ParseDate(field.value);
    if( dtstr != "" )
    {
      field.value = dtstr;
    } else {
      field.value = "";
      alert("The date you entered is not valid.");
      field.focus();
    }
  }
  if( fldtype == "Time" )
  {
    if( field.value == "" )
    {
      return;
    }

    var tmstr = ParseTime(field.value, false, false);
    if( tmstr != "" )
    {
      field.value = tmstr;
    } else {
      field.value = "";
      alert("The time you entered is not valid.");
      field.focus();
    }
  }
}

//
//
// ParseDate
//
// Parses a date that has been entered in MMDD, MMDDYY, MMDDCCYY, or
// CCYYMMDD format.  Space, dash, slash, or period punctation is optional
// but must be consistent between each element if used, and is required
// if single-digit months or days are specified.  Several helper functions
// are called to perform the parsing, so check the comment headers on those
// function for more details.
//
// Returns the date formatted as MM/DD/CCYY no matter what the input
// was, or returns an empty string ("") if the date is not valid.
//
function ParseDate(datestr)
{
  // splits the input string up based on punctation and returns a consistently
  // unpunctuated format with only the numbers from the original date string
  var s = StringizeDate(datestr);

  // if nothing is returned, the date string has no numbers in it
  if( s.length == 0 )
  {
    return "";
  }

  // get a 3-element array with Y, M, D in it
  var elems = ValidateDate(s);
  if( elems.length == 0 )
  {
    // if nothing was returned, the date wasn't valid
    return "";
  }

  //if( elems[0] == '999' ) elems[0] = '1999';	// JLB 10/09/1999: assume year of 999 means 1999
  // JLB 10/25/1999: assume year less than 4 digits is invalid
  if( elems[0].length < 4 )
    return "";

  // format the date in MM/DD/YYYY format, each element returned from
  // ValidateDate() is already formatted correctly
  return elems[1] + "/" + elems[2] + "/" + elems[0];
}

//
// ParseTime
//
// Parses a time that has been entered in HHMM, HHMM, or HHMM AM/PM
// format and returns a 12-hour formatted time string.  Also accepts
// optional seconds for all three formats.  The first two formats are
// 24-hour time, and for the third 12-hour format an A or P must be
// found anywhere in the time string in order for the hours to be
// considered on a 12-hour clock.
//
// Note on the bIncludeSeconds parameter.  This routine accepts seconds
// optionally in the time string, but only returns them in the formatted
// time string if you specify a non-zero (i.e. true) value for the
// bIncludeSeconds parameter.
//
// The b24Hour parameter should be set to a non-zero value to get a
// 24-hour time returned, or to zero to get a 12-hour time returned.
//
function ParseTime(timestr, bIncludeSeconds, b24Hour)
{
  // splits the input string up based on punctation and returns a consistently
  // unpunctuated format with only the numbers from the original time string
  var s = StringizeDate(timestr);

  // if nothing is returned, the time string has no numbers in it
  if( s.length == 0 )
  {
    return "";
  }

  // get a 3-element array with H, M, and S in it
  var elems = ValidateTime(s);
  if( elems.length == 0 )
  {
    // if nothing was returned, the time wasn't valid
    return "";
  }

  // if there is an A or P in the original string, convert the time
  // to 24-hour
  if( timestr.toUpperCase().indexOf("A") != -1 || timestr.toUpperCase().indexOf("P") != -1 )
  {
    // 12-hour time, convert hour of 12 to 0, then add 12 to whatever
    // the hour is if there is a P in the original string
    if( elems[0] == 12 )
    {
      elems[0] = "00";
    }
    if( timestr.toUpperCase().indexOf("P") != -1 )
    {
      elems[0] = parseInt(elems[0]) + 12;
      elems[0] = FormatNumericString(elems[0], 2);   /* coerce to numeric and add 12 */
    }
  }

  // format the time in 12-hour format
  var retstr = "";
  if( b24Hour )
  {
    retstr = elems[0] + ":" + elems[1]
    if( bIncludeSeconds )
    {
      retstr += ":" + elems[3];
    }
  } else {
	var midnight = false;
    // convert hours to 12-hour, and save indicator
    if( elems[0] == 0 )
    {
      elems[0] = "12";
	  // Distinguish between 12 noon and 12 midnight.
	  midnight = true;
    }
    var indicator = "AM";
    if( parseInt(elems[0]) >= 12 && midnight == false)
    {
	  // If the time is not twelve noon then change hours to zero
	  if(parseInt(elems[0]) != 12)
	  {
		elems[0] = parseInt(elems[0]) - 12;
		elems[0] = FormatNumericString(elems[0], 2);
	  }
      indicator = "PM";
    }
    retstr = elems[0] + ":" + elems[1];
    if( bIncludeSeconds )
    {
      retstr += ":" + elems[2];
    }
    retstr += " " + indicator;
  }

  return retstr;
}

//
// ValidateDate
//
// Validates a MMDD, MMDDYY, MMDDCCYY, or CCYYMMDD date string and
// returns a 3-element array with CCYY in position 0, MM in position 1,
// and DD in position 2.  Returns an empty string if the date string
// is not a valid date.
//
// For MMDDYY the century is defaulted to 1900 if the year is >= 40
// or 2000 if the year is < 40.  For MMDD the century and year are
// defaulted to the current date's century and year.
//
// CCYYMMDD format is identified by testing the first two digits for
// being > 12, which would be an invalid month.
//
function ValidateDate(datestr)
{
  var elems = new Array();    // elements to return

  // if the date string is less than four digits, it is not valid
  if( datestr.length < 4 )
  {
    return "";
  }

  // special case: if string is 8 digits and the first two are > 12,
  // assume CCYYMMDD format
  if( datestr.length == 8 && datestr.substr(0,2) > 12 )
  {
    elems[0] = datestr.substr(0,4);
    elems[1] = datestr.substr(4,2);
    elems[2] = datestr.substr(6,2);
  } else
  {
    // otherwise, pull in MMDD[[CC]YY] order
    elems[1] = datestr.substr(0,2);
    elems[2] = datestr.substr(2,2);

    if( datestr.length > 4 )    // YY (possibly CC) is present
    {
      elems[0] = datestr.substr(4,2);
      if( datestr.length > 6 )  // YY is at end, previous (4,2) was CC
      {
        elems[0] = elems[0] + datestr.substr(6,2);
      }
    } else
    {
      elems[0] = "";
    }

    // default the century (REVIEW: in 100 years, hard-coded to 1940/2039 range
    if( elems[0].length == 0 )
    {
      elems[0] = "" + (new Date()).getFullYear();   // coerce to string data type
    } else if( elems[0].length == 2 )
    {
      // default the century
      if( elems[0] >= 40 )
      {
        elems[0] = "19" + elems[0];
      } else
      {
        elems[0] = "20" + elems[0];
      }
    }
  }

  // verify the month and day are valid, otherwise return an empty string
  var cy = elems[0], m = elems[1], d = elems[2];
  if( m < 1 || m > 12 )
  {
    return "";
  }
  if( (d > 31) ||
      // 30 days has April, June, September, and November
      ((m==4 || m==6 || m==9 || m==11) && d > 30) )
  {
    return "";
  }
  // Feburary is special
  if( m==2 )
  {
    // do leap year calculation - every fourth year is a leap year except
    // for century years that are not divisible by 400, according to Giglio,
    // who didn't live 1000 years to decide about any 1000/4000 problem.
    var isLeap = cy%4 == 0 && !( cy%100 == 0 && cy%400 != 0 );
    // 28 days has February, but 29 in a leap year
    if( (d > 28 && !isLeap) || (d > 29 && isLeap) )
    {
      return "";
    }
  }

  // elems array has been setup, so return it
  return elems;
}

//
// ValidateTime
//
// Validates a HHM or HHMMSS time string and returns a 3-element array
// with HH in position 0, MM in position 1, and SS in position 2.
// Returns an empty string if the time string is not a valid time.
//
// Seconds are defaulted to 0 if not specified.
//
function ValidateTime(timestr)
{
  var elems = new Array();    // elements to return

  // if the time string is less than four digits, it is not valid
  if( timestr.length < 4 )
  {
    return "";
  }

  // pull elements in HHMM[SS] order
  elems[0] = timestr.substr(0,2);
  elems[1] = timestr.substr(2,2);
  if( timestr.length > 4 )
  {
    elems[2] = timestr.substr(4,2);
  } else {
    elems[2] = "00";
  }

  // verify the time is valid, otherwise return an empty string
  var h = elems[0], m = elems[1], s = elems[2];
  if( (h < 0 || h > 24) || (m < 0 || m > 60) || (s < 0 || s > 60) )
  {
    return "";
  }

  // elems array has been setup, so return it
  return elems;
}

//
// FormatNumericString
//
// Formats the number specified by the n parameter to be a string of
// length specified by the len parameter.
//
function FormatNumericString(n, len)
{
  var pad = "00000000000000000000000000000000000000000000000000000000000";
  var full = pad + n;   // coerce data type to left-most parameter in r-value
  return full.substr(full.length - len, len);
}

//
// StringizeDate
//
// Processes the date string that is passed in and returns a new string
// that contains only numbers.  If valid punctation is found the characters
// between the punctuation will be treated as a single element and will be
// padded with a leading zero if necessary to ensure the element is at least
// two digits in length.
//
// This behavior is illustrated in the examples below and basically allows
// you to instantly get a date into MMDD, MMDDYY, MMDDYYYY, or YYYYMMDD format
// whether it was punctuated or not.  Additional processing (provided by other
// functions in this library) is required to verify the date is valid and
// format it into a single, standard date format.
//
// Pass in this, to get this:
//    0508    = { 0508 }
//    5 8     = { 05, 08 }
//    5/7/97  = { 05, 07, 97 }
//
// NOTE: This function works for times as well (since a colon is included
// as a valid punctuation character, but you have to determine AM/PM for
// 12-hour times from the original string as they will be stripped out
// of the return value from this function.
//
function StringizeDate(datestr)
{
  var i, c;                 // loop variables
  var queue = "";           // character queue for element being extracted
  var elems = new Array();  // array of elements that were extracted
  var elemIndex = 0;        // current array index
  var numbers = "0123456789";   // valid characters for extraction (numbers
                                // only, the rest of the functions assume
                                // coercion to numeric data is going to happen)
  var punctuation = " -/.";     // valid punctuation characters

  // go through the string one character at a time
  for( i=0; i<datestr.length; i++ )
  {
    c = datestr.substr(i,1);
    if( numbers.indexOf(c) != -1 )
    {
      // if the current character is a number, extract it to the queue
      queue = queue + c;
    } else if( punctuation.indexOf(c) != -1 )
    {
      // if we hit a punctuation character, store the queue to the elems
      // array and clear the queue
      elems[elemIndex++] = queue;
      queue = "";
    }
    // any other character, discard it
  }
  if( queue != "" )
  {
    elems[elemIndex++] = queue;
  }

  // if we extracted multiple elements, make sure they are all at least
  // two digits.  they are already at least a single digit if they are
  // in the array at this point.
  for( i=0; i<elems.length; i++ )
  {
    if( elems[i].length < 2 )
    {
      elems[i] = "0" + elems[i];
    }
  }

  // return the elements we extracted as a single string with no separator
  return elems.join("");
}
