////////////////////////////////////////////////////////////////////////////////
//
// Miscellaneous handling form functions.
//
////////////////////////////////////////////////////////////////////////////////
function noEnter(evt) {
   var key;
   if (window.event)
      key = window.event.keyCode;
   else
      key = evt.which;

   return !(key == 13);
}


function submitEnter(evt, form) {
   var key;
   if (window.event)
      key = window.event.keyCode;
   else
      key = evt.which;

   if (key == 13)
      form.submit();
   else
      return true;
}


function formatCurrency(val) {
   var str   = String(val);
   var parts = str.split('.');
   var nInt  = parts[0];
   var nDec  = parts.length > 1 ? '.' + parts[1] : '.00';
   var rgx   = /(\d+)(\d{3})\.(\d\d)/;

   if (str.substring(0, 1) == "$")
      return str;

   var nVal = parseFloat(nInt + nDec).toFixed(2);

   while (rgx.test(nVal))
      nVal = nVal.replace(rgx, '$1' + ',' + '$2' + '.' + '$3');

   return "$" + nVal;
}


function currencyToFloat(cur) {
   var ret;
   var newcur;
   
   if (cur != "")
      newcur = cur.replace(",", "");
   else
      return cur;
   
   if (ret = newcur.match(/^\$(.+)/))
      return parseFloat(ret[1]);
   else
      return parseFloat(newcur);
}


function adjustValue(strField, nDelta, nLimit) {
   var objField = document.getElementById(strField);
   
   if (objField.disabled)
      return;
   
   var nValue   = (objField.value == "") ? 0 : parseInt(objField.value);

   if (objField.value != nValue)
      return;

   if ((nDelta < 0) && (nValue == 0)) 
      return;

   if ((nDelta > 0) && (nValue + nDelta > nLimit))
      return;

   objField.value = nValue + nDelta;
}

function caseName(objField) {
   if (typeof(objField) != "object")
      return;
      
   if (objField.value == "")
      return;

   var strSpacer = " ";
   var strNewVal = "";
   var strVal    = objField.value;
       strVal    = strVal.split(strSpacer);

   for ( var c = 0 ; c < strVal.length ; c++ ) {
      if (c+1 == strVal.length)
         strSpacer = "";

      strNewVal += strVal[c].substring(0, 1).toUpperCase() + strVal[c].substring(1, strVal[c].length) + strSpacer;
   }

   objField.value = strNewVal;
}
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
//
// Form validation functions.
//
////////////////////////////////////////////////////////////////////////////////

/**********************************************************
* jvalx generic embedded form validation library
* created:  20030314
*  author:   jeff emminger
* version:  2.2.0
*
* LICENSE:
* distributed with GNU GPL license
*   http://www.gnu.org/licenses/gpl.txt
* this library is free to use, provided these credits remain in place
* donations welcomed!  paypal me:  jeff_at_jeffemminger_dot_com
************************************************************/

/******
CHANGES:
  20031112:  fixed bug in isDate() with Mozilla - multiple calls
             to regex.test() fails when global switch present
  20040603:  made XHTML-compliant using comment nodes to store form element config rules
  20040603:  changed errored-field highlighting to remain until element is validated, not upon focus
  20040604:  added "conditional" value of "required" attribute of config node
  20040604:  added "condition" attribute of config node
  20040607:  fixed bug in isAlpha:  wasn't allowing capital letters.
  20040607:  added optional "regexIgnoreCase" attribute to be used when "regex" is present
  20040608:  fixed bug in Rules(), where attempting to strip regex delimiters would fail when no regex present
  20040608:  added enhancements to isInteger() regex
  20040609:  added support for multiple dataType values, comma delimited (thanks to John Miller
             of www.DynamicDrive.com for the suggestion)
  20040713:  fixed bug with optional logical groups not validating properly.
  20041108:  fixed bug in testMinMax, hidden and password fields not validating properly
  20050201:  added ability to allow whitespace in config nodes around "=", e.g. required = "true" 
  20050327:  fixed bug with regex nodes not stripping regex delimiters
  20050506:  fixed but with "decimal" type allowing integers, added "lenient" attribute
  20050517:  renamed internal debug() method to jdebug(), was freaking mozilla out
  20051010:  fixed but with testMinMax not being called if dataType == decimal
  20060209:  changed signatures of jvalOverride() and jvalReset() to require the form
             as an argument to prevent collision of multiple forms on the same page
  20060409:  removed default invalid field highlighting - user *must* specify a class of 
             "jvalx-invalid-field" and "jvalx-invalid-label" to have formatting applied
  20060410:  fixed bug in isSelected()

/******
TODO:
  - add "dateMask" attribute for "date" dataTypes
  - allow whitespace in config nodes around "=", e.g. required = "true"
  - rewrite checkGroupRequired()
******/

/******
*  acceptable dataTypes:
*    text    : any character
*    alpha   : any letter, space, hyphen or underscore
*    integer : any integer
*    decimal : any decimal
*    email   : any valid email format
*    phone   : U.S. format phone: x-xxx-xxx-xxxx
*    ccard   : Credit Card Number (MasterCard, Discover, Visa, AmEx, Diners)
*    date    : U.S. format date: MM/DD/YYYY
*    radio   : radio buttons
*    select  : selection list
*    [null]  : the input's default "type"

  config node custom attribute definitions:
  ===================================
  required    : (true|false|conditional) whether or not the input must be given a value
  conditional : the condition to test for when required="conditional".  the result of the condition
                  determines if the field is required or not. (true=required)
  dataType    : one or more of the above acceptable dataTypes, separated by commas
  lenient     : allows integers when dataType is decimal
  min         : (dec) minimum numeric value or text length of the input's value, depending on it's dataType
  max         : (dec) maximum numeric value or text length of the input's value, depending on it's dataType
  setlen      : (dec) exact text length of the input's value
  lowerElement: if dataType="dateRange", the name of the form element representing the upper date.
  upperElement: if dataType="dateRange", the name of the form element representing the lower date.
  equalOk     : (bool) if dataType="dateRange", whether or not the two dates can be equal
  firstOk     : (bool) if dataType="select" and required="true", whether or not the first option is an acceptable choice.
                  e.g.  first option is "Please Choose", then firstOk="false"
  regex       : a regular expression to test the input's value against, without opening and closing regex delimiters "/"
  regexIgnoreCase : whether the regex should be case insensitive or not.
  errorMsg    : the error message to be displayed (if any) if the input's value fails validation.


  prototypes for form elements with custom config nodes:
  =============================

alpha:
<input type="text"
  name=""
  value=""
  title=""
  size=""
  maxlength=""/><!--
  for=""
  required=""
  min=""
  max=""
  dataType="alpha"
  regex=""
  errorMsg="" -->

date:
<input type="text"
  name=""
  value=""
  title=""
  size="10"
  maxlength=""/><!--
  for=""
  required=""
  min=""
  max=""
  dataType="date"
  lowerElement|upperElement=""
  equalOk=""
  regex=""
  errorMsg="" -->

decimal:
<input type="text"
  name=""
  value=""
  title=""
  size=""
  maxlength=""/><!--
  for=""
  required=""
  min=""
  max=""
  minval=""
  maxval=""
  dataType="decimal"
  regex=""
  errorMsg=""  -->

email:
<input type="text"
  name=""
  value=""
  title=""
  size=""
  maxlength=""/><!--
  for=""
  required=""
  min=""
  max=""
  dataType="email"
  regex=""
  errorMsg="" -->

integer:
<input type="text"
  name=""
  value=""
  title=""
  size=""
  maxlength=""/><!--
  for=""
  required=""
  min=""
  max=""
  minval=""
  maxval=""
  dataType="integer"
  regex=""
  errorMsg="" -->

phone:
<input type="text"
  name=""
  value=""
  title=""
  size=""
  maxlength=""/><!--
  for=""
  required=""
  min=""
  max=""
  dataType="phone"
  regex=""
  errorMsg="" -->
  
select:
<select name=""
  size="1"
  ondblclick=""
  onchange=""/>
  <option value=""></option>
</select>
<!--
  for=""
  required=""
  firstOk=""
  errorMsg=""
-->


Phone regex:
   Strict ###[-.]###[-.]####     ^\d{3}[\.\-]\d{3}[\.\-]\d{4}$
   Strict (###)[ ]?###[-.]####   ^(\d{3})\s?\d{3}[\.\-]\d{4}$
   Strict ##########             ^\d{7}$
   Flexible                      ^((((\d\s)|\d)?[\(\-\.\s]\s?)?\d{3}\s?[\)\-\.\s]?\s?)?\d{3}\s?[\.\-\s]?\s?\d{4}$


***********************************************************/

/*** CONSTANTS ***/
var ELEMENT_NODE = 1;
var TEXT_NODE = 3;
var COMMENT_NODE = 8;

self.jval_debugger_on = false;

/*** MAIN VALIDATION FUNCTION ***/
function jValidate(f, bAllowMultipleSubmission) {
   //  ignore NS4.x
   if (document.layers) {
      return true;
   }
   else {
      //  loop through all form elements
      if (f.bSubmitted) return false;
      if (f.jvalOverridden) return true;
      
      var els = f.elements;
      var bFlag;

      for (var x = 0; x < els.length; x++) {
         if (els[x].tagName.toLowerCase() == "fieldset" || els[x].disabled) continue;

         var el = new El(els[x]);
         var bIsValid = false;
         if (!el.dataType) continue;
         //  loop, in case el has multiple dataTypes
         for (self.DTX = 0; self.DTX < el.dataType.length; self.DTX++) {
            //  determine the element's type
            switch(el.dataType[self.DTX]) {
               case "alpha":
                  bFlag = _text(el);
                  break;
               case "integer":
                  bFlag = _numeric(el);
                  break;
               case "decimal":
                  bFlag = _numeric(el);
                  break;
               case "email":
                  bFlag = _email(el);
                  break;
               case "phone":
                  bFlag = _phone(el);
                  break;
               case "ccard":
                  bFlag = _ccard(el);
                  break;
               case "date":
                  bFlag = _date(el);
                  break;
               case "checkbox":
                  bFlag = _checkbox(el);
                  break;
               case "file":
                  bFlag = _text(el);
                  break;
               case "hidden":
                  bFlag = _text(el);
                  break;
               case "password":
                  bFlag = _text(el);
                  break;
               case "radio":
                  bFlag = _radio(el);
                  break;
               case "select":
                  bFlag = _select(el);
                  break;
               case "text":
                  bFlag = _text(el);
                  break;
               case "textarea":
                  bFlag = _text(el);
                  break;
               default:
                  bFlag = true;
            }
            //  test the element's constraint, if any
            var bConstraintOk = el.testConstraint();

            if (bFlag && bConstraintOk) bIsValid = true;

         }

         if (!bIsValid) {
            resetOriginalRequired(f);
            
            if (!bFlag) {
               return el.throwError();
            }
            else if (!bConstraintOk) {
               return el.throwError(true);
            }
         }

         //  reset the element's backgroundColor in case still yellow
         if (/rgb\(255,\s?255,\s?0\)/.test(el.formElement.style.backgroundColor)) {
            el.formElement.style.backgroundColor = "";
         }
      }
      if (!bAllowMultipleSubmission) window.bSubmitted = true;
      return true;
   }
}

function El(el) {
   //  make an "El" object to expedite attribute retrieval
   this.form = el.form;
   this.formElement = el;
   this.type = this.type?this.type:null;
   this.name = el.name?el.name:null;
   this.value = el.value;
   this.title = el.title?el.title:null;
   this.size = el.size?el.size:null;

   //  store the rules for this element
   var ruleNode = getRuleNode(el);
   if (ruleNode) {
      ruleText                = ruleNode.nodeValue;
      this.dataType           = getRule(ruleNode, "dataType").length
                              ? getRule(ruleNode, "dataType").replace(/\s/g, "").split(",")
                              : [el.type.split("-")[0].toString().toLowerCase()];
      this.required           = getRule(ruleNode, "required");
      this.condition          = getRule(ruleNode, "condition");
      this.constraint         = getRule(ruleNode, "constraint");
      this.setlen             = getRule(ruleNode, "setlen");
      this.min                = getRule(ruleNode, "min");
      this.max                = getRule(ruleNode, "max");
      this.minval             = getRule(ruleNode, "minval");
      this.maxval             = getRule(ruleNode, "maxval");
      this.regex              = getRule(ruleNode, "regex");
      this.regexIgnoreCase    = /^true$/i.test(getRule(ruleNode, "regexIgnoreCase"));
      this.errorMsg           = getRule(ruleNode, "errorMsg").length
                              ? getRule(ruleNode, "errorMsg").replace(/[\n\r]\s+/g, " ").replace(/\\n/g, "\n")
                              : "Please enter a valid value.";
      this.constraintErrorMsg = getRule(ruleNode, "constraintErrorMsg").length
                              ? getRule(ruleNode, "constraintErrorMsg").replace(/\\n/g, "\n")
                              : this.errorMsg;
      this.lowerElement       = getRule(ruleNode, "lowerElement").length
                              ? el.form[getRule(ruleNode, "lowerElement")]
                              : null;
      this.upperElement       = getRule(ruleNode, "upperElement").length
                              ? el.form[getRule(ruleNode, "upperElement")]
                              : null;
      this.equalOk            = /^true$/i.test(getRule(ruleNode, "equalOk"));
      this.firstOk            = /^true$/i.test(getRule(ruleNode, "firstOk"));
      this.lenient            = /^true$/i.test(getRule(ruleNode, "lenient"));
   }
   return this;
}

function getRuleNode(el) {
   //  the rules node should come next...
   var nextNode = nextCommentNode(el);
   if (nextNode
      && nextNode.nodeType == COMMENT_NODE
      && getRule(nextNode, "for") == el.getAttribute("name")) {
      return nextNode;
   }
   //  ...else it should be previous...
   var previousNode = previousCommentNode(el);
   if (previousNode
      && previousNode.nodeType == COMMENT_NODE
      && getRule(previousNode, "for") == el.getAttribute("name")) {
      return previousNode;
   }
   // ...or else we can't find it
   return null;
}
function previousCommentNode(el) {
   if (!el.previousSibling) {
      return null;
   }
   else {
      if (el.previousSibling.nodeType == COMMENT_NODE) {
         return el.previousSibling;
      }
      else return previousCommentNode(el.previousSibling);
   }
}

function nextCommentNode(el) {
   if (!el.nextSibling) {
      return null;
   }
   else {
      if (el.nextSibling.nodeType == COMMENT_NODE) {
         return el.nextSibling;
      }
      else return nextCommentNode(el.nextSibling);
   }
}

function getRule(node, sRule) {
   //  extracts sRule from node.nodeValue
   var sText = node.nodeValue;
   
   if (sText.indexOf(sRule) == -1) return "";
      
   //  escape quotes and backslashes so the regex works
   sText = sText.replace(/\\\"/g, escape("\"")).replace(/\\\\/g, escape("\\"));
   /* fix text editor color coding "*/
   
   var re = new RegExp("\\b" + sRule + "\\s*=\\s*\"([^\"]*)\"", "i");
   var arr = re.exec(sText);
   
   if (arr == null || arr.length < 1) {
      return "";
   }
   else {
      //  for "regex" rule, remove regex delimiters if present
      if (sRule.toLowerCase() == "regex") {
         arr[1] = arr[1].replace(/^\/|\/$/g, "");
      }
      //  unescape the quotes and backslashes
      return unescape( arr[1] );
   }
}

function setRule(node, key, val) {
   var sText = node.nodeValue;
   var keyStart, valStart, valEnd, result, sKeyAndVal;
   
   var re = new RegExp("\\b" + key + "(\\s)*=(\\s)*", "i");
   
   if ((result = re.exec(sText)) != null) {
      //  found the key, need to extract whole key/val pair
      keyStart = result.index;
      valStart = sText.indexOf('"', keyStart) + 1;
      
      if (/regex|errormsg/i.test(key)) {
         //  these attributes allow quotes and backslashes
         //  end == position of ", provided the prev char is not \, unless prev char to \ is also \
         for (var x = valStart; x < sText.length; x++) {
            if ((sText.charAt(x) == '"'
               && sText.charAt(x-1) == "\\"
               && sText.charAt(x-2) == "\\")
               ||
               (sText.charAt(x) == '"'
               && sText.charAt(x-1) != "\\")) {
               valEnd = x + 1;
               break;
            }
         }
      }
      else {
         valEnd = sText.indexOf('"', valStart) + 1;
      }
      
      //  need to escape backslashes in sKeyAndVal so replace() finds them 
      sKeyAndVal = sText.substring(keyStart, valEnd).replace(/\\/g, "\\\\");
      re = new RegExp("\\b" + sKeyAndVal, "i");
      
      sText = sText.replace(re, key + "=\"" + val + "\"");
      
      //  create a new comment node containing sText, replace the old one with it
      var newNode = document.createComment(sText);
      node.parentNode.replaceChild(newNode, node);
   }
}

/************  class methods  ************/

/**
 * calls highlight(), alerts the errorMsg, returns false
 */
El.prototype.throwError = function _throwError(bConstraintError) {
   this.highlight();
   //  flag form as not submitted
   window.bSubmitted = false;
   try {
      this.formElement.focus();
      this.formElement.select();
   }
   catch(e) {}
   //  show error message
   if (bConstraintError) alert(this.constraintErrorMsg);
   else if (this.errorMsg) alert(this.errorMsg);

   return false;
}

/**
 * returns whether or not a date range's lower & upper values can be equal
 */
El.prototype.getEqualOk = function _getEqualOk() {
   return this.equalOk?this.equalOk == true:
      this.upperElement && this.upperElement.equalOk?
      this.upperElement.equalOk == true:
      this.lowerElement && this.lowerElement.equalOk?
      this.lowerElement.equalOk == true:
      false;
}

function jGetElementsByName(form, name) {
   var els = form.getElementsByTagName("*");
   var matches = [];
   for (var x = 0; x < els.length; x++) {
      var re = new RegExp('\\b' + name + '\\b');
      if (re.test(els[x].name)) {
         matches.push(els[x]);
      }
   }
   return matches;
}

/**
 * highlights the invalid form element
 */
El.prototype.highlight = function _highlight() {
   var els = [];
   if (window.rangeError) {
      els[0] = this.formElement;
      els[1] = this.lowerElement?this.lowerElement:this.upperElement;
      window.rangeError = false;
      this.errorMsg = "The Lower date must be less than" +
         (this.getEqualOk()?" or equal to":"") +
         " the Upper Date.";
   }
   else els = jGetElementsByName(this.form, this.name);

   for (var x = 0; x < els.length; x++) {
      //  apply user-specified class
      if (!/jvalx-invalid-field/i.test(els[x].className)) {
         els[x].className += " jvalx-invalid-field";
      }
      
      //  highlight the field's label if present
      var id = els[x].id;
      
      if (id != null && id.length > 0) {
         var labels = this.form.getElementsByTagName("label");
         labelLoop:
         for (var y = 0; y < labels.length; y++) {
            var labelFor = labels[y].attributes.getNamedItem("for").value;
            
            if (labelFor != null && labelFor == id) {
               labels[y].style.borderWidth = "1px";
               if (!/jvalx-invalid-label/i.test(labels[y].className)) {
                  labels[y].className += " jvalx-invalid-label";
               }
               break labelLoop;
            }
         }// end for each label
      }// end if has an id
   }// end for each el
   return true;
}
/**
 * checks if an element is part of an array of like-named elements
 * if so, if any in group have value, all become required
 */
El.prototype.checkGroupRequired = function _checkGroupRequired() {
   var els = jGetElementsByName(this.form, this.name);

   if (els.length == 1) return true;
   else {
      var bValue = false;
      for (var sgr = 0; sgr < els.length; sgr++) {
         if (this.isRequired() || els[sgr].value) bValue = true;
      }
      if (!this.isRequired()) {
         for (var sgr = 0; sgr < els.length; sgr++) {
            var ruleNode = getRuleNode(els[sgr]);
            setRule(ruleNode, "required", bValue);
         }
         var originalRequired = this.required;
         this.required = bValue;
         
         //  need to reset original "required" value after validation
         addResetOriginalRequired(this.name, originalRequired);
      }
   }
}

El.prototype.isRequired = function() {
   if (this.required.toString() == "true") return true;
   else if (this.required == "conditional") {
      return eval(this.condition);
   }
   else return false;
}

El.prototype.testConstraint = function() {
   if (this.constraint && this.constraint.length) {
      //  fix for if "this" is presented as a function arg
      return eval(this.constraint.replace(/this([\s\,\)])/g, "this.formElement$1"));
   }
   else return true;
}

/************  end class methods  ************/

/************  begin dataType validator functions ************/
window.rangeError = false;

function _text(el) {
   el.checkGroupRequired();

   if (el.isRequired() && !el.value) return false;
   else if (el.value) {
      if (el.dataType[self.DTX] == "alpha") {
         if (!isAlpha(el.value)) {
            return false;
         }
      }
      if (!testSetLen(el)) {
         el.errorMsg += "\nMust be " + el.setlen + " characters.";
         return false;
       }
      if (!testMinMax(el)) {
         el.errorMsg += ((el.min)?"\nMinimum " + el.min + " characters.":"") +
            ((el.max)?"\nMaximum " + el.max + " characters.":"");
         return false;
      }
      if (!testRegex(el)) {
         return false;
      }
   }
   return true;
}

function _numeric(el) {
   if (el.isRequired() && el.value == "") return false;
   else if (el.value != "") {
      if (el.dataType[self.DTX] == "decimal") {
         //  lenient == allow integers
         if (el.lenient) {
            if (isNaN(el.value)) return false;
         }
         else {
            if (!/^\d+\.\d+$/.test(el.value)) return false;
         }
      }
      
      if (el.dataType[self.DTX] == "integer")
         if (!isInteger(el.value)) return false;
         
      if (!testSetLen(el)) {
         el.errorMsg += "\nMust be " + el.setlen + " characters.";
         return false;
       }
       
      if (!testMinMax(el)) {
         el.errorMsg += ((el.min)?"\nMinimum value length " + el.min + ".":"") +
            ((el.max)?"\nMaximum value length " + el.max + ".":"");
         return false;
      }
      
      if (!testMinMaxVal(el)) {
         el.errorMsg += ((el.minval)?"\nMinimum value " + el.minval + ".":"") +
            ((el.maxval)?"\nMaximum value " + el.maxval + ".":"");
         return false;
      }
      
      if (!testRegex(el)) return false;
   }
   return true;
}

function _email(el) {
   if (el.isRequired() && !el.value) return false;
   else if (el.value) {
      return isEmail(el.value);
   }
   return true;
}

function _select(el) {
   if (el.isRequired()) {
      return isSelected(el.formElement, el.firstOk);
   }
   else return true;
}

function _date(el) {
   if ((el.isRequired() || (el.upperElement && el.upperElement.value)) && !el.value) {
      return false;
   }
   else if (el.value) {
      if (!fixDate(el.formElement)) {
         return false;
      }
      else {
         el.value = fixDate(el.formElement);
         el.formElement.value = fixDate(el.formElement);
      }

      if (el.upperElement) el.upperElement.required = "true";
      //  if dateRange, validate range
      if (el.lowerElement) {
         if (!fixDate(el.lowerElement)) {
            return false;
         }
         else el.lowerElement.value = fixDate(el.lowerElement);

         if (el.getEqualOk()) {
            if (new Date(el.value).getTime() < new Date(el.lowerElement.value).getTime()) {
               window.rangeError = true;
               return false;
            }
         } else {
            if (new Date(el.value).getTime() <= new Date(el.lowerElement.value).getTime()) {
               window.rangeError = true;
               return false;
            }
         }
      }
   }
   return true;
}

function _phone(el) {
   if (el.isRequired() || el.value) {
      return isPhone(el.value);
   }
   else return true;
}

function _ccard(el) {
   if (el.isRequired() || el.value) {
      return isCCard(getCCType(), el.value);
   }
   else return true;
}

function _checkbox(el) {
   //  same behavior as radio until "requireAll" attr done
   return _radio(el);
   /*
   if (el.isRequired()) {
      return el.formElement.checked;
   }
   else return true;
   */
}

function _radio(el) {
   if (el.isRequired()) {
      var els = jGetElementsByName(el.form, el.name);
      var bChecked = false;
      for (var x = 0; x < els.length; x++)
         if (els[x].checked) bChecked = true;
      return bChecked;
   }
   else return true;
}

/************  end dataType validator functions ************/

/************  helper functions  ************/

/**
 * overrides jValidate() to allow form to be submitted without validation
 */
function jvalOverride(form) {
   form.jvalOverridden = true;
}

/**
 * resets form to "unsubmitted" if calling jValidate() before custom validation
 */
function jvalReset(form) {
   form.bSubmitted = false;
}

function isAlpha(s) {
   return /^[a-z\s-_]+$/i.test(s);
}

function testRegex(el) {
   if (el.regex != null)
      if (el.regex.length > 0) {
         sI = el.regexIgnoreCase ? "i" : "";
         re = new RegExp(el.regex, sI);
         jdebug("regex: " + el.regex);
         jdebug("sI: " + sI);
         jdebug("test: " + /^C/.test(el.value));
         jdebug("test2: " + re.test(el.value));
         jdebug("source: " + re.source);
         return re.test(el.value);
      }
   return true;
}

function testSetLen(el) {
   if (el.dataType[self.DTX] == "text"
         || el.dataType[self.DTX] == "alpha"
         || el.dataType[self.DTX] == "textarea"
         || el.dataType[self.DTX] == "integer"
         || el.dataType[self.DTX] == "numeric") {
      if (el.setlen && el.value.length != el.setlen) return false;
   }
   return true;
}

function testMinMax(el) {
   if (el.dataType[self.DTX] == "text"
         || el.dataType[self.DTX] == "alpha"
         || el.dataType[self.DTX] == "textarea"
         || el.dataType[self.DTX] == "hidden"
         || el.dataType[self.DTX] == "password") {
      if (el.min && el.value.length < el.min) return false;
      if (el.max && el.value.length > el.max) return false;
   }
   else {
      if (el.min && parseFloat(el.value.replace(/\,/g, "")) < el.min) return false;
      if (el.max && parseFloat(el.value.replace(/\,/g, "")) > el.max) return false;
   }
   return true;
}

function testMinMaxVal(el) {
   if (el.dataType[self.DTX] == "integer"
         || el.dataType[self.DTX] == "decimal") {
      if (el.minval && el.value < el.minval) return false;
      if (el.maxval && el.value > el.maxval) return false;
   }
   return true;
}

function addResetOriginalRequired(name, val) {
   if (!self.jvalOriginalRequired)
      self.jvalOriginalRequired = [];
   self.jvalOriginalRequired.push([name,val]);
}

function resetOriginalRequired(form) {
   if (self.jvalOriginalRequired) {
      var OR = jvalOriginalRequired;
      for (var x = 0; x < OR.length; x++) {
         var obj = OR.pop();
         var name = obj[0];
         var val = obj[1];
         var els = jGetElementsByName(form, name);
         
         for (var y = 0; y < els.length; y++) {
            var ruleNode = getRuleNode(els[x]);
            setRule(ruleNode, "required", val);
         }         
      }
   }
}

/**********************************************************
*   general function library
***********************************************************/

function isDate(testDate) {
   if ( !(/^[\d\/\.\-]+$/.test(testDate)) ) {
      return false;
   }

   var testDate = new Date(testDate);
   if (isNaN(testDate.getTime())) {
      return false;
   }
   else {
      return true;
   }
}

function isPhone(s) {
   var re = /^((((\d\s)|\d)?[\(\-\.\s]\s?)?\d{3}\s?[\)\-\.\s]?\s?)?\d{3}\s?[\.\-\s]?\s?\d{4}$/;
   return re.test(s);
}

function isCCard(type, ccnum) {
   if (type == "Visa")
      var re = /^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/;        // Visa: length 16, prefix 4, dashes optional.
   else if (type == "MasterCard")
      var re = /^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/;   // Mastercard: length 16, prefix 51-55, dashes optional.
   else if (type == "Discover")
      var re = /^6011-?\d{4}-?\d{4}-?\d{4}$/;          // Discover: length 16, prefix 6011, dashes optional.
   else if (type == "American Express")
      var re = /^3[4,7]\d{13}$/;                       // American Express: length 15, prefix 34 or 37.
   else if (type == "Diners")
      var re = /^3[0,6,8]\d{12}$/;                     // Diners: length 14, prefix 30, 36, or 38.

   if (!re.test(ccnum))
      return false;

   // Remove all dashes for the checksum checks to eliminate negative numbers
   ccnum = ccnum.split("-").join("");

   // Checksum ("Mod 10")
   // Add even digits in even length strings or odd digits in odd length strings.
   var checksum = 0;

   for ( var i = (2-(ccnum.length % 2)) ; i <= ccnum.length ; i += 2 )
      checksum += parseInt(ccnum.charAt(i-1));

   // Analyze odd digits in even length strings or even digits in odd length strings.
   for ( var i = (ccnum.length % 2) + 1 ; i<ccnum.length ; i += 2 ) {
      var digit = parseInt(ccnum.charAt(i-1)) * 2;
      if (digit < 10)
         checksum += digit;
      else
         checksum += (digit-9);
   }

   if ((checksum % 10) == 0)
      return true;
   else
      return false;
}

function isEmail(sTest) {
   /*
   *   accepts emails like:
   *      foo@bar.com
   *      abc_123.zxy-987.etc@do_re.me-fa.so.la.ti.do
   *
   *   allows alphanumeric, underscore and hyphen
   *   ...except must be 2+ alpha only after last dot.
   *   returns boolean
   */
   var pattern = /^[\w\-]+(\.[\w\-]+)*@[\w\-]+\.([\w\-]+\.)*[a-z]{2,}$/i;
   return pattern.test(sTest);
}

function isAlphaNumeric(sTest) {
   /*
   *  tests to make sure a string is alpha-numeric only; returns boolean
   *  pattern1 ensures string contains at least one alpha character, case-insensitive
   *  pattern2 allows a-zA-Z0-9 and hypen and underscore and whitespace
   */
   var bFlag = false;

   var pattern1 = /[a-zA-Z]+/;
   if (pattern1.test(sTest)) {
      var pattern2 = /^[\w\-\s]+\s*$/;
      bFlag = pattern2.test(sTest);
   }
   return bFlag;
}

function isInteger(s) {
   // tests to make sure test string is an integer; returns boolean
   return /^-?\d{1,3}(,?\d{3})*(\.00)?$/.test(s);
}

function fixDate(oEl, bEmptyOK) {
   /*
   *   1.  format date from mmddyy or mmddyyyy to mm/dd/yyyy
   *   2.  check to see if formatted date is a valid date
   *   3.  return formatted date string
   *   *** requires isDate() ***
   */

   //  bEmptyOK allows null values in date fields
   if (bEmptyOK && !oEl.value) return true;

   var sVal = oEl.value.replace(/[\\\-\.\s\,\:\;\*\+]/gi,"/");
   var sDate = "";
   var bFlag = true;

   //  format date from mmddyy or mmddyyyy to mm/dd/yyyy

   //  format numbers < 10 to "0" + digit
   if (sVal.indexOf("/") != sVal.lastIndexOf("/")) {
      arTemp = sVal.split("/");
      for (x = 0; x < 3; x++)
         arTemp[x] = (arTemp[x] < 10)?"0" + parseInt(arTemp[x], 10):arTemp[x];
      sVal = arTemp.join("/");
   }

   //  fail if mm & dd not two digits, yy not 2 or 4 digits
   if (sVal.length != 6 && sVal.length != 8 && sVal.length != 10 ||
      (sVal.indexOf("/") == 1 || sVal.indexOf("/") == 3)) {
      bFlag = false;
   }

   else if (sVal.length < 10) {
      //  sVal is mmddyy, mmddyyyy or mm/dd/yy format
      if (sVal.indexOf("/") < 0) {
         //  mmddyy or mmddyyyy
         sDate = sVal.substring(0,2) + "/" + sVal.substring(2,4) + "/" + sVal.substring(4);
      }
      else {
         //  mm/dd/yy format:  ok already
         sDate = sVal;
      }
   }
   else {
      //  sVal is mm/dd/yyyy format already
      sDate = sVal;
   }

   sY = sDate.substring(6);

   if (sY.length == 2) {
      sY = (parseInt(sY, 10) > parseInt(new Date().getFullYear().toString().substring(2), 10) + 1)?"19" + sY:"20" + sY;
   }

   //  rebuild sDate with fixed 4-digit year
   sDate = sDate.substring(0,6) + sY;

   //  check to see if final formatted date is valid
   if (!isDate(sDate)) {
      bFlag = false;
   }

   if (!bFlag) return false;
   else {
      //  turn sDate into Date object, extract MM DD & YYYY
      dDate = new Date(sDate);
      iM = dDate.getMonth() + 1;
      iD = dDate.getDate();
      iY = dDate.getFullYear();

      //  return MM/DD/YYYY formatted string
      return ((iM > 9)?iM:"0" + iM) + "/" + ((iD >9)?iD:"0" + iD) + "/" + iY;
   }
}

function isSelected(oSel, bFirstOk) {
   //  returns false if no option selected
   var x = bFirstOk?-1:0;
   return (oSel.selectedIndex > x);
}

function getDebugWin() {
   if (!window["debugWin"]) {
      var debugWin = window.open("");

      var ta = document.createElement("textarea");
      ta.setAttribute("id", "jvalDebugger");
      ta.style.width = "100%";
      ta.style.height = "35em";

      debugWin.document.body.appendChild(ta);
      
      window["debugWin"] = debugWin;
   }
   
   if (!document.getElementById("jvalDebugger")) {
      var div = document.createElement("div");
      div.setAttribute("style", "clear:both;");
      var ta = document.createElement("textarea");
      ta.setAttribute("id", "jvalDebugger");
      ta.style.width = "100%";
      ta.style.height = "35em";
      div.appendChild(ta);
      document.body.appendChild(div);
   }
}
function jdebug(s) {
   if (self.jval_debugger_on == true) {
      getDebugWin();
      document.getElementById("jvalDebugger").value += s + "\n";
      
      window["debugWin"].document.getElementById("jvalDebugger").value += s + "\n";
   }
}