/*---------------------------------------------------------------------------------
  VAM.js
  Copyright 2003, Peter L. Blum, All Rights Reserved
  Release 1.0.0.0
  Part of Validators And More (VAM) v1.0
  No part of pVO file can be transmitted, reproduced, or distributed without express written consent
  of Peter Blum. Email: Contact@PeterBlum.com.
  pVO file provides client side scripting common to all controls in pVO product.
  
  This file must always be loaded to support any client-side VAM functions of VAM2.js and VAM3.js.
  Contains:
  - support functions
  - validation framework
  - common conditions
---------------------------------------------------------------------------------*/

//---- BROWSER DETECTION  ----------------------------------------------------------
// pVO detection code is based on http://www.xs4all.nl/~ppk/js/gVAM_UA.html
var gVAM_UA = navigator.userAgent.toLowerCase();
var gVAM_OS, gVAM_Browser, gVAM_Version, gVAM_place, gVAM_Temp;
var gIsIEWin = false; 
var gIsIEWin55 = false;
var gIsIEMac = false;
var gIsIEMac51 = false;
var gIsNetscapeNav = false;
var gIsNetscapeMoz = false;
var gIsSafari = false;
/* off until we need to test for it
var gIsKonqueror = false;
var gICab = false;
var gIsOpera = false;   // v1-6
var gIsOpera7 = false;  // v7 up
*/
var gSupportsInnerHTML = (document.body != null) && (document.body.innerHTML != null);
var gSupportsSetInterval = window.setInterval != null;

/* off until we need to test for it
if (checkIt('konqueror'))
{
	gVAM_Browser = "Konqueror";
	gVAM_OS = "Linux";
	gIsKonqueror = true;
}
else if (checkIt('omniweb')) gVAM_Browser = "OmniWeb"
else if (checkIt('opera')) {gVAM_Browser = "Opera"; gIsOpera = true; }
else if (checkIt('webtv')) gVAM_Browser = "WebTV";
else if (checkIt('icab')) { gVAM_Browser = "iCab"; gICab = true; }
else 
*/
if (checkIt('msie')) {gVAM_Browser = "Internet Explorer"; gIsIEWin = true; }
else if (checkIt('safari')) {browser = "Safari"; gIsSafari = true; } // !dc v1.0.1
else if (checkIt('netscape')) {gVAM_Browser = "Netscape Mozilla"; gIsNetscapeMoz = true; }
else if (!checkIt('compatible'))
{
	gVAM_Browser = "Netscape Navigator"
	gVAM_Version = gVAM_UA.charAt(8);
	gIsNetscapeNav = true;
}
else gVAM_Browser = "Unknown";

if (!gVAM_Version && gVAM_place) gVAM_Version = gVAM_UA.charAt(gVAM_place + gVAM_Temp.length);

if (!gVAM_OS)
{
	if (checkIt('linux')) gVAM_OS = "Linux";
	else if (checkIt('x11')) gVAM_OS = "Unix";
	else if (checkIt('mac')) gVAM_OS = "Mac"
	else if (checkIt('win')) gVAM_OS = "Windows"
	else gVAM_OS = "Unknown";
}
if (gIsIEWin && (gVAM_OS == "Mac"))
{
   gIsIEWin = false;
   gIsIEMac = true;
   if (gVAM_UA.indexOf("msie 5.1") > -1)
     gIsIEMac51 = true;
}
else if (gIsIEWin)
{
   if ((gVAM_UA.indexOf("msie 5.5") > -1) || (gVAM_UA.indexOf("msie 6") > -1))
      gIsIEWin55 = true;
}
/* not used. If we need to test for opera, add it back
else if (gIsOpera)   //!dc v1.0.1
{
   if (parseInt(gVAM_Version) >= 7)
   {
     gIsOpera7 = true;
     gIsOpera = false;
   }
}
*/
function checkIt(string)
{
	gVAM_place = gVAM_UA.indexOf(string) + 1;
	gVAM_Temp = string;
	return gVAM_place;
}

// VAM_InitMultiAction needs to pass pAO.id around to VAM_HookupControl
// because child actions never have pAO.id defined
var gVAM_MAId = null;

//---- COMMON FUNCTIONS ----------------------------------------------------------

function VAM_GetById(pId)
{
   if (document.getElementById)
      return document.getElementById(pId);
   else if (document.all)
      return document.all[pId]
   else if (document.layers)  // NS 6, only handles one layer of nesting
   {
      var vElement = "";
      eval("if (document."+ pId + ") vElement = document."+pId+"; else vElement =document." + gVAM_FormName + "." + pId);
      return vElement;
   }
   else
      return null;   // return eval("document." + gVAM_FormName + "." + pId);
}  // VAM_GetById

//!dc v1.0.1
// VAM_GetAtt is a shell for the getAttribute function that
// can assign a default when the attribute is missing.
// It handles browsers that lack getAttribute.
function VAM_GetAtt(pE, pAName, pDefVal) 
{
   if (pE.getAttribute)
   {
      var vR = pE.getAttribute(pAName, 0);
      if (vR == null)
         vR = pDefVal; 
      else if ((vR == "") && (!document.all))  // Gecko returns "" for empty
         vR = pDefVal;
      return vR;
   }
   else   // NS 4 - do nothing
      return pDefVal;
} // VAM_GetAtt()
//!dc-end

// VAM_SetInnerHTML sets the InnerHTML property unless its the IE Mac 5.1 gVAM_Browser
// That gVAM_Browser hangs when setting innerHTML to anything other than a blank string
// when pFld is a TD (cell of a table). On the Mac, it inserts the text into a <SPAN> tag.
// So don't expect the innerHTML to be identical to pValue.
function VAM_SetInnerHTML(pFld, pValue)
{
   if (gIsIEMac51)
   {
      pFld.innerHTML = "";
      var vNewEl = document.createElement("span");
      vNewEl.innerHTML = pValue;
      pFld.appendChild(vNewEl);
   }
   else
      pFld.innerHTML = pValue;
}  // VAM_SetInnerHTML

function VAM_SetLeftPos(pFld, pPos)
{
   if (pFld.style.pixelLeft)
      pFld.style.pixelLeft = pPos;
   else if (pFld.style.posLeft)
      pFld.style.posLeft = pPos.toString() + "px";
   else
      pFld.style.left = pPos.toString() + "px";
}

function VAM_SetTopPos(pFld, pPos)
{
   if (pFld.style.pixelTop)
      pFld.style.pixelTop = pPos;
   else if (pFld.style.posTop)
      pFld.style.posTop = pPos.toString() + "px";
   else
      pFld.style.top = pPos.toString() + "px";
}

// Sets the focus to the field of pFldId. It selects the field if possible.
// It scrolls the page to show the field
function VAM_SetFocus(pFldId)
{
   var vFld = VAM_GetById(pFldId);
   if ((vFld != null) && (vFld.focus != null) && 
       ((vFld.type == null) || (vFld.type != "hidden")) &&  //!bf v1.0.1 hidden field (input type='hidden') is possible with RichTextBox controls that use the hidden field as the "real data" on the form
       ((vFld.disabled == null) || !vFld.disabled) && 
       ((vFld.style == null) || (vFld.style.visibility != "hidden")))   // visibility isn't tested on parent controls
   {
      vFld.focus();
      if (vFld.select)
         vFld.select();
   }
}  // VAM_SetFocus

// VAM_ParseInt takes numeric text and strips off lead zeros.
// It returns the resulting value as an integer.
// VAM_ParseInt resolves an issue with parseInt which converts values with lead zeros as octal values
// which accepts trailing non digit characters.
// VAM_ParseInt is designed to return NaN when there is an illegal value including any characters other
// than digits. It does not trim spaces or handle octal or hexidecimal values.
function VAM_ParseInt(pVal)
{
   var vR = 0;
   var vNeg = false;
   for (var vI = 0; vI < pVal.length; vI++)
   {
      var vC = pVal.charAt(vI);
      if ((vC >= '0') && (vC <= '9'))
         vR = (vR * 10) + parseInt(vC);
      else if (((vC == "-") || (vC == "(")) && (vI==0))
         vNeg = true
      else if (vC != ")")
         return NaN;
         
   }
   if (vNeg)
      vR = -vR;
   return vR;
   
//   if ((pValue == '0') || (pValue == '00'))
//      return 0;
//   else 
//      return parseInt(pValue.replace(/^0*/, ''));
      
}  // VAM_ParseInt()

// Pass any string containing HTML tags. It removes the tags. It returns a string without tags.
// Tags are always "<" + 1 or more characters + ">".
function VAM_StripTags(pHTML)
{
//!bf v1.0.1
   return gIsIEMac ? pHTML : VAM_RERpl(pHTML, "<(.|\n)+?>", "");
/*
   return VAM_RERpl(pHTML, "<(.|\n)+?>", "");
//!bf-end */   
}  // VAM_StripTags

// RegExp text replacement with options "gi" (global, ignore case)
// pText is the text buffer. pFind is the text to find. pReplace is the text to replace
function VAM_RERpl(pText, pFind, pReplace)
{
   var vRx = new RegExp(pFind, "ig");  // NOTE: Don't use the "m" (multiline) flag because its buggy on many browsers. Only use it when using the ^ and $ symbols.
   return pText.replace(vRx, pReplace);
}  // VAM_RERpl

//***** VAM OBJECT METHODS -*******************************
// These function are associated with the VAM object to handle the EvalFnc, InitFnc, 
// and other methods.
// Every one takes the VAM Object as its first parameter. While the user can access
// the same object from 'pVO' because its a method, establishing subclasses
// works only when you pass around the VAM Object. So don't use 'pVO'. Use the first parameter.

//---- Condition InitFnc -----------------------------------------------
// Method name: InitFnc
// VAM Object type: Condition
// Purpose: Initializes the Condition object. Called by VAM_InitActions as the page is loaded
//   - Initialize missing properties. The server side omits some properties at their default values
//     These are assigned by the InitFnc to their default. In addition, it creates any properties
//     that are the exclusive domain of the client side like a reference to an element on the page.
//   - Register each field that runs onchange or onclick using VAM_HookupControl.
//   - Due to subclassing, it may need to call an ancestor class's InitFnc. All condition
//     InitFncs must call VAM_InitCond.
// Parameters:
//   Condition object
//   Action object
// Returns:
//     Nothing.


// default for all conditions
function VAM_InitCond(pCO, pAO)
{
   if (pCO.Trim == null)
      pCO.Trim = true;
   if (pCO.NotCond == null)
      pCO.NotCond = false;
   if (pCO.ConvVal == null)
      pCO.ConvVal = VAM_ConvertStrFld;
}

// for BaseOneFieldCondition
function VAM_InitOneFldCond(pCO, pAO)
{
   VAM_InitCond(pCO, pAO);
   if (pCO.IDToEval != "")
      VAM_HookupControl(VAM_GetById(pCO.IDToEval), pAO, pCO);
}  // VAM_InitOneFldCond

// for BaseTwoFieldCondition
function VAM_InitTwoFldCond(pCO, pAO)
{
   VAM_InitOneFldCond(pCO, pAO);
   if (pCO.IDToEval2 != "")
      VAM_HookupControl(VAM_GetById(pCO.IDToEval2), pAO, pCO);
}  // VAM_InitTwoFldCond
/*
// for BaseMultiFieldCondition. All IDs are in an array with the IDs property
function VAM_InitMultiFldCond(pCO, pAO)
{
   if (pCO.IDs != null)
   {
      for (var vI = 0; vI < pCO.IDs.length; vI++)
         VAM_HookupControl(VAM_GetById(pCO.IDs[vI]), pAO, pCO);
   }
   else
      pCO.IDs = new Array; // empty so loops can work with it
}  // VAM_InitMultiFldCond
*/
// For MultiCondition
function VAM_InitMultiCond(pCO, pAO)
{
   VAM_InitCond(pCO, pAO);
   // allow child conditions to initialize
   for (var vI = 0; vI < pCO.Conds.length; vI++)
   {
      var vChild = pCO.Conds[vI];
      if (vChild.InitFnc)
         vChild.InitFnc(vChild, pAO);
   }  // for
}  // VAM_InitMultiCond

// for RangeCondition
// Convert the MinTxt and MaxTxt elements from strings to their actual type
// (int, string, bool, object, date, etc.) in Min and Max.
function VAM_InitRangeCond(pCO, pAO)
{
   VAM_InitOneFldCond(pCO, pAO);
   if (pCO.MinTxt != null)
      pCO.Min = pCO.ConvStr(pCO, pCO.MinTxt);
   else
      pCO.Min = null;
   if (pCO.MaxTxt != null)
      pCO.Max = pCO.ConvStr(pCO, pCO.MaxTxt);
   else
      pCO.Max = null;
}  // VAM_InitRangeCond()

// for BaseCompareToValueCondition. Creates Val property from ValTxt.
function VAM_InitCompValCond(pCO, pAO)
{
   VAM_InitOneFldCond(pCO, pAO);
   pCO.Val = pCO.ConvStr(pCO, pCO.ValTxt);
}  // VAM_InitCompValCond


//---- Condition EvalFnc --------------------------------
// Method name: EvalFnc
// VAM Object type: Condition
// Purpose: Evaluates the condition. Usually called by VAM_EvalCondition during the VAM_DoAction.
//   Sometimes a calculation takes place whose value will be used in a validation error message.
//   In this case, assign the resulting value of the calculation to a property for the 
//   error message method to use. For example, pCO.Diff = vVal1 - vVal2.
// Parameters:
//   Condition object
// Returns:
//     The results of the Condition's evaluation:
//     1 = success, 0 = fail, -1 = cannot evaluate
//     Some actions need to use the -1 state to restore appearances (like validators to hide the error msg)

// For MultiCondition. Returns -1 when Conds is empty
function VAM_EvalMultiCond(pCO)
{
   if (pCO.Conds.length == 0)
      return -1;
      
   var vResult = true;
   var vCanEval = false;
   if (pCO.ANDOp)   // AND
   {
      // the first child that evaluates to false stops pCO loop
      for (var vI = 0; vResult && (vI < pCO.Conds.length); vI++)
      {
         var vR = VAM_EvalCondition(pCO.Conds[vI]);
         if (vR == 0)
            vResult = false;  // will stop
         if (vR != -1)
            vCanEval = true;
         // -1 and 1 both leave vResult unchanged
      }  // for
   }
   else  // OR
   {
      vResult = false;
      // the first child that evaluates to true stops pCO loop
      for (var vI = 0; !vResult && (vI < pCO.Conds.length); vI++)
      {
         var vR = VAM_EvalCondition(pCO.Conds[vI]);
         if (vR == 1)
            vResult = true;  // will stop
         if (vR != -1)
            vCanEval = true;
         // -1 and 0 both leave vResult unchanged
      }  // for
   }
   if (!vCanEval)
      return -1;
      
   if (pCO.NOTOp)
      vResult = !vResult;
   return vResult ? 1 : 0;
}  // VAM_EvalMultiCond()


// For RequiredTextCondition. Returns 1 when it doesn't match items in Unassnd.
function VAM_EvalReqTextCond(pCO)
{
   var vResult = true;
   var vVal = VAM_GetTextValue(pCO.IDToEval, pCO.Trim);
   if (!pCO.Unassnd || (pCO.Unassnd.length == 0))
      return (vVal != "") ? 1 : 0;

   if (pCO.CaseIns)
      vVal = vVal.toUpperCase();
      
   // compare to each item in Unassnd until there is match. 
   // Note: Unassnd are uppercase when CaseIns. They are already pre-trimmed.
   for (var vI = 0; vResult && (vI < pCO.Unassnd.length); vI++)
      if (pCO.Unassnd[vI] == vVal)
         vResult = false;
   return vResult ? 1 : 0;
}  // VAM_EvalReqTextCond

// for RequiredListCondition with <SELECT> statements
function VAM_EvalReqListCond(pCO)
{
   var vSelectFld = VAM_GetById(pCO.IDToEval);
   if (vSelectFld.selectedIndex == null)
      return -1;  // browser doesn't support it.
   if (pCO.UnassgnIdx == null) // init to default -1
      pCO.UnassgnIdx = -1;
   return (vSelectFld.selectedIndex != pCO.UnassgnIdx) ? 1 : 0;
}  // VAM_EvalReqListCond

// for RequiredListCondition with CheckBoxList or RadioButtonList
// These declare child elements with checked states using the IDToEval + '_' + #
// where # is 0 up.
// CheckBoxList always uses pCO.UnassgnVal = -1;
function VAM_EvalReqCheckCond(pCO)
{
   var vChkdPos = -1;
   var vDone = false;
   for (var vI = 0; !((vChkdPos != -1) || vDone); vI++)
   {
      var vChild = VAM_GetById(pCO.IDToEval + '_' + vI);
      if (vChild != null)
      {
         if (vChild.checked)
            vChkdPos = vI;
      }
      else  // passed all children
         vDone = true;
   }
   if (!pCO.UnassgnVal) // init to default -1
      pCO.UnassgnVal = -1;
   return (vChkdPos != pCO.UnassgnVal) ? 1 : 0;
}  // VAM_EvalReqCheckCond

// for RangeCondition
// Min and Max have been defined according to their datatypes, ready for comparision
// Method ConvStr handles converting text to the datatypes in prep for the VAM_Compare function
// Returns -1 when the text is blank or conversion fails.
function VAM_EvalRangeCond(pCO)
{
   var vVal = pCO.ConvVal(pCO, pCO.IDToEval);
   if (vVal == null)
      return -1;
/*
   var vVal = VAM_GetTextValue(pCO.IDToEval, pCO.Trim);
   if (vVal == "")
      return -1;
      
   vVal = pCO.ConvStr(pCO, vVal);
   if (vVal == null)
      return -1;
*/      
   return ( ((pCO.Min == null) || (pCO.Comparer(pCO, pCO.Min, vVal, 5)) ) &&
            ((pCO.Max == null) || (pCO.Comparer(pCO, pCO.Max, vVal, 3)) )
          ) ? 1 : 0;
}  // VAM_EvalRangeCond


// for BaseCompareTwoFieldsCondition
// Returns -1 when either field is blank or cannot be converted
function VAM_EvalComp2FldsCond(pCO)
{
/*
   var vVal1 = VAM_GetTextValue(pCO.IDToEval, pCO.Trim);
   var vVal2 = VAM_GetTextValue(pCO.IDToEval2, pCO.Trim);
   if ((vVal1 == "") || (vVal2 == ""))
      return -1;
   vVal1 = pCO.ConvStr(pCO, vVal1);
   vVal2 = pCO.ConvStr(pCO, vVal2);
*/   
   var vVal1 = pCO.ConvVal(pCO, pCO.IDToEval);
   var vVal2 = pCO.ConvVal(pCO, pCO.IDToEval2);
   if ((vVal1 == null) || (vVal2 == null))
      return -1;
   return pCO.Comparer(pCO, vVal1, vVal2, pCO.Op) ? 1 : 0;
}  // VAM_EvalComp2FldsCond

// for BaseCompareToValueCondition
// Returns -1 if the text is blank or cannot be converted
function VAM_EvalCompValCond(pCO)
{
/*
   var vVal = VAM_GetTextValue(pCO.IDToEval, pCO.Trim);
   if (vVal == "")
      return -1;
   vVal = pCO.ConvStr(pCO, vVal);
*/
   var vVal = pCO.ConvVal(pCO, pCO.IDToEval);
   if (vVal == null)
      return -1;
   return pCO.Comparer(pCO, vVal, pCO.Val, pCO.Op) ? 1 : 0;
}  // VAM_EvalCompValCond

// for BaseDataTypeCheckCondition. Returns -1 when the text is empty.
// Preserves the result in pCO.Val. It will be null if not converted.
function VAM_EvalDTCheckCond(pCO)
{
   pCO.Val = null;
/*   
   var vVal = VAM_GetTextValue(pCO.IDToEval, pCO.Trim);
   if (vVal == "")
      return -1;
   pCO.Val = pCO.ConvStr(pCO, vVal);
*/
   pCO.Val = pCO.ConvVal(pCO, pCO.IDToEval);
   if (!gVAMCanEval)
      return -1;
   return pCO.Val != null ? 1 : 0;
}  // VAM_EvalDTCheckCond

// for RegexCondition. Always -1 when trimmed field is blank and Expr is blank.
function VAM_EvalRegexCond(pCO)
{
   if (pCO.Expr == "")
      return -1;
   var vVal = VAM_GetTextValue(pCO.IDToEval, pCO.Trim);
   if (vVal == "")
      return -1;
      
   var vRx = new RegExp(pCO.Expr, pCO.Flags);
   return vRx.test(vVal) ? 1 : 0;
}  // VAM_EvalRegexCond

// for BaseTextLengthCondition
// Saves the length into pCO.Count for use by the error message dynamic token
// Saves the difference into pCO.Diff (always positive)
// Requires pCO.CntElFnc to handle the element count.
function VAM_EvalBTxtLenCond(pCO)
{
   var vVal = VAM_GetTextValue(pCO.IDToEval, pCO.Trim);
   pCO.Count = pCO.CntElFnc(pCO, vVal); // save for error message
   
   if (pCO.IDToEval2 != "")
   {
      vVal = VAM_GetTextValue(pCO.IDToEval2, pCO.Trim);
      pCO.Count = pCO.Count + pCO.CntElFnc(pCO, vVal); // save for error message
   }
   
   if (pCO.Min != 0)
      if (pCO.Min > pCO.Count)
      {
         pCO.Diff = pCO.Min - pCO.Count; // save for error message
         return 0;
      }
   if (pCO.Max != 0)
      if (pCO.Max < pCO.Count)
      {
         pCO.Diff = pCO.Count - pCO.Max;
         return 0;
      }
   return 1;
}  // VAM_EvalBTxtLenCond

// for CheckStateCondition
function VAM_EvalCheckStateCond(pCO)
{
   var vFld = VAM_GetById(pCO.IDToEval);
   if (vFld.checked != null)  // is it a checkbox or radiobutton
      return (vFld.checked == pCO.Chk) ? 1 : 0;
   else
      return -1;
}  // VAM_EvalCheckStateCond

// for SelectedIndexCondition on checkboxes and radiobuttons. Returns -1 when Idx is out of range.
// IDToEval is a grouping where IDToEval + "_" + <position> gets to the actual control.
function VAM_EvalSelIdxCheckCond(pCO)
{
   var vFld = VAM_GetById(pCO.IDToEval + "_" + pCO.Idx.toString());
   if (vFld == null)
      return -1;
   return (vFld.checked == pCO.Sel) ? 1 : 0;
}  // VAM_EvalSelIdxCheckCond

// for SelectedIndexCondition on List and DropList controls. Returns -1 when Idx is out of range
function VAM_EvalSelIdxListCond(pCO)
{
   var vFld = VAM_GetById(pCO.IDToEval);
   return ((vFld.selectedIndex == pCO.Idx) == pCO.Sel) ? 1 : 0;
}  // VAM_EvalSelIdxListCond


// Used by the VAMLib.MSCompat.CustomValidator as a wrapper around
// Microsoft's ClientValidationFunction def.
// That function is defined in the property: CVFnc
// It takes two parameters:
// Source - the span tag holding the error message. However, conditions don't
//   know anything about error Messages. We'll create an object with various 
//   attributes like "controltovalidate" for compatibility. 
// Arguments - an object with two attributes: Value and IsValid.
//   Value - The string retrieved from the control to validate or "" if no controltovalidate.
//   IsValid - boolean. Must set this to true or false before returning.
// There are two versions of this method.
// 1. VAM_MSCompatCustomCond is used when ControlToValidate is blank. It lacks pCO.IDToEval
// 2. VAM_MSCompatOneFldCustomCond is used when ControlToValidate is assigned.
function VAM_MSCompatCustomCond(pCO)
{
// source parameter.
   var vSrc = {clientvalidationfunction:pCO.CVFnc};
      
   // arguments parameter
   var vArgs = {Value: "", IsValid:true};
   eval(pCO.CVFnc + "(vSrc, vArgs);"); // run their function
   
   return vArgs.IsValid ? 1 : 0;

}  // VAM_MSCompatCustomCond

// See comments above VAM_MSCompatCustomCond
function VAM_MSCompatOneFldCustomCond(pCO)
{
   // source parameter.
   var vSrc = {controltovalidate:pCO.IDToEval, clientvalidationfunction:pCO.CVFnc};
      
   // arguments parameter
   var vVal = VAM_GetTextValue(pCO.IDToEval, pCO.Trim);
   if (vVal.length == 0)   // ignored when the field is blank. Follows Microsoft's methodology
      return 1;
   var vArgs = {Value: vVal, IsValid:true};
   eval(pCO.CVFnc + "(vSrc, vArgs);"); // run their function
   
   return vArgs.IsValid ? 1 : 0;
}  // VAM_MSCompatOneFldCustomCond

// Supports the OverrideClientSideEvaluation property
// When the property OCSMode is:
// - null or 1, return 1. Always indicates success
// - 0, on submit return 1 and set Mode=1 so later changes don't restore the message
//     on change, return 0.
// You will need to leave the EnableClientScript property set to true.
function VAM_EvalAltCS(pCO)
{
   if ((pCO.OCSMode == 0) && gVAMSubmitEvent)
   {
      pCO.OCSMode = 1;  // after this happens, onclick and onchange events will not restore the error message
      return 1;
   }
   else if (pCO.OCSMode == 0)
      return 0;
   else
      return 1;
}
//---- Action InitFnc method -----------------------------------------
// Method name: InitFnc
// VAM Object type: Action
// Purpose: Initializes the Action object. Called by VAM_InitActions as the page is loaded
//   - Initialize missing properties. The server side omits some properties at their default values
//     These are assigned by the InitFnc to their default. In addition, it creates any properties
//     that are the exclusive domain of the client side like a reference to an element on the page.
//   - Due to subclassing, it may need to call an ancestor class's InitFnc. The base class
//     for actions does not have its own InitFunc.
//   - It does NOT attempt to initialize the condition. The condition has already been
//     initialized and in fact could have its eval function called if you need to show something
//     right away.
// Parameters:
//   Action object - while 'this' can be used to get the properties, this parm
//     allows subclassing. So don't use 'this', use the action object to properties.
// Returns:
//     Nothing.

// Default init method for validators
// It gets the <span> tag element by the ID in pAO.ErrFldID
// Assigns the resulting field to pAO.ErrFld
// Gets the original Class and font color for blink mode.
function VAM_InitValAction(pAO)
{
// fill in properties that are not assigned because they are using their default state
   if (pAO.SumMsg == null)
      pAO.SumMsg = "";
   if (pAO.SelErrMsg == null)
      pAO.SelErrMsg = VAM_SelErrMsg;
   if (pAO.SelSumMsg == null)
      pAO.SelSumMsg = VAM_SelSumMsg;
   if (pAO.UseFocus == null)
      pAO.UseFocus = false;
   if (pAO.ShowAlert == null)
      pAO.ShowAlert = false;
   if (pAO.CtlErrCss == null)
      pAO.CtlErrCss = "";
   if (pAO.Group == null)
      pAO.Group = "";
   if (pAO.EvtToVal == null)
      pAO.EvtToVal = 0;

// these properties are from BaseErrorFormatter. Assign defaults
   if (pAO.Blnk == null)
      pAO.Blnk = false;
   if (pAO.BlnkCss == null)
      pAO.BlnkCss = '';
      
   if (pAO.IsValid == null)
      pAO.IsValid = true;
   pAO.BlinkCnt = 0; // when positive, # of blinks left. When -1, continuous blink. 0 = no blinking
   if (pAO.ErrFldID != null)
   {
      pAO.ErrFld = VAM_GetById(pAO.ErrFldID);
      pAO.OrigCss = pAO.ErrFld.className; // if className is not defined, OrigCss is null
      pAO.OrigColor = (pAO.ErrFld.style != null) ? pAO.ErrFld.style.color : null;
      pAO.ImgErrFld = VAM_GetById(pAO.ErrFldID + "_Img"); // often null. Only assigned when there is an image
      
      // the error field is visible, let the FrmttrFnc assign client-side settings
      if ((pAO.ErrFld.style != null) && (pAO.ErrFld.style.visibility != "hidden") && pAO.FmttrFnc)
      {
         if (pAO.TokenRepl) // if defined, we need to run the condition to setup tokens
            VAM_EvalCondition(pAO.Cond);  // ignore the result. Internally, properties were updated
         pAO.FmttrFnc(pAO, true);
         
//!dc v1.0.1 follow the onsubmit blinking rule on postback         
         gVAMSubmitEvent = true;
         VAM_StartBlink(pAO); 
         gVAMSubmitEvent = false;
//!dc-end         
      }
     
   }
   // NOTE: The caller, VAM_InitActions, has already setup pAO.Ctl with references to all controls  
}  // VAM_InitValAction


//---- Action ActnFnc method -----------------------------------------
// Method name: ActnFnc
// VAM Object type: Action
// Purpose: Runs the action. Usually called by VAM_DoAction function.
// Parameters:
//   Action object - while 'this' can be used to get the properties, this parm
//     allows subclassing. So don't use 'this', use the action object to properties.
//   Condition Result (int) - the results of the Condition's evaluation.
//     1 = success, 0 = fail, -1 = cannot evaluate
//     Some actions need to use the -1 state to restore appearances (like validators to hide the error msg)
// Returns:
//     Nothing.

// Default Action method for validators
// Delegates some work to the FmttrFnc.
// It handles ShowAlert, UseFocus, and CtlErrCss here.
// Sets pAO.IsValid = pSuccess
function VAM_DoValidate(pAO, pEvalRes)
{
   pAO.IsValid = pEvalRes != 0;  // 1 and -1 are valid
   
   if (pAO.FmttrFnc && (pAO.Dspl != 0))
   {
      if (pEvalRes != -1)
         pAO.FmttrFnc(pAO, !pAO.IsValid);  // apply the error message
      // show or hide the span tag
      if (pAO.Dspl == 2)  // dynamic
         pAO.ErrFld.style.display = pAO.IsValid ? "none" : "inline";
      pAO.ErrFld.style.visibility = pAO.IsValid ? "hidden" : "inherit";
      if (!pAO.IsValid)
         VAM_StartBlink(pAO);
      else
         VAM_StopBlink(pAO);
   }
}  // VAM_DoValidate

// ---- Hookup subcontrol methods -------------------------------
// Method name: HUCtrlFnc
// VAM Object type: Condition
// Purpose: Controls must have their onchange or onclick events hooked up
//    with VAM_HookupControl. Most control IDs reflect the actual
//    control to pass to VAM_HookupControl.
//    Some, like CheckBoxLists and RadioButtonLists, contain subcontrols
//    which are hookedup instead of of the original ID.
//    These methods are defined for each control whose subcontrols must be hooked-up.
//    They are assigned to the Cond object's HUCtrlFnc method. 
//    VAM_HookupControl will be passed the Cond object. If the Cond contains an HUCtrlFnc, it will be called.
//    Internally, this method should call VAM_HookupControl again, with the HUCtrlFnc property = null.
// Parameters:
//    Condition object - contains ID(s) to translate into subcontrols.
//    Action object - to be passed into VAM_HookupControl
//    Main control ID (string) - the ID assigned by the user to the control. This
//       ID must be converted into the subcontrol's ID, usually by adding a fixed string
//          like ID + "_Button"
// Returns:
//     Nothing.

// For CheckBoxList and RadioButtonList. Subcontrols use ID + "_" + ##
// where ## is a number from 0 up.
function VAM_HUCheckRadioList(pCO, pAO, pFldID)
{
   var vDone = false;
   for (var vI = 0; !vDone; vI++)
   {
      vFld = VAM_GetById(pFldID + "_" + vI);
      if (vFld != null)
         VAM_HookupControl(vFld, pAO, null);
      else
         vDone = true;
   }
}  // VAM_HUCheckRadioList

//---- VAMTypeConverter.ConvertValue -----------------------------------------------
// Method name: ConvVal
// VAM Object type: Condition
// Purpose: VAMTypeConverters use this method to extract a value from a particular
//    control and return it in desired datatype (string, int, double, Date, object, etc.)
//    By default, ConvVal uses VAM_ConvertStrFld which gets a string from any field
//    that supports strings and uses the ConvStr function to get it to the desired datatype.
//    Replace it to handle specialized fields like those that have several fields representing
//    one value.
//    There are three possible results: converted successfully, failed, cannot evaluate.
//    The first two results are represented by the function result.
//    The third requires the global gVAMCanEval. Set it to true if it can evaluate
//    and false if it cannot evaluate.
// Parameters:
//   Condition object
//   IDToEval - the ID of the field being evaluated. Since Condition has multiple IDs, this specifies
//       the desired one.
// Returns:
//     If it can convert, return the desired datatype (can be string, int, double, Date, object, etc).
//     If it cannot convert, return null.
//     In both cases, return gVAMCanEval = true
//     If it cannot eval, return gVAMCanEval = false

var gVAMCanEval = true;

// default. Handles any control that can be represented by VAM_GetTextValue
// Sets gVAMCanEval = false when the text is blank.
function VAM_ConvertStrFld(pCO, pID)
{
   var vVal = VAM_GetTextValue(pID, pCO.Trim);
   if (vVal == "")
   {
      gVAMCanEval = false;
      return null;
   }
   else
   {
      gVAMCanEval = true;
      return pCO.ConvStr(pCO, vVal);
   }
}  // VAM_ConvertStrFld

//---- VAMTypeConverter.CSConvertFromFunction Function -----------------------------------------------
// Method name: ConvStr
// VAM Object type: Condition
// Purpose: VAMTypeConverters use this method to convert a string into another datatype.
//   VAMTypeConverter.CSConvertFromFunction holds the name of the function.
// Parameters:
//   Condition object
//   String to convert
// Returns:
//     If it can convert, return the desired datatype (can be string, int, double, Date, object, etc).
//     If it cannot convert, return null.

// Convert to string
function VAM_StrConv(pCO, pValue)
{
   return pValue;
}  // VAM_StrConv

// Convert to uppercase string. Case insensitive matching
function VAM_CIStrConv(pCO, pValue)
{
   return pValue.toUpperCase();
}  // VAM_CIStrConv

// Convert to integer. Requires the caller to trim if that's desired.
function VAM_IntConv(pCO, pValue)
{
   var vT = '\\{0}';
   vT = vT.replace('{0}', pCO.grpsep);  // fixes a bug in the HTML&JavaScript Compressor v4.1.1 by FreeSoft
//   pValue = pValue.replace(new RegExp(vT, "gim"), '');  // remove thousands separators which are valid characters but not supported by VAM_ParseInt
   pValue = VAM_RERpl(pValue, vT, ''); // remove thousands separators which are valid characters but not supported by VAM_ParseInt
   var vVal = VAM_ParseInt(pValue);
   vVal = isNaN(vVal) ? null : vVal;
   // filter out negatives if not permitted
   if (!pCO.Neg && (vVal != null) && (vVal < 0))
      vVal = null;
   return vVal;
}  // VAM_IntConv

// Convert to decimal.  Requires the caller to trim if that's desired.
function VAM_DecConv(pCO, pValue)
{
   var vT = '\\{0}';
   vT = vT.replace('{0}', pCO.grpsep);  // fixes a bug in the HTML&JavaScript Compressor v4.1.1 by FreeSoft
//   pValue = pValue.replace(new RegExp(vT, "gim"), '');  // remove thousands separators which are valid characters but not supported by parseFloat
   pValue = VAM_RERpl(pValue, vT, ''); // remove thousands separators which are valid characters but not supported by parseFloat

// parseFloat returns a valid string when the trailing characters are not digits. Use RegEx to filter illegal chars
// Acknowledgement: Microsoft's WebUIValidation.js, modified to allow one more case, digits followed by a period with no followup digits
   var vPtrn = "^\\s*([-\\+])?(\\d+)?(\\{0})?(\\d+)?\\s*$";
   vPtrn = vPtrn.replace("{0}", pCO.decsep); // fixes a bug in the HTML&JavaScript Compressor v4.1.1 by FreeSoft
   exp = new RegExp(vPtrn);
   m = pValue.match(exp);
   if (m == null)
      return null;
//!bf v1.0.1 Opera 7 doesn't convert null values in m[1]/m[4] to any empty string
   var vPrepVal = (m[1] != null ? m[1] : "") 
                + (m[2].length>0 ? m[2] : "0")
                + "."
                + (m[4] != null ? m[4] : "");
/*
   var vPrepVal = m[1] + (m[2].length>0 ? m[2] : "0") + "." + m[4];
//!bf-end */   

   var vVal = parseFloat(vPrepVal);
   vVal = isNaN(vVal) ? null : vVal;
   // filter out negatives if not permitted
   if (!pCO.Neg && (vVal != null) && (vVal < 0.0))
      vVal = null;
   return vVal;
}  // VAM_DecConv

// Convert to date represented as an integer in milliseconds. Returns millisecs or null.
// Milliseconds is from the Date object's valueOf() function.
// Properties required:
// Pattern (string) - order of the date. Single char of MDY, separated by | and uppercase
// DateSep (string) - single character date separator
// CentBrk (int) - 2 digit years add 1900 at and above pCO value; 2000 below it.
// If the text starts with a 4 digit year but the pattern does not, switch to YMD format
// and support either DateSep or '-' as the separator.
function VAM_DateConv(pCO, pValue)
{
   pValue = VAM_Trim(pValue);
   if (pValue.length == 0)
      return null;
      
   var vD = -1;
   var vM = -1;
   var vY = -1;
   
   var vDateSep = pCO.DateSep;
   // need either DateSep or '-'.
   if (pValue.indexOf(vDateSep) == -1)
      if (pValue.indexOf('-') != -1)
         vDateSep = '-';
      else
         return null;   // no separators
         
   var vDateParts = pValue.split(vDateSep);
   if (vDateParts.length < 3)
      return null;   // not enough separators
   var vDateCount = 0;
   // if the first element has a 4 digit year and the order doesn't start with the year
   // switch to ISO 1860 YMD pattern
   var vPattern = pCO.Pattern.split('|');
   if (vPattern[0] != "Y")
   {
      var vTemp = VAM_ParseInt(vDateParts[0]);
      if (vTemp >= 1000)
         vPattern = new Array("Y", "M", "D");
   }
   
   for (var vI = 0; vI < vDateParts.length; vI++)
   {
      var vPV = VAM_ParseInt(vDateParts[vI]);
      if (isNaN(vPV))   // illegal value
         return null;
      switch (vPattern[vI].charAt(0))
      {
         case 'D':
            if ((vPV <= 0) || (vPV > 31))   // illegal value
               return null;
            vD = vPV;
            break;

         case 'M':
            if ((vPV > 12) && (vPV <= 0))
               return null;
            vM = vPV;
            break;

         case 'Y':
            if (vPV < 1)
               return null;
            if (vPV < 100)
            {
               if ((pCO.CentBrk == 0) || (vPV <= pCO.CentBrk))
                  vPV = vPV + 2000;
               else
                  vPV = vPV + 1900;
            }
            vY = vPV;
            break;

      }  // switch
   } // for

   vM -= 1;
   var date = new Date(vY, vM, vD);
   if (date == null) // couldn't convert it due to values out of range (like Feb 31)
      return null;
   else  // confirm all the parts are the same date as their inputs
      return (vY == date.getFullYear() && vM == date.getMonth() && vD == date.getDate()) ? date.valueOf() : null;
}  // VAM_DateConv

// for CurrencyTypeConverterDef.
// Properties are:
// * decdigits (int) - Max number of digits after decimal separator. May be 99 when not checking limits.
// * decsep (char) - Currency Decimal Separator.
// * grpsep (char) - Currency Group Separator. (AKA Thousands separator)
// * symbol (string) - Currency Symbol. When "", no currency symbol is allowed
// pCO function is meant to emulate Double.Parse because most users will use Convert.ToDouble
// (which calls Double.Parse) or Double.Parse directly, especially to handle CurrencySymbols.
// The format can be:
// LEAD TEXT: [sp][$][+|-|(][[sp]$[sp]]
// NUMBER ITSELF: [any number of 0-9 and grpsep][decsep][0-9, limited to decdigits if non-zero]
// END TEXT: [sp][$[sp]][)][sp]
// where [sp] is spaces. 
// Rules:
// * When '(' appears, so must ')'
// * Groupseparators are allowed in any position including a series: 1,,,000 is OK. 1,2,3,0.00 is OK
// * Groupseparators cannot appear after the decimal separator
// * Number of digits for decimal part is not limited in Double.Parse. Here UseDecimalDigits determines
//    if that rule works.
// * Only one currency symbol is allowed. For example, $1.00$ is illegal
// * Currency symbol can be anywhere except within the digits. $(1.00) ($1.00) (1.00$) (1.00)$
// * Trim lead and trailing spaces
// * Cannot have spaces between +|-|( and first digit unless separated by currency symbol $ 1.00 is OK - 1.00 is not.
// * No more than one decimal separator allowed
function VAM_CurrencyConv(pCO, pValue)
{
   // handle currency symbols. If more than 1, stop. Otherwise, remove it with surrounding spaces.
   if (pCO.symbol != "")
   {
      var vSym = (pCO.symbol == '$') ? "\\$" : pCO.symbol;
      var vRE = new RegExp("\\s*(" + vSym + ")\\s*", "gi");   // \\s* is zero or more spaces
      var m = pValue.match(vRE);
      if (m != null) // found something
      {
         if (m.length > 1) 
            return null;
         if ((m.length == 1) && (m[0] != ""))   // has one
            pValue = pValue.replace(vRE, "");   // remove
      }
   }
   pValue = VAM_Trim(pValue); // at pCO point, we should have left [+|-|(][decimal number][[sp])]. Parsing will prove it
   var vTxtLen = pValue.length;
   if (vTxtLen == 0)
      return null;
   
   // parse left to right with vPos containing the char we are evaluating
   // Left of decimal: One of these chars or nothing: +, -, (
   var vPos = 0;
   var vChar = pValue.charAt(0);
   var vLParen = vChar == "(";
   var vNeg = vLParen || (vChar == "-"); // when true we have a negative symbol
   if ((vChar == "+") || vNeg)
   {
      vPos++;
      vChar = pValue.charAt(vPos);
   }
   // whole number part. Build the string vDecStr adding digits. If anything other 
   // than a digit or grpsep we're done.
   var vDecStr = "";
   var vDone = false;
   do
   {
      if ((vChar >= "0") && (vChar <= "9"))
         vDecStr += vChar;
      else if (vChar != pCO.grpsep)   // eat group seps
         vDone = true;
      if (!vDone)
         if (++vPos < vTxtLen)
            vChar = pValue.charAt(vPos)
         else
            vDone = true;

   } while (!vDone);
   // if its a decimal char, add to vDecStr and process the decimal part
   if (vChar == pCO.decsep)
   {
      vDecStr += ".";   // use the same character supported by parseFloat
      var vDigCnt = 0;  // count digits up to pCO.decdigits
      vDone = false;
      if (++vPos < vTxtLen)
      {
         vChar = pValue.charAt(vPos)
         do
         {
            if ((vChar >= "0") && (vChar <= "9"))
            {
               vDecStr += vChar;
               vDigCnt++;
            }
            else
               vDone = true;
            if (!vDone)
               if (++vPos < vTxtLen)
                  vChar = pValue.charAt(vPos)
               else
                  vDone = true;

         } while (!vDone);
      }
      if (vDigCnt > pCO.decdigits)
         return null;
   }
   // trailing chars. Only space and trailing )
   if (vChar == " ")
   {
      vPos++;
      if (vPos < vTxtLen)
         vChar = pValue.charAt(vPos);
   }
   var vRParen = vChar == ")";
   if (vRParen)
      vPos++;
   // error check. Do we have anything after vPos? Do we have both parens?
   if ((vPos < vTxtLen) || (vLParen != vRParen))
      return null;
      
   var vVal = parseFloat(vDecStr);
   if (isNaN(vVal))
      return null;
   if (vNeg)
      if (!pCO.Neg)   // negs not permitted
         vVal = null;
      else
         vVal = -vVal;
   return vVal;
}  // VAM_CurrencyConv

// ---- Data comparison methods -------------------------
// Method name: Comparer
// VAM Object type: Condition
// Purpose: Comparer allows you to customize the comparision rules of the standard
//    conditions that compare two datatypes. When you use a string, integer, double, or Date,
//    JavaScript can handle comparison with simply >, =, < operators. The VAM_Comparer
//    method handles those cases. When you have an object, you may need to compare several
//    properties at once. Supply your own Comparer.
// Parameters:
//    Condition object
//    LeftValue - the data for the left side of the comparision expression. Its datatype
//       depends on the results of the ConvStr method.
//    RightValue - the data for the right side of the comparision expression.
//    Operator - 0 = equals, 1 = not equals, 2= greater than, 3= greater or equal
//   4= less than, 5= less or equal
// Returns:
//     boolean. If the expression evaluates to true, it returns true. Otherwise it returns false.

// The default comparison method. It handles any two values of the same type using
// standard boolean operators. Other compare methods may call pCO as a function after
// preparing leftVal and rightVal as javascript primatives.
function VAM_Comparer(pCO, pLeftVal, pRightVal, pOp)
{
   switch (pOp)
   {
      case 0:  // equals
         return pLeftVal == pRightVal;
      case 1:  // not equals
         return pLeftVal != pRightVal;
      case 2:  // greater than
         return pLeftVal > pRightVal;
      case 3:  // greater or equals
         return pLeftVal >= pRightVal;
      case 4:  // less than
         return pLeftVal < pRightVal;
      case 5:  // less or equals
         return pLeftVal <= pRightVal;
   }
   return true;   // error if it gets here.
}  // VAM_Comparer

// ---- Validator Error Formatter methods -----------------
// Method name: FmttrFnc
// VAM Object type: Action
// Purpose: Action objects format the error message with the appropriate
//    HTML and JavaScript using these methods.
//    Generally these modify the field specified by pAO.ErrFldID or its children.
//    Images are identified by pAO.ImgErrFld.
//    If you want to change the text of a field, call VAM_SetInnerHTML
// Parameters:
//    Action object
//    Show (boolean) - when true, the error message is shown.
//       When false, the error message is hidden. You usually don't need to hide
//       anything when Show=false because pErrFldID is hidden by the caller.
//       So use this to remove anything external to pErrFldID.
// Returns:
//     Nothing.

// for TextErrorFormatter.
function VAM_TextFmttr(pAO, pShow)
{
   if (pShow)
   {
   // message goes into pAO.ErrFldID + "_Txt"
      if (pAO.TxtErrFld == null)
         pAO.TxtErrFld = VAM_GetById(pAO.ErrFldID + "_Txt");

      VAM_SetInnerHTML(pAO.TxtErrFld, VAM_GetErrMsg(pAO));
   }
}  // VAM_TextFmttr

// for ToolTipImageErrorFormatter
function VAM_TTFmttr(pAO, pShow)
{
   if (pShow && pAO.ImgErrFld)
      pAO.ImgErrFld.title = VAM_GetErrMsg(pAO);
}  // VAM_TTFmttr

// for AlertImageErrorFormatter
function VAM_AlertFmttr(pAO, pShow)
{
// pAO happens only the first time. Then it automatically calls VAM_GetErrMsg.
   if (pShow && pAO.ImgErrFld && (pAO.ImgErrFld.onclick == null))
      pAO.ImgErrFld.onclick = new Function("alert(VAM_GetErrMsg(gVAMActions[" + pAO.id + "]))");
  // NOTE: We don't use VAM_StripTags here because AlertImageErrorFormatters never support HTML in the error message
}  // VAM_AlertFmttr

// ---- Blink methods -------------------------------------
// Method name: BlinkFnc
// VAM Object type: Action
// Purpose: Handles the blinking effect. It is called periodically to let your method
//    change the appearance of the error formatter (pAO.ErrFldID/pAO.ErrFld)
// Parameters:
//    Action object
//    Blink (boolean) - When true, set the error message to its blinked state.
//       When false, set the error message to its normal state.
// Returns:
//     Nothing.


// Default Blink function. Toggles between the original and BlinkCssClass styles.
// If BlnkCss is '', toggles between the original and Transparent font color.
// If the pAO.ImgErrFld is assigned, Change its visibility. pAO maintains its size 
// on the page so the user can click while its hidden
function VAM_Blink(pAO, pBlink)
{
   if ((pAO.BlnkCss != "") && (pAO.ErrFld.className != null))
      pAO.ErrFld.className = pBlink ? pAO.BlnkCss : pAO.OrigCss;
   else if (pAO.ErrFld.style != null) // font color
      pAO.ErrFld.style.color = pBlink ? '' : pAO.OrigColor; // transparent
  // toggle image. 
  if ((pAO.ImgErrFld != null) && (pAO.ImgErrFld.style != null))
      pAO.ImgErrFld.style.visibility = pBlink ? "hidden" : "inherit";
}  // VAM_Blink

//---- Select Error Message methods --------------------------
// Method name: SelErrMsg
// VAM Object type: Action
// Purpose: Selects the initial text for the ErrorMessage shown in the validator.
//    The default method, VAM_SelErrMsg returns pAO.ErrMsg.
//    Replace it if you have several error message sources.
//    Use pAO and pAO.Cond to determine which error message is appropriate.
//    No token replacement (pAO.TokenRepl) should be done here. The caller handles that.
// Parameters:
//    Action object
// Returns:
//     String containing the error message.

// Default SelErrMsg method. Returns pAO.ErrMsg with no further processing
function VAM_SelErrMsg(pAO)
{
   return pAO.ErrMsg;
}  // VAM_SelErrMsg

//---- Select Summary Error Message methods --------------------------
// Method name: SelSumMsg
// VAM Object type: Action
// Purpose: Selects the initial text for the SummaryErrorMessage shown in the ValidatorSummary.
//    The default method, VAM_SelSumMsg returns pAO.SumMsg.
//    Replace it if you have several error message sources.
//    Use pAO and pAO.Cond to determine which error message is appropriate.
//    No token replacement (pAO.TokenRepl) should be done here. The caller handles that.
// Parameters:
//    Action object
// Returns:
//     String containing the error message.


// Default SelSumMsg method. Returns pAO.SumMsg with no further processing
function VAM_SelSumMsg(pAO)
{
   return pAO.SumMsg;
}  // VAM_SelSumMsg

//---- Token replacement methods --------------------------
// Method name: TokenRepl
// VAM Object type: Action
// Purpose: Individual Action classes define a list of tokens that get replaced.
//    Use this method to handle replacement. 
//    Generally these replacements look like this:
//    pText = VAM_RERpl(pText, "{TOKEN}", [Newvalue]);
//    Often the condition evaluation method determines the value and assigns it to
//    a property on the Condition object just for this method to use.
//    If your action is subclassed from other that supports tokens,
//    be sure to call the ancestor's replacement method to handle its tokens.
// Parameters:
//    Action object
//    Text (string) - the string to convert. It may be multiline.
// Returns:
//     String containing the updated error message.

// for BaseOneFldValidationAction. Replaces {TEXTVALUE} with VAM_GetTextValue(Cond.IDToEval)
function VAM_OneFldReplToken(pAO, pText)
{
   if (pAO.Cond.IDToEval != "")
//      return pText.replace(new RegExp("{TEXTVALUE}", "gmi"), VAM_GetTextValue(pAO.Cond.IDToEval, pAO.Cond.Trim));
      return VAM_RERpl(pText, "{TEXTVALUE}", VAM_GetTextValue(pAO.Cond.IDToEval, pAO.Cond.Trim))
   else
      return pText;
}  // VAM_OneFldReplToken

// for BaseTwoFldValidationAction. Replaces {TEXTVALUE2} with VAM_GetTextValue(Cond.IDToEval2)
function VAM_TwoFldReplToken(pAO, pText)
{
   pText = VAM_OneFldReplToken(pAO, pText);
   if (pAO.Cond.IDToEval2 != "")
//      return pText.replace(new RegExp("{TEXTVALUE2}", "gmi"), VAM_GetTextValue(pAO.Cond.IDToEval2, pAO.Cond.Trim));
      return VAM_RERpl(pText, "{TEXTVALUE2}", VAM_GetTextValue(pAO.Cond.IDToEval2, pAO.Cond.Trim))
   else
      return pText;
}  // VAM_TwoFldReplToken

// for TextLengthValidatorAction. Replaces "{COUNT}" with pAO.Cond.Count. {EXCEEDS} with pAO.Cond.Diff.
function VAM_TxtLenReplToken(pAO, pText)
{
   pText = VAM_OneFldReplToken(pAO, pText);
   pText = VAM_SPReplToken(pText, pAO.Cond.Count, "COUNT");
   pText = VAM_SPReplToken(pText, pAO.Cond.Diff, "EXCEEDS");
//   pText = pText.replace(new RegExp("{COUNT}", "gmi"), pAO.Cond.Count);
   pText = VAM_RERpl(pText, "{COUNT}", pAO.Cond.Count);   
//   return pText.replace(new RegExp("{EXCEEDS}", "gmi"), pAO.Cond.Diff);
   return VAM_RERpl(pText, "{EXCEEDS}", pAO.Cond.Diff); 
}  // VAM_TxtLenReplToken

// A support function for the Token Replacement methods
// Handles the singular/plural tokens. They have a format of {[tokenname]:singular:plural}
// If this pattern is found, use the text of 'singular' when pCnt = 1. Otherwise use 'plural'.
// pText is the original string.
// Return the updated string or the original if there wasn't a match to the token.
function VAM_SPReplToken(pText, pCnt, pTName)
{
   var vRE = new RegExp("\\{" + pTName + ":([^:}]*):([^:}]*)\\}"); // [^:}] = everything except a colon and }

   var vMatch = vRE.exec(pText);
   if ((vMatch != null) && (vMatch.length == 3)) 
   {
      var vOrig = new RegExp("{" + pTName + ":" + vMatch[1] + ":" + vMatch[2] + "}", "gi");  // do not use "m" flag - older browsers fail
      if (pCnt == 1)
         return pText.replace(vOrig, vMatch[1]);
      else
         return pText.replace(vOrig, vMatch[2]);
   }
   else  // not found
      return pText;
}  // VAM_SPReplToken

//---- Can Run Action methods --------------------------
// Method name: CanRun
// VAM Object type: Action
// Purpose: Determines if the ActnFnc can be run. Provides a way to shut off the ActnFnc.
// Parameters:
//   Action object
// Returns:
//     true - it can run
//     false - it cannot run.

// for BaseAction
function VAM_CanRunActn(pAO)
{
   if (pAO.Enabler)
   {
      var vR = VAM_EvalCondition(pAO.Enabler);
      return vR != 0;   // 1 and -1 return true
   }
   else
      return true;
}  // VAM_CanRunActn

// for BaseValidatorAction
function VAM_CanRunVal(pAO)
{
   if ((pAO.EvtToVal == 1) && !gVAMSubmitEvent)
      return false;
   if ((pAO.EvtToVal == 2) && gVAMSubmitEvent)
      return false;
   return VAM_CanRunActn(pAO);
}  // VAM_CanRunVal


// As part of the VAM_CanRunVal process, when EvtToVal is 1, the action must be
// in an onsubmit event. The VAM_TryToSubmit() function tells VAM_CanRunVal
// that its in onsubmit by setting gVAMSubmitEvent to true
var gVAMSubmitEvent = false;

// --- Validation Summary GetInnerHTML Methods --------------------
// Method name: GetInnerHTML
// VAM Object type: Validator Summary
// Purpose: Is passed the already formatted list of error messages (including "").
//   It must return the complete innerHTML for the validation summary <DIV> tag.
//   For example, the ValidationSummary class adds a header and footer.
// Parameters:
//   ValSum object
//   List - string. Fully formatted list of error Messages.
// Returns:
//     A string containing the innerHTML for the <DIV> tag.

// for ValidatorSummary. (NOTE: BaseValidatorSummary does not define this function.)
// Inserts pList between the Header and Footer
function VAM_ValSumInnerHTML(pVSO, pList)
{
   var vIH = "";
   if (pVSO.Hdr != "")
      vIH = pVSO.Hdr + pVSO.HdrSep;
   if (pVSO.PreListFnc != null)  // assumes PostListFnc is also assigned
      vIH += pVSO.PreListFnc(pVSO) + pList + pVSO.PostListFnc(pVSO);
   if (pVSO.Ftr != "")
      vIH += pVSO.Ftr;
   return vIH;
}  // VAM_ValSumInnerHTML

// --- Validation Summary Pre List Methods --------------------
// Method name: PreListFnc
// VAM Object type: Validator Summary
// Purpose: Gets the string that appears immediately before the list of errors.
//    It doesn't include the header. Its just the initial part of the body.
// Parameters:
//   ValSum object
// Returns:
//     A string containing whatever must precede the list. Often this is a begin tag.

// the default function. Generates a <p> tag with optional class and title attributes
function VAM_ValSumPreDefault(pVSO)
{
   return "<p" + VAM_ValSumPreAttributes(pVSO) + ">";
}  // VAM_ValSumPreDefault

// used for displaymode = bulletlist. Generates <ol> or <ul> tags with the appropriate type and class and title attributes
function VAM_ValSumPreBullet(pVSO)
{
   if (pVSO.BulletTL.length == 1)   // its an OL
      return "<ol type='" + pVSO.BulletTL + "' " + VAM_ValSumPreAttributes(pVSO) + ">";
   else
      return "<ul type='" + pVSO.BulletTL + "' " + VAM_ValSumPreAttributes(pVSO) + ">";
}  // VAM_ValSumPreBullet

// Used for singleparagraph. Generates a <span> tag with optional class and title attributes
function VAM_ValSumPreSglPara(pVSO)
{
   return "<span" + VAM_ValSumPreAttributes(pVSO) + ">";
}  // VAM_ValSumPreSglPara

// A support function that returns a string containing the class and title attributes
// This string should be plugged into the beginning tag.
function VAM_ValSumPreAttributes(pVSO)
{
   var vAttr = "";
   if (pVSO.ErrMsgCss != "")
      vAttr = vAttr + " class='" + pVSO.ErrMsgCss + "'";
   if (pVSO.LinkTT != "")
      vAttr = vAttr + " title='" + pVSO.LinkTT + "'";
   return vAttr;
}  // VAM_ValSumPreAttributes

// --- Validation Summary Pre List Methods --------------------
// Method name: PostListFnc
// VAM Object type: Validator Summary
// Purpose: Gets the string that appears immediately after the list of errors.
//    It doesn't include the footer. Its just the end part of the body.
// Parameters:
//   ValSum object
// Returns:
//     A string containing whatever must follow the list. Often this is an end tag.


// the default function. Generates a </p> tag
function VAM_ValSumPostDefault(pVSO)
{
   return "</p>";
}  // VAM_ValSumPreDefault

// used for displaymode = bulletlist. Generates </ol> or </ul> tags
function VAM_ValSumPostBullet(pVSO)
{
   if (pVSO.BulletTL.length == 1)   // its an OL
      return "</ol>";
   else
      return "</ul>";
}  // VAM_ValSumPreBullet

// used for displaymode = singlepara. Generates </span> tag
function VAM_ValSumPostSglPara(pVSO)
{
   return "</span>";
}  // VAM_ValSumPostSglPara

// --- Validation Summary Format List Item Methods --------------------
// Method name: FmtListFnc
// VAM Object type: Validator Summary
// Purpose: The Validation Summary passes a string with a single error message.
//    This method should format the error message and return it.
// Parameters:
//    ValSum object
//    ErrorMessage (string) - text to format.
//    RowNum (integer) - The row number of the error message, starting at 0.
// Returns:
//     A string containing the formatted error message.

// Used when DisplayMode == List. pVSO.ListLdTxt + pMsg + <br>
function VAM_ValSumFmtItemList(pVSO, pMsg, pRowNum)
{
   return pVSO.ListLdTxt + pMsg + "<br>";
}  // VAM_ValSumFmtItemList

// Used when DisplayMode == BulletList. "<li>" + pMsg + "</li>"
function VAM_ValSumFmtBullet(pVSO, pMsg, pRowNum)
{
   return "<li>" + pMsg + "</li>";
}  // VAM_ValSumFmtBullet

// Used when DisplayMode == SingleParagraph. Row 0: pMsg; Other rows: pVSO.SglParSep + pMsg
function VAM_ValSumFmtSglPara(pVSO, pMsg, pRowNum)
{
   if (pRowNum == 0)
      return pMsg;
   else
      return pVSO.SglParSep + pMsg;
}  // VAM_ValSumFmtSglPara

//---- BaseTextLengthCondition Count Element Function -----------------------------------------------
// Method name: CntElFnc
// VAM Object type: Condition
// Purpose: Used by BaseTextLengthConditions from VAM_EvalTextLenCond
//   Called to evaluate the number of elements within the text passed.
//   Function defines the element size, such as character or word
// Parameters:
//   Condition object
//   Text (trimming already applied if user requested it)
// Returns:
//   Integer with the number of elements found. 

// for TextLengthCondition. Number of characters
function VAM_CntChars(pCO, pText)
{
   return pText.length;
}  // VAM_CntChars

// for WordCountCondition. Number of words
function VAM_CntWords(pCO, pText)
{
   if (pText.length == 0)
      return 0;
      
   // use a regex to find the words. The \w and \W tokens handle most of what
   // we want. \w = any of these chars: A-Z,a-z,0-9,_
   // We also want the single quote (possessive nouns and contractions) in there
   pText = pText.replace(new RegExp("'", "gi"), "");  // remove single quotes

   var vRegEx = new RegExp("\\b(\\w+?)\\b", "ig"); // do not support "m" flag because it doesn't work on older browsers
//   var vRegEx = new RegExp("\\s+", "mg");
// unfortunately, this loop will have to do even though its slower than letting the underlying JS engine
// find all copies internally. JS simply doesn't have that capability
   var vCnt = 0;
   while (vRegEx.exec(pText) != null)
     vCnt++;
   return vCnt;
}  // VAM_CntWords

//---- VAM SUPPORT FUNCTIONS ------------------------------

// button clicks that set CausesValidation true or otherwise attach to VAM_ValOnClick
// will set this to true so that VAM_TryToSubmit knows to process the validation action.
// Buttons with CausesValidation false use VAM_NoValidateOnClick to set this to false.
// When no button submits the page, its a special case where the user hits Enter while
// in a textbox. That needs to validate by using the VAM_Group field for the group.
var gVAM_CauseVal = true;

// Wrapper around the action function. Evaluates the condition then if supported, calls ActnFnc
// with true (success) or false (failed).
function VAM_DoAction(pAO)
{
   pAO.CondResult = -1;
   if (pAO.Enabled && pAO.ActnFnc)
   {
   // given the object another shot to stop now
      if (pAO.CanRun && !pAO.CanRun(pAO))
      {
      
      // for validators, restore the interface as if there was nothing wrong
         if (pAO.VT == "VAL")
            pAO.ActnFnc(pAO, 1);  
         return;
      }

      pAO.CondResult = VAM_EvalCondition(pAO.Cond);
      pAO.ActnFnc(pAO, pAO.CondResult);
   }
}  // VAM_DoAction

// Wrapper around the evaluation function. Returns 1 (success), 0 (failed), -1 (cannot eval)
function VAM_EvalCondition(pCO)
{
   if (pCO.Enabled && pCO.EvalFnc)
   {
      var vR = pCO.EvalFnc(pCO);
      if ((vR != -1) && pCO.NotCond)
         vR = (vR == 1) ? 0 : 1;
      return vR;
   }
   else
      return -1;
}  // VAM_EvalCondition

// Main page will call this once to run through the initialization process of all actions.
// Actions are all defined in gVAMActions array.
// It calls InitFnc on each Action. That in turn, calls InitFnc on each Condition.
// Conditions will hookup any control IDs they have to onchange or onclick event handlers
// to call VAM_DoAction so that field changes run actions.
function VAM_InitActions()
{
   var vAutoRun = new Array();   // each action that has the AutoRun property
   for (var vActnID = 0; vActnID < gVAMActions.length; vActnID++)
   {
      vAO = gVAMActions[vActnID];
      vAO.id = vActnID;   // define the ID officially
      if (vAO.Cond)
      {
         var vCO = vAO.Cond;
         
         // finish init
         if (vCO.InitFnc)
            vCO.InitFnc(vCO, vAO);
      }
//!bf v1.0.1
      var vEn = vAO.Enabler;
      if ((vEn != null) && (vEn.InitFnc != null))
         vEn.InitFnc(vEn, vAO);
//!bf-end

      // this is after the condition because the action may have to run the condition here
      // Until this point, the condition only needed the action for VAM_HookupControl
      if (vAO.InitFnc)
         vAO.InitFnc(vAO);
         
      if (vAO.AutoRun)
         vAutoRun[vAutoRun.length] = vAO;
         
   }
   // auto run actions now
   for (var vI = 0; vI < vAutoRun.length; vI++)
      VAM_DoAction(vAutoRun[vI]);
}  // VAM_InitActions

// Called during the initialization process for each field ID that should monitor
// changes via onchange or onclick. It connects the event handler to VAM_FieldChanged(pAOID)
// It adds the ActionID into an array property called ActionIDs.
// pFld is the field object to hookup. pAO is an Action VAM object that owns the condition.
// pCO is the associated condition object. Its HUCtrlFnc method is a Hookup Control method. 
// If not null, it will be called INSTEAD of the rest of the code here. 
// That method should call this function for each sub-control
// ID that needs to be hooked-up.
// If pCO.HUEvts == null, don't hookup at all.
// pAltEvent is a boolean. It is optional. When true, this function does not
// setup an actual onclick or onchange event handler. The caller sets its own event handler up.
// Returns true if this is the first time pFld is hookedup. Use this to add your own
// event handler after calling this. Returns false if pFld was previously hooked up.
function VAM_HookupControl(pFld, pAO, pCO, pAltEvent)
{
   if (pFld == null)
      return false;
   if ((pCO != null) && (!pCO.HUEvts))  // HUEvts is boolean. When true, hookup. When false or null, skip
      return false;
   if ((pCO == null) || (pCO.HUCtrlFnc == null))
   {
      var vFT = false;  // vFT= "firsttime"
      // establish pAO.Ctl, an array of controls that pAO refers to
      if (pAO.Ctl == null)
         pAO.Ctl = new Array;
      pAO.Ctl[pAO.Ctl.length] = pFld;
      
      if (pFld.ActionIDs == null) 
      {
         vFT = true;
         pFld.ActionIDs = new Array;
         
         if (!pAltEvent)
         {
            var vUseOnChange = ((pFld.type != null) && ((pFld.type == "text") || (pFld.type == "password")))   //!bf v1.0.1 handle password textbox
               || (pFld.tagName == "TEXTAREA") || (pFld.tagName == "SELECT");
            var vEv = vUseOnChange ? pFld.onchange : pFld.onclick;
            if (typeof(vEv) == "function") 
            {            
               vEv = vEv.toString();
               vEv = vEv.substring(vEv.indexOf("{") + 1, vEv.lastIndexOf("}"));
            }
            else
               vEv = "";

            var vFunc = new Function("VAM_FieldChanged('" + pFld.id + "'); " + vEv);
            if (vUseOnChange)
               pFld.onchange = vFunc;
            else           
               pFld.onclick = vFunc;
         }
            
         // if its a validator (pAO.IsValid is declared), add pFld to gVAM_ValFlds
         // This prepares a list of fields to review after validator action occurs
         if (pAO.VT == "VAL")
            gVAM_ValFlds[gVAM_ValFlds.length] = pFld;
	   }
      pFld.ActionIDs[pFld.ActionIDs.length] = gVAM_MAId != null ? gVAM_MAId : pAO.id;
      return vFT;
   }
   else
   {  // allow the Hookup Control method to call VAM_HookupControl for its subcontrols.
      pCO.HUCtrlFnc(pCO, pAO, pFld.id);
      return false;
   }

}  // VAM_HookupControl

// VAM_FindAOById is a user function that takes the ClientID to a control and returns
// an Action whose CID property matches. The CID property isn't written unless
// the BaseAction.SupportsClientScriptLookupByID is true.
// Returns null when not found. Otherwise returns an Action object.
// Use this to get to an action and its condition to evaluate and modify its properties.
function VAM_FindAOById(pClientID)
{
   pClientID = pClientID.toUpperCase();
   for (var vActnID = 0; vActnID < gVAMActions.length; vActnID++)
   {
      vAO = gVAMActions[vActnID];
      if ((vAO.CID != null) && (vAO.CID == pClientID))   // vAO.CID is already uppercase
         return vAO;
   }
   return null;
}  // VAM_FindAOById

function VAM_SetEnabled(pAO, pEnabled)
{
   if ((pAO != null) && (pEnabled != pAO.Enabled))
   {
      pAO.Enabled = pEnabled;
      if (pEnabled)
         VAM_DoAction(pAO);
      else
      {
         if (pAO.VT == "VAL")
            pAO.ActnFnc(pAO, 1);
      }
      // required field marker needs to be shown/hidden
      if (pAO.CID != null)
      {
         var vRFM = VAM_GetById(pAO.CID + "_RFM");
         if (vRFM != null)
            vRFM.style.visibility = pEnabled ? "inherit" : "hidden";
      }
   }
}  // VAM_SetEnabled

// Called by the onchange event handler to run the action
function VAM_FieldChanged(pFldId)
{
   gVAMSubmitEvent = false;
   var vFld = VAM_GetById(pFldId);
   // Loop through all actions and call VAM_DoAction on each.
   for (var vI = 0; vI < vFld.ActionIDs.length; vI++)
      VAM_DoAction(gVAMActions[vFld.ActionIDs[vI]]);
   VAM_PostValidateFld(vFld); // changes fields classes, sets focus, and shows alerts
/* TOGGLING STATES NOT SUPPORTED
   // change the toggle state on the RunFld. It is detected by the ToggleStateCondition
   if (vFld.TglSt != null)
      vFld.TglSt = !vFld.TglSt;
*/      
}  // VAM_FieldChanged

// Called when there is an attempt to submit. Its usually hooked up to buttons
// and image buttons onclick event handler.
// Returns true if validation succeeded. False if not.
// Pass pGroup = "*" to validate every enabled validator regardless of the group
// Pass pReal = true for a real submit and false when its not really going to submit
// NOTE: VAMPage.GetValidateGroupScript must be modified if the parameters are changed.
function VAM_TryToSubmit(pGroup, pReal)
{
   var vIsValid = true; // true
   gVAMSubmitEvent = pReal;
   if (this.gVAMActions == null) // when there are no actions that are enabled
      return true;
   // loop through gVAMActions. Any that has a Group is a validator
   // Match to pGroup and run its validation. If any fail, vIsValid is false
   // and any ValidatorSummarys for pVO group are shown
   for (var vI = 0; vI < gVAMActions.length; vI++)
   {
      var vAO = gVAMActions[vI];
      if ((vAO.Group != null) && // indicates its a validator action
          ((vAO.Group == pGroup) || (pGroup == "*")))   // Group should be uppercase already
      {
         vAO.IsValid = true;   // because VAM_DoAction may not call the condition
         VAM_DoAction(vAO); // sets vAO.IsValid
         if (!vAO.IsValid)
            vIsValid = false; // don't stop on the first invalid one. Let all show their message
      }
   }  // for
   VAM_PostValidate(pGroup, true); // changes fields classes, sets focus, shows alerts and sets up gVAM_ErrMsgs for SummaryValidators
   return vIsValid;
}  // VAM_TryToSubmit()

// setup the form's onreset event to call this. It clears all validators as if the page was just loaded
// When pIsPostBack is true, it calls VAM_TryToSubmit to show all validation errors.
// Otherwise, it clears any validator error message.
// VAMPage.SetupOnResetEvent uses this.
function VAM_OnReset(pIsPostBack)
{
   if (this.gVAMActions == null) // when there are no actions that are enabled
      return;
   if (pIsPostBack)
      VAM_TryToSubmit("*", false);
   else
   {
      // loop through gVAMActions. Any that has a Group is a validator
      // Match to pGroup and run its validation. If any fail, vIsValid is false
      // and any ValidatorSummarys for pVO group are shown
      for (var vI = 0; vI < gVAMActions.length; vI++)
      {
         var vAO = gVAMActions[vI];
         if (vAO.Group != null) // indicates its a validator action
         {
            vAO.IsValid = true;
            if (vAO.Enabled && vAO.ActnFnc)
               vAO.ActnFnc(vAO, 1);  // will clear the error message with "1"
         }
      }  // for
      VAM_PostValidate("*", true); // changes fields classes, sets focus, shows alerts and sets up gVAM_ErrMsgs for SummaryValidators
   }
   
   // field state controllers and others 
   if (this.VAMRunAllFSC)  // this function is in VAM_FSC.js
      VAM_RunAllFSC();
}  // VAM_OnReset()

// Called by the form's onsubmit event. Runs VAM_TryToSubmit using the group
// from the hidden field 'VAM_Group'. If its valid, return true. Otherwise return false.
function VAM_ValOnSubmit()
{
// allows buttons without the need to cause validation to skip this. Set gVAM_CauseVal to true in onclick to continue
   if (!gVAM_CauseVal)
      return true;
   gVAM_CauseVal = true; // reset it
      
   var vGrpFld = VAM_GetById("VAM_Group"); // .net doesn't put an ID on the hidden field. So NS6+ can't find it
   if (vGrpFld == null)
   {
      var vFlds = document.getElementsByName("VAM_Group");
      if ((vFlds == null) || (vFlds.length == 0))
         return true;
      vGrpFld = vFlds[0]; 
   }
   
   // optional initial messagebox
   if ((this.gVAMConfMsg != null) &&  (this.confirm != null) &&
       ((this.gVAMConfMsgGrp == "*") || (this.gVAMConfMsgGrp == vGrpFld.value)))
      if (!confirm(gVAMConfMsg))
         return false;
         
   // optional user supplied function
   if (this.gVAMCstmSubmitFnc != null)
      if (!eval(gVAMCstmSubmitFnc + "('" + vGrpFld + "');"))
         return false;
      
//!dc v1.0.1
   var vR = VAM_TryToSubmit(vGrpFld.value, true);
   if (vR)
      VAM_DisableSubmit();
      
//JENZABAR: Added the following line to fix a bug that was causing validation to occur when it should not. (Downey, 10/21/03)
   vGrpFld.value = '';
      
   return vR;
/*      
   return VAM_TryToSubmit(vGrpFld.value, true);
//!dc-end */   
}  // VAM_ValOnSubmit

// Called from onclick events on buttons and hrefs on <a> tags to
// establish the group in the hidden field. After pVO, those controls
// should be setup in some way to submit the page. That runs VAM_ValOnSubmit
// to do the actual validation
// pGroup is the name of the group. It can be "". It must be uppercase.
function VAM_ValOnClick(pGroup)
{
   var vGrpFld = VAM_GetById("VAM_Group"); // .net doesn't put an ID on the hidden field. So NS6+ can't find it
   if (vGrpFld == null)
   {
      var vFlds = document.getElementsByName("VAM_Group");
      if ((vFlds == null) || (vFlds.length == 0))
         return;
      vGrpFld = vFlds[0]; 
   }
   vGrpFld.value = pGroup;
   gVAM_CauseVal = true;   // signal to VAM_ValOnSubmit to run all its code
}  // VAM_ValOnClick

// Modifies the onclick event handler of pFldId to call VAM_ValOnClick
// <a> tags whose href is setup to run Javascript will still get onclick setup
// pGroup is null, it does not use VAM_ValOnClick. Instead it sets gVAM_CauseVal = false
// to indicate that this button does not validate (it has CausesValidation=false)
function VAM_UpdateOnClick(pFldId, pGroup)
{
   var vFld = VAM_GetById(pFldId);
   var vFnc = vFld.onclick;
   if (typeof(vFnc) == "function" ) 
   {            
      vFnc = vFnc.toString();
      vFnc = vFnc.substring(vFnc.indexOf("{") + 1, vFnc.lastIndexOf("}"));
   }
   else 
      vFnc = "";
   if (pGroup != null)
      vFld.onclick = new Function("VAM_ValOnClick('"+ pGroup+ "'); " + vFnc);
   else
      vFld.onclick = new Function("gVAM_CauseVal = false; " + vFnc);
}  // VAM_UpdateOnClick
//!dc v1.0.1
// PeterBlum.VAM.LinkButton needs to update the href= attribute with
// a call to VAM_HrefClick.
// Can be used to setup href on any control with that attribute.
function VAM_InitLinkBtn(pFldId, pGroup)
{
   var vFld = VAM_GetById(pFldId);
   
   vFld.href = 'javascript: VAM_HrefClick("' + pGroup + '", "' + vFld.href.replace('javascript:', '') + '");'; // single quotes are used because href already contains single quotes
}  // VAM_InitLinkBtn

// For validation on an href. Runs both VAM and MS validation. Then calls __doPostBack if
// all validation succeeded.
// runs when an <a> tag is clicked. Submit is the __doPostBack captured from the href.
function VAM_HrefClick(pGroup, pSubmit)
{
   var vOK = true; 
   VAM_ValOnClick(pGroup); 
   if (!VAM_ValOnSubmit()) 
      return;
   if (this.Page_ClientValidate != null)  // MS validators are installed
      if (!Page_ClientValidate())
         return;
   eval(pSubmit);
}  // VAM_HrefClick
//!dc-end

//!dc-1.0.1
// Sets all controls listed in gVAMSubmitIDs to disabled
// Call this when submitting the page so the user cannot double-click on a submit button
// Always returns true so it can be part of the form's onsubmit event handler.
// NOTE: The disabled property is not uniformly supported on all browsers or all elements on the page.
function VAM_DisableSubmit()
{
   if (this.gVAMSubmitIDs != null)
   {
   // Cannot disable until after the form.submit function executes
   // because a disabled button will not write its value to the form post back info
   // and that means the server side Click event will not fire
   // This delay executes after form.submit occurs
      var vCode = "javascript:VAM_DSBody();"; 
      setTimeout(vCode, 20); 
   }
   return true;
}  // VAM_DisableSubmit

function VAM_DSBody()
{
   for (var vI = 0; vI < gVAMSubmitIDs.length; vI++)
   {
      var vFld = VAM_GetById(gVAMSubmitIDs[vI]);
      if (vFld.disabled != null)
         vFld.disabled = true;
   }

}  // VAM_DSBody
//!dc-end

// Returns a trimmed string
// pVO is taken from Microsoft's WebUIValidation.js
function VAM_Trim(s) {
    var m = s.match(/^\s*(\S+(\s+\S+)*)\s*$/);
    return (m == null) ? "" : m[1];
}  // VAM_Trim

// Returns the textual value of input text, input file, textarea, and select elements.
// Assumes pId has a match. Based on Microsoft's and DOM Validator's script for ValidatorGetValue
// When pTrim is true, it trims the text before returning it
function VAM_GetTextValue(pId, pTrim) {
    var vC = VAM_GetById(pId);

// text, input file, and text area
    if (typeof(vC.value) == "string") 
        return pTrim ? VAM_Trim(vC.value) : vC.value;
        
// SELECT using modern browser code        
    if (vC.options && vC.selectedIndex)
      if (vC.selectedIndex == -1)
         return "";
      else
      {
         var vVal = vC.options[vC.selectedIndex].value;
         if (pTrim)
            vVal = VAM_Trim(vVal);
         return vVal;
      }
      
//!bf v1.0.1
// radiobuttonlist (to match Microsoft's functionality). Also supports CheckBoxList
// RadioButtonList is in a <table>. The ID refers to that table.
// Test for both the table and the first radio button's presence with its ID = pId + "_0"
      if (vC.tagName == "TABLE")
      {
         var vD = false;
         for (var vI = 0; !vD; vI++)
         {
            var vChild = VAM_GetById(pId + '_' + vI);
            if (vChild != null)
            {
               if ((vChild.checked != null) && vChild.checked) // it's a radio button or checkbox
                  return vChild.value;
            }
            else  // passed all children or it wasn't a radiobuttonlist. Exit the loop
               vD = true;
         }  // for
      }  // if table
/*
    if (typeof(vC.tagName) == "undefined" && typeof(vC.length) == "number") {
        for (var j=0; j < vC.length; j++) {
            var inner = vC[j];
            if (typeof(inner.value) == "string" && (inner.type != "radio" || inner.status == true)) {
                return pTrim ? VAM_Trim(inner.value) : inner.value;
            }
        }   // for
    }
//!bf-end*/    
    return ""; // last check
}  // VAM_GetTextValue

// Returns the actual string for the error message on the validator that is shown the user
// Includes token replacement
function VAM_GetErrMsg(pVO)
{
   var vMsg = pVO.SelErrMsg(pVO);
   if ((vMsg != "") && pVO.TokenRepl)
      vMsg = pVO.TokenRepl(pVO, vMsg);
   return vMsg;
}  // VAM_GetErrMsg

// Returns the actual string for the error message on the summary validator that is shown the user
// Includes token replacement and retrieval of the ErrMsg when the SumMsg is blank.
function VAM_GetSumMsg(pVO)
{
   var vMsg = pVO.SelSumMsg(pVO);
   if ((vMsg != "") && pVO.TokenRepl)
      vMsg = pVO.TokenRepl(pVO, vMsg);
   if (vMsg == "")
      vMsg = VAM_GetErrMsg(pVO);
   return vMsg;
}  // VAM_GetSumMsg

// --- Post validation functions and globals ----------------------------
// Validation may show several forms of information when an error is detected.
// The Action function will toggle on an error message using Action.FmttrFnc.
// The ability to set focus, change the data field color, and popup an alert
// are dependent on this function. A field may have several validator actions.
// Some may be reflect invalid conditions. Some valid. We cannot determine
// the state of the field until we review all actions on a field.
// These functions and globals are used to establish the remaining actions.

// gVAM_ValPassCnt's value is assigned to each field's ValPassCnt attribute.
// If fld.ValPassCnt < gVAMValPassCnt, the field has not been processed in this pass.
// It is incremented each time VAM_PostValidate and VAM_PostValidateFld are called.
var gVAM_ValPassCnt = 0; 

// gVAM_ValErrMsgs is an array that is initialized to empty each time VAM_PostValidate
// and VAM_PostValidateFld are called. VAM_PostValidateBody will add the error message strings
// (either normal or summary) for each action that reports its invalid.
// The elements of this array are objects with these properties:
// Msg - the message string
// Grp - the group that triggered this string
// FldId - the associated field that generated the error. Used for setting focus
var gVAM_ValErrMsgs = null;

// This function will go through all fields in gVAM_ValFlds. It will increment
// gVAM_ValPassCnt and apply the settings of gVAM_AlertOnSubmit, gVAM_FocusOnSubmit
// and gVAM_ErrCtlCss.
// When pAll is true, all features are included. When false, PostValidationAction is skipped
// and so are the validationsummaries not showing in auto update mode.
function VAM_PostValidate(pGroup, pAll)
{
   if (this.gVAM_PostOnSubmit == null) // no validators defined
      return;
   if (gVAM_PostOnSubmit)
   {
      gVAM_ValPassCnt++;
      gVAM_ValErrMsgs = new Array;  // abandons the previous contents
      var vFstInv = null;  // will be assigned to the first field found invalid
      for (var vI = 0; vI < gVAM_ValFlds.length; vI++)
      {
         var vFld = gVAM_ValFlds[vI];
         if (!VAM_PostValidateBody(vFld, pGroup, true))
            if (vFstInv == null)
               vFstInv = vFld;
      }
      
      if ((vFstInv != null) && pAll)
         VAM_PostValidateAction(vFstInv, gVAM_FocusOnSubmit, gVAM_AlertOnSubmit);
            
      VAM_UpdateSummaries(pGroup, !pAll);
   // The user can supply a JavaScript function to call now in gVAMValUpdFnc
   // This is useful for moving absolutely positioned objects since the page may have
   // resized as validators and the summary show and hide
      if (this.gVAMValUpdFnc)
         eval(this.gVAMValUpdFnc);
      
   }
}  // VAM_PostValidate

// Called by VAM_FieldChanged to apply post validation rules to the field supplied
function VAM_PostValidateFld(pFld)
{
   if (this.gVAM_PostOnChange == null) // no validators defined
      return;
   if (gVAM_PostOnChange)
   {
      gVAM_ValPassCnt++;
      gVAM_ValErrMsgs = new Array;  // abandons the previous contents
      if (!VAM_PostValidateBody(pFld, "*", false))
         VAM_PostValidateAction(pFld, gVAM_FocusOnChange, gVAM_AlertOnChange);
            
   }
   VAM_AutoUpdateSummaries("*");
// The user can supply a JavaScript function to call now in gVAMValUpdFnc
// This is useful for moving absolutely positioned objects since the page may have
// resized as validators and the summary show and hide
   if (this.gVAMValUpdFnc)
      eval(this.gVAMValUpdFnc);
   
}  // VAM_PostValidateFld

// Called by VAM_PostValidate and VAM_PostValidateFld to process all validator-type
// actions attached to pFld. (pFld.ActionIDs array.) Only updates those whose 
// pFld.ValPassCnt < gVAM_ValPassCnt.
// Returns a boolean where true indicates the field is valid and false indicates invalid.
// For each invalid validator action, it adds the error message or summary message to
// gVAM_ValErrMsgs. pSubmit determines which msg is added. gVAM_ValErrMsgs is used to formulate
// the alert of errors and ValidationSummary.
// pGroup determines if the action is part of a group. Can pass '*' to match to all groups.
function VAM_PostValidateBody(pFld, pGroup, pSubmit)
{
   var vIsValid = null; // when it becomes not null, we found a validator
   if (pFld.ValPassCnt == null)  // first time
      pFld.ValPassCnt = 0;
   if (pFld.ValPassCnt < gVAM_ValPassCnt)
   {
      var vOtherFlds = new Array;   // any other fields associated with an action are assigned here (when !pSubmit)
      pFld.ValPassCnt = gVAM_ValPassCnt;
      for (var vI = 0; vI < pFld.ActionIDs.length; vI++)
      {
         vAO = gVAMActions[pFld.ActionIDs[vI]];
         if (vAO.ValPassCnt == null)
            vAO.ValPassCnt = 0;
            
         // is it a validator and a matching group?
         if ((vAO.IsValid != null) && (vAO.CondResult != -1) && 
             ((pGroup == '*') || (vAO.Group == pGroup)))
         {
            if (!vAO.IsValid)
            {
               if (vAO.ValPassCnt < gVAM_ValPassCnt)   // same action may be tied to two fields. Get one error message
               {
               //NOTE: Warning messages (pAO.EvtToVal==2) should not appear in a validation summary
                  if (!pSubmit || (vAO.EvtToVal != 2))
                  {
                     var vMsg = pSubmit ? VAM_GetSumMsg(vAO) : VAM_GetErrMsg(vAO);
                     if (vMsg != "")
                        gVAM_ValErrMsgs[gVAM_ValErrMsgs.length] = {Msg: vMsg, Grp: vAO.Group, FldId: pFld.id};
                  }
               }
               vIsValid = false;
            }
            else if (vIsValid == null) // first validator
               vIsValid = true;
          
            // when pSubmit is false, update the other fields on vAO.Ctl to the same class
            if (!pSubmit)
               for (var vJ = 0; vJ < vAO.Ctl.length; vJ++)
               {
                  var vOther = vAO.Ctl[vJ];
                  if (vOther != pFld)
                  {
                  // mark vOther with the state of vIsValid in vOther.IsValid. Init vOther.IsValid on the first time
                  // Change vOther.IsValid only the first time or when vIsValid is false.
                     vOtherFlds[vOtherFlds.length] = vOther;
                     if ((vOther.ValPassCnt == null) || (vOther.ValPassCnt < gVAM_ValPassCnt)
                          || !vAO.IsValid)
                        vOther.IsValid = vAO.IsValid;
                     vOther.ValPassCnt = gVAM_ValPassCnt;
                  }  // if
               }  // for vJ
          } // if this action is available
          vAO.ValPassCnt = gVAM_ValPassCnt;
      }  // for vI
      
      // if there was a validator and gVAM_ErrCtlCss has a valid, update the class
      if (vIsValid != null)
      {
         VAM_PostValidateErrCtl(pFld, vIsValid);
         for (var vI = 0; vI < vOtherFlds.length; vI++)
         {
            VAM_PostValidateErrCtl(vOtherFlds[vI], vOtherFlds[vI].IsValid);
         }  // for vJ

      }  // if (vIsValid)
   }  // if (passcnt)
   return vIsValid != false;  // will handle vIsValid = null as true
}  // VAM_PostValidateBody

// Called by VAM_PostValidateBody to update the className attribute of pFld.
// When pIsValid is true, assign pFld.OrigCss. When false, assign gVAM_ErrCtlCss
function VAM_PostValidateErrCtl(pFld, pIsValid)
{
   if ((gVAM_ErrCtlCss != "") && (pFld.className != null))
   {
      if (pIsValid)
      {
         if (pFld.OrigCss != null)
            pFld.className = pFld.OrigCss;
      }
      else
      {
         if (pFld.OrigCss == null)
            pFld.OrigCss = pFld.className;
         if (gVAM_ErrCtlCss != "")
            pFld.className = gVAM_ErrCtlCss;
      }
   }  // if (gVAM_ErrCtlCss)
}  // VAM_PostValidateErrCtl

// called by VAM_PostValidate and VAM_PostValidateFld when there was an invalid field
// It applies the actions of setting focus when pFocus is true and showing
// an alert when pAlert is true. 
// The alert is build from pMsgTmplt, which is the string containing the list of msgs,
// gVAM_ValErrMsgs which are the error messages, and pLeader which is inserted in front of each
// message.
// pMsgTmplt uses the token '{0}' to be replaced by the Messages. The token is optional.
// Each message gets gLeader in front. The token '#' is replaced by a message number.
// Each message gets a carriage return after it.
function VAM_PostValidateAction(pFld, pFocus, pAlert)
{
   if (pFocus)
   {         
      var vCode = "javascript:VAM_SetFocus('" + pFld.id + "');";  // VAM_SetFocus tests for visibility and enabled
      setTimeout(vCode, 10); 
   }
   if (pAlert)
   {
   // if the {0} token is found, build the list of error messages into one string
   // and replace the token with it. if pMsgTmplt is blank, do the same.
      var vMsg = gVAM_AlertTemplate;
      if ((vMsg == "") || (vMsg.indexOf("{0}") > -1))
      {
         var vBody = "";
         for (var vI = 0; vI < gVAM_ValErrMsgs.length; vI++)
         {
            var vCnt = vI + 1;
            var vLine = "";
            if (gVAM_AlertListStyle)
               vLine = gVAM_AlertLeadText.replace("#", vCnt) + gVAM_ValErrMsgs[vI].Msg + "\n";
            else
               vLine = gVAM_ValErrMsgs[vI].Msg + " ";
            vBody = vBody + vLine;
         }  // for
         if (vMsg == "")
            vMsg = vBody;
         else
            vMsg = vMsg.replace("{0}", vBody);
      }
      alert(VAM_StripTags(vMsg));
   }
}  // VAM_PostValidateAction

// As the last step of VAM_PostValidate, goes through the list of 
// validation summaries, gVAM_ValSummary, and shows or hides them.
// At this time gVAM_ValErrMsgs contains all the summary messages associated 
// with the group of the action that generated them
// When pAutoUpd is true, only update those marked auto update and already showing
function VAM_UpdateSummaries(pGroup, pAutoUpd)
{
   if (this.gVAM_ValSummary != null)
   {
      for (var vI = 0; vI < this.gVAM_ValSummary.length; vI++)
      {
         var vVSO = this.gVAM_ValSummary[vI];
         if (!pAutoUpd || (vVSO.Showing && vVSO.AutoUpd))
            VAM_UpdateValSum(vVSO, pGroup);
      }
   }
   
   // The user can supply a JavaScript function to call now in gVAM_ValUpdFnc
   // This is useful for moving absolutely positioned objects since the page may have
   // resized as validators and the summary show and hide
   if (this.gVAM_ValUpdFnc)
      eval(this.gVAM_ValUpdFnc);
}  // VAM_UpdateSummaries

// Called when an individual field changes its validation
// Looks for any validator that matches the group with
// vVSO.Showing and vVSO.AutoUpd true.
// When one is found, run VAM_PostValidate as if we hit submit but
// ignore the post validation focus, alert, and field change
function VAM_AutoUpdateSummaries(pGroup)
{
   if (this.gVAM_ValSummary != null)
   {
      for (var vI = 0; vI < this.gVAM_ValSummary.length; vI++)
      {
         var vVSO = this.gVAM_ValSummary[vI];
         if (vVSO.Showing && vVSO.AutoUpd)
         {
            VAM_PostValidate(pGroup, false);
            return;
         }
      }
   }
}  // VAM_AutoUpdateSummaries

// Changes the inner HTML of pVSO.ValSumId when pGroup is "*" or matches
// its own groups. It gets error messages from gVAM_ValErrMsgs.
// If there are no error messages, the innerHTML is set to blank.
// Otherwise its formatted by the pVSFld.FormatFnc function.
function VAM_UpdateValSum(pVSO, pGroup)
{
   var vUse = false;
   if (pGroup == "*")
      vUse = true;
   else
   {
      if (pVSO.fGroups == null)  // first time setup fGroups
      {
         pVSO.fGroups = pVSO.Grp.split('|');
         // trim spaces
         for (var vI = 0; vI < pVSO.fGroups.length; vI++)
            pVSO.fGroups[vI] = VAM_Trim(pVSO.fGroups[vI]);
      }
      
      for (var vI = 0; !vUse && (vI < pVSO.fGroups.length); vI++)
         vUse = (pVSO.fGroups[vI] == "*") || (pVSO.fGroups[vI] == pGroup);  // both are already uppercase
   }
   if (vUse)
   {
      // go through gVAM_ValErrMsgs. Any that match the group is added to vList
      var vList = "";
      var vPosNum = 0;
      for (var vI = 0; vI < gVAM_ValErrMsgs.length; vI++)
      {
         var vMsgGrp = gVAM_ValErrMsgs[vI].Grp;
         var vMatch = pGroup == "*";
         if (!vMatch)
            for (var vJ = 0; !vMatch && (vJ < pVSO.fGroups.length); vJ++)
               vMatch = (pVSO.fGroups[vJ] == "*") || (pVSO.fGroups[vJ] == vMsgGrp);  // both are already uppercase
         if (vMatch)
         {
            if (pVSO.FmtListFnc != null)
            {
               vMsg = gVAM_ValErrMsgs[vI].Msg;
               
               // apply a hyperlink to VAM_SetFocus
               if (pVSO.Links)
                  vMsg = "<a href=\"javascript:VAM_SetFocus('" + gVAM_ValErrMsgs[vI].FldId + "');\">"+ vMsg+"</a>";
                  
               vList = vList + pVSO.FmtListFnc(pVSO, vMsg, vPosNum);
               vPosNum++;
            }
            else  // no list is shown
               vList = "!";   // indicates there was something found
         }  // if (vMatch)
      }  // for vI
      
      pVSO.Showing = (vList != ""); // indicates that its showing something
      
      // if we have something to show, build the error message. Otherwise, set the innerHTML to blank
      vFld = VAM_GetById(pVSO.ValSumID);
      if (pVSO.Showing)
      {
/*      
         var vFullMsg = "";
         if (pVSO.Hdr != "")
            vFullMsg = pVSO.Hdr + pVSO.HdrSep;
         if (pVSO.PreListFnc != null)  // assumes PostListFnc is also assigned
            vFullMsg += pVSO.PreListFnc(pVSO) + vList + pVSO.PostListFnc(pVSO);
         if (pVSO.Ftr != "")
            vFullMsg += pVSO.Ftr;
         VAM_SetInnerHTML(vFld, vFullMsg);
*/
         VAM_SetInnerHTML(vFld, pVSO.GetInner(pVSO, vList));         
         vFld.style.visibility = "inherit";
         if (!pVSO.InvSpc)
            vFld.style.display = "block";
         
         if (pVSO.RelCtl != null)
            VAM_GetById(pVSO.RelCtl).style.visibility = "inherit";
      }
      else
      {
         if (!pVSO.InvSpc)
            vFld.style.display = "none";
         vFld.style.visibility = "hidden";
         VAM_SetInnerHTML(vFld, "");
         if (pVSO.RelCtl != null)
            VAM_GetById(pVSO.RelCtl).style.visibility = "hidden";
      }
   }  // if (vUse)
   
}  // VAM_UpdateValSum

// Blinking goes through gVAM_Actions looking for items whose BlinkCnt != 0.
// It shouldn't take any action when all actions are BlinkCnt = 0
// This is a count of the actions whose BlinkCnt != 0
var gVAM_BlinkObjCnt = 0;
// All blinking objects should be in the same state, normal or blinked,
// at the same time. When this is true, they are in their blinked state
// Set to true when gVAM_BlinkObjCnt switches from 0 to 1.
var gVAM_BlinkState = false;

// SetInterval returns this value
var gVAM_BlinkTimerID = 0;

// Called by an action to start blinking.
// If already blinking or pAO.Blnk == false or pAO.BlinkFnc == null, it does nothing.
// Otherwise, it increments gVAM_BlinkObjCnt, sets pAO.BlinkCnt, and if needed, starts
// SetInterval on VAM_DoBlink
function VAM_StartBlink(pAO)
{
   if (gSupportsSetInterval && (pAO.Blnk) && (pAO.BlinkCnt == 0) && (pAO.BlinkFnc != null))
   {
      pAO.BlinkCnt = gVAMSubmitEvent ? gVAMBlinkOnSubmit : gVAMBlinkOnChange;
      if (pAO.BlinkCnt == 0) return;         
      gVAM_BlinkObjCnt++;
      if (gVAM_BlinkObjCnt == 1)
      {
      // start the blinking function
         gVAM_BlinkState = false;
         gVAM_BlinkTimerID = window.setInterval("JavaScript: VAM_DoBlink();", gVAMBlinkTime);
      }
      else if (gVAM_BlinkState)  // set it to the blinked state because others on the page already are at this state
         pAO.BlinkFnc(pAO, true); 
   }
}  // VAM_StartBlink

// Called to shut off blinking for the action.
// Will shut off the setInterval(VAM_DoBlink) when gVAM_BlinkObjCnt == 0
// Expects pAO.BlinkCnt != 0
function VAM_StopBlink(pAO)
{
   if (pAO.BlinkCnt != 0)
   {         
      pAO.BlinkFnc(pAO, false);  // show it in non-blinked state
      gVAM_BlinkObjCnt--;
      pAO.BlinkCnt = 0;
      if (gVAM_BlinkObjCnt == 0)
      {
         window.clearInterval(gVAM_BlinkTimerID);
         gVAM_BlinkTimerID = 0;
      }
   }
}  // VAM_StopBlink

// Runs through gVAM_Actions. Any with pAO.BlinkCnt != 0 will call its pAO.BlinkFnc
// For pAO.BlinkCnt > 0, it decrements when gVAM_BlinkState == false. If BlinkCnt == 1,
// it calls VAM_StopBlink.
function VAM_DoBlink()
{
   gVAM_BlinkState = !gVAM_BlinkState;
   for (var vActnID = 0; vActnID < gVAMActions.length; vActnID++)
   {
      vAO = gVAMActions[vActnID];
      if ((vAO.BlinkCnt != null) && (vAO.BlinkCnt != 0))
      {
         if ((vAO.BlinkCnt > 0) && !gVAM_BlinkState)  // decrement or turn off vAO.BlinkCnt
         {
            if (vAO.BlinkCnt == 1)
            {
               VAM_StopBlink(vAO);
               continue;
            }
            else
               vAO.BlinkCnt--;
         }
         vAO.BlinkFnc(vAO, gVAM_BlinkState);
      }
   }  // for 
}  // VAM_DoBlink
