text: // Copyright (C) 2005 Rod Roark <rod@sunsetsystems.com>
  //
  // This program is free software; you can redistribute it and/or
  // modify it under the terms of the GNU General Public License
  // as published by the Free Software Foundation; either version 2
  // of the License, or (at your option) any later version.
  
  // Onkeyup handler for dates.  Converts dates that are keyed in to a
  // consistent format, and helps to reduce typing errors.
  //
  function datekeyup(e, defcc, withtime) {
   if (typeof(withtime) == 'undefined') withtime = false;
  
   while(true) {
    var delim = '';
    var arr = new Array(0, 0, 0, 0, 0, 0);
    var ix = 0;
    var v = e.value;
  
    // Build an array to facilitate error checking.
    for (var i = 0; i < v.length; ++i) {
     var c = v.charAt(i);
     if (c >= '0' && c <= '9') {
      ++arr[ix];
     } else if (ix < 2 && (c == '-' || c == '/')) {
      arr[++ix] = 0;
     } else if (withtime && ix == 2 && c == ' ') {
      arr[++ix] = 0;
     } else if (withtime && (ix == 3 || ix == 4) && c == ':') {
      arr[++ix] = 0;
     } else {
      e.value = v.substring(0, i);
      return;
     }
    }
  
    // We have finished scanning the string.  If there is a problem,
    // drop the last character and repeat the loop.
    if ((ix > 5) ||
        (!withtime && ix > 2)   ||
        (ix > 4 && arr[4] == 0) ||
        (ix > 3 && arr[3] == 0) ||
        (ix > 2 && arr[2] == 0) ||
        (ix > 1 && arr[1] == 0) ||
        (ix > 0 && arr[0] == 0) ||
        (arr[0] > 8) ||
        (ix > 0 && arr[0] > 2 && (arr[0] != 4 || arr[1] > 2 || arr[2] > 2)) ||
        (arr[2] > 2 && (arr[2] > 4 || arr[0] > 2 || arr[1] > 2)))
    {
     e.value = v.substring(0, v.length - 1);
    } else {
     break;
    }
   }
  
   // The remainder does reformatting if there is enough data for that.
   if (arr[2] == 4 && defcc == '1') { // mm/dd/yyyy
    e.value  = v.substring(arr[0] + arr[1] + 2, arr[0] + arr[1] + 6) + '-'; // year
    if (arr[0] == 1) e.value += '0';
    e.value += v.substring(0, arr[0]) + '-'; // month
    if (arr[1] == 1) e.value += '0';
    e.value += v.substring(arr[0] + 1, arr[0] + 1 + arr[1]); // day
   }
   else if (arr[2] == 4) { // dd-mm-yyyy
    e.value  = v.substring(arr[0] + arr[1] + 2, arr[0] + arr[1] + 6) + '-'; // year
    if (arr[1] == 1) e.value += '0';
    e.value += v.substring(arr[0] + 1, arr[0] + 1 + arr[1]) + '-'; // month
    if (arr[0] == 1) e.value += '0';
    e.value += v.substring(0, arr[0]); // day
   }
   else if (arr[0] == 4 && arr[2] > 0) { // yyyy-mm-dd
    e.value  = v.substring(0, arr[0]) + '-'; // year
    if (arr[1] == 1) e.value += '0';
    e.value += v.substring(arr[0] + 1, arr[0] + 1 + arr[1]) + '-'; // month
    e.value += v.substring(arr[0] + arr[1] + 2, arr[0] + arr[1] + 2 + arr[2]); // day (may be 1 digit)
   }
   else if (arr[0] == 8 && defcc == '1') { // yyyymmdd
    e.value  = v.substring(0, 4) + '-'; // year
    e.value += v.substring(4, 6) + '-'; // month
    e.value += v.substring(6, 8); // day
   }
   else if (arr[0] == 8) { // ddmmyyyy
    e.value  = v.substring(4, 8) + '-'; // year
    e.value += v.substring(2, 4) + '-'; // month
    e.value += v.substring(0, 2); // day
   }
   else {
    return;
   }
   if (withtime) {
    e.value += v.substring(arr[0] + arr[1] + arr[2] + 2);
   }
  }
  
  // Onblur handler to avoid incomplete entry of dates.
  //
  function dateblur(e, defcc, withtime) {
   if (typeof(withtime) == 'undefined') withtime = false;
  
   var v = e.value;
   if (v.length == 0) return;
  
   var arr = new Array(0, 0, 0, 0, 0);
   var ix = 0;
   for (var i = 0; i < v.length; ++i) {
    var c = v.charAt(i);
    if (c >= '0' && c <= '9') {
     ++arr[ix];
    } else if (c == '-' || c == '/' || c == ' ' || c == ':') {
     arr[++ix] = 0;
    } else {
     alert('Invalid character in date!');
     return;
    }
   }
  
   // A birth date may be just age in years, in which case we convert it.
   if (ix == 0 && arr[0] > 0 && arr[0] <= 3 && e.name.indexOf('DOB') >= 0) {
    var d = new Date();
    d = new Date(d.getTime() - parseInt(v) * 365.25 * 24 * 60 * 60 * 1000);
    var s = '' + d.getFullYear() + '-';
    if (d.getMonth() < 9) s += '0';
    s += (d.getMonth() + 1) + '-';
    if (d.getDate() < 10) s += '0';
    s += d.getDate();
    e.value = s;
    return;
   }
  
   if ((!withtime && ix != 2) || (withtime && ix < 2) || arr[0] != 4 || arr[1] != 2 || arr[2] < 1) {
    if (confirm('Date entry is incomplete! Try again?'))
     e.focus();
    else
     e.value = '';
    return;
   }
  
   if (arr[2] == 1) {
    e.value = v.substring(0, 8) + '0' + v.substring(8);
   }
  }
  
  // Private subroutine for US phone number formatting.
  function usphone(v) {
   if (v.length > 0 && v.charAt(0) == '-') v = v.substring(1);
   var oldlen = v.length;
   for (var i = 0; i < v.length; ++i) {
    var c = v.charAt(i);
    if (c < '0' || c > '9') {
     v = v.substring(0, i) + v.substring(i + 1);
     --i;
    }
   }
   if (oldlen > 3 && v.length >= 3) {
    v = v.substring(0, 3) + '-' + v.substring(3);
    if (oldlen > 7 && v.length >= 7) {
     v = v.substring(0, 7) + '-' + v.substring(7);
     if (v.length > 12) v = v.substring(0, 12);
    }
   }
   return v;
  }
  
  // Private subroutine for non-US phone number formatting.
  function nonusphone(v) {
   for (var i = 0; i < v.length; ++i) {
    var c = v.charAt(i);
    if (c < '0' || c > '9') {
     v = v.substring(0, i) + v.substring(i + 1);
     --i;
    }
   }
   return v;
  }
  
  // Telephone country codes that are exactly 2 digits.
  var twodigitccs = '/20/30/31/32/33/34/36/39/40/41/43/44/45/46/47/48/49/51/52/53/54/55/56/57/58/60/61/62/63/64/65/66/81/82/84/86/90/91/92/93/94/95/98/';
  
  // Onkeyup handler for phone numbers.  Helps to ensure a consistent
  // format and to reduce typing errors.  defcc is the default telephone
  // country code as a string.
  //
  function phonekeyup(e, defcc) {
   var v = e.value;
   var oldlen = v.length;
  
   // Deal with international formatting.
   if (v.length > 0 && v.charAt(0) == '+') {
    var cc = '';
    for (var i = 1; i < v.length; ++i) {
     var c = v.charAt(i);
     if (c < '0' || c > '9') {
      v = v.substring(0, i) + v.substring(i + i);
      --i;
      continue;
     }
     cc += c;
     if (i == 1 && oldlen > 2) {
      if (cc == '1') { // USA
       e.value = '+1-' + usphone(v.substring(2));
       return;
      }
      if (cc == '7') { // USSR
       e.value = '+7-' + nonusphone(v.substring(2));
       return;
      }
     }
     else if (i == 2 && oldlen > 3) {
      if (twodigitccs.indexOf(cc) >= 0) {
       e.value = v.substring(0, 3) + '-' + nonusphone(v.substring(3));
       return;
      }
     }
     else if (i == 3 && oldlen > 4) {
      e.value = v.substring(0, 4) + '-' + nonusphone(v.substring(4));
      return;
     }
    }
    e.value = v;
    return;
   }
  
   if (defcc == '1') {
    e.value = usphone(v);
   } else {
    e.value = nonusphone(v);
   }
  
   return;
  }
  
  // onKeyUp handler for mask-formatted fields.
  // This feature is experimental.
  function maskkeyup(elem, mask) {
   if (!mask || mask.length == 0) return;
   var i = 0; // elem and mask index
   var v = elem.value;
   for (; i < mask.length && i < v.length; ++i) {
    var ec = v.charAt(i);
    var mc = mask.charAt(i);
    if (mc == '#' && (ec < '0' || ec > '9')) {
     // digit required but this is not one
     break;
    }
    if (mc == '@' && ec.toLowerCase() == ec.toUpperCase()) {
     // alpha character required but this is not one
     break;
    }
   }
   v = v.substring(0, i);
   while (i < mask.length) {
    var mc = mask.charAt(i++);
    if (mc == '*' || mc == '#' || mc == '@') break;
    v += mc;
   }
   elem.value = v;
  }
  
  // onBlur handler for mask-formatted fields.
  // This feature is experimental.
  function maskblur(elem, mask) {
   var v = elem.value;
   var i = mask.length;
   if (i > 0 && v.length > 0 && v.length != i) {
    // there is a mask and a value but the value is not long enough
    for (; i > 0 && mask.charAt(i-1) == '#'; --i);
    // i is now index to first # in # string at end of mask
    if (i > v.length) {
     // value is too short even if trailing digits in the mask are ignored
     if (confirm('Field entry is incomplete! Try again?'))
      elem.focus();
     else
      elem.value = '';
     return;
    }
    // if the mask ends with digits then right-justify them in the value
    while (v.length < mask.length) {
     v = v.substring(0, i) + '0' + v.substring(i, v.length);
    }
    elem.value = v;
   }
  }
  
  