﻿//Javascript file 10/15/06
//alert("2 Utils.js");
//Utils.js MUST BE CALLED EVERYWHERE Cusanos.js IS CALLED:

/*
CONTENTS

INCLUDES iFrame drag and drop functions:

fnBlink() //12/16/10 new:

fnCalcProducePrice()
fnCancel()
fnConvertToDecimal()

fnDialer()
fnBlink() //12/16/10 new:
fnFindBy()
fnFormatDate()

fnGetBrowserName()       //04/06/2011 new: 
fnGetClientHeight()
fnGetDateEndOfMonth()    //12/08/07 - for comparison reports:
fnGetDayOfWeek()
fnGetFirstDateOfWeek()
fnGetFocusYPos()
fnGetOffset()
fnGetPhone()
fnGetPublicHolidays()
fnGetScrollTop()

fnOpenExcel_Email()

//////////////////////////////////////
//For user control DropDownMS.ascx:
//  10/18/10 Why can't these functions be in DropDownMS.ascx?
//  They're better here. Otherwise they get duplicated when there is more than one ddlDropdownMS on a page:
//  For that reason, would be better is ALL functions in DropDownMS.ascx were here:
fnInitializeDropDownMS()
fnInitializeDropDownMS2()
fnInitializePageSize() //06/21/08 new:
//////////////////////////////////////

fnMyDateDiff()  '10/21/09 new:
fnMyRound2()    NOTE fnMyRound() is in Cusanos:
isNaD()

fnOK() 
fnOpenDatePickerGR()

fnPopupMsg()
fnPeriod

fnSetElementHeight2()

fnTrimB()
fnUnLoadDatePickerGR()

fnValCloseOutTimeOffSet()
fnValPrice()
fnValNumber()     //10/07/07  Moved here from customer.aspx - for use in Setup.aspx:
fnValRanges()     //06/08/08 replaces fnValfuelSurcharge()
fnValTime()       //06/12/09 Moved here from Setup.aspx - for use in Customer.aspx:
fnValTime24hr()   //03/07/2011 new. Peter insists!
fnWriteMsg()
*/

var myTimerBlink = "";


//04/03/2011 Two public variables. Used in fnOpenDatePickerGR(). ReadOnly true textbox while DatePicker is open:
var tbxName = "";
blnResetReadOnly = false;

//////////////////////////////////////////////////////////////////////////
////////////////////////////// POPUPMSG //////////////////////////////////
//////////////////////////////////////////////////////////////////////////
function fnPopupMsg(sMessage, sColor, sType)
{
    //02/15/09 Modified to return values SERVER side. Previously capable client side only.
    //  Customer.WriteMsg() similarly modified. _OrderEntry.WriteMsg() has NOT been similarly modified:
    //07/19/08 Modified to use message stored in tbxPopupMsg. First used in Master_OrderEntry, CallLists_OrderEntry.
    //  Use try/catch to protect on those pages where still using MessageBox.Text:
   
    //04/25/07 Expanded use to return value:
    //OK (default), Yes, No, OK: 
    //alert("Utils.js 53 fnPopupMsg sMessage = " + sMessage + ", sColor = " + sColor + ", sType = " + sType);
    //10/17/06 Opens WinMessage.aspx into an iFrame. Window popup is blocked by browsers!
    //   Called client side but may be initiated client side or server side:
    //   If arguments are "", then uses MessageBox, PopupMsgAttribs which were assigned server side: 
    //   sType currently defaults to "O" (OK):
    //Requirements on consuming page:
    //1. <IFRAME id='ifrmPopupMsg' src='' scrolling="no" style="position:absolute;height:0;width:0; z-index:5"></IFRAME>
    //2. <SCRIPT language=javascript src="Utils.js"></SCRIPT>

   blnReturn="OK";
   if (sMessage=="")
   {
        //Initiated server side:
        sPopupAttribs =  document.getElementById("tbxPopupAttribs").value;
        //alert("67 sPopupAttribs = " + sPopupAttribs); 
        if (sPopupAttribs=="") {return;}  //Ensures called up on first round trip only:

        //Attribs: For now only color:
        sColor=sPopupAttribs;
        if (sColor.length>1)
        {
            sType=sColor.substr(1);
            sColor=sColor.substr(0,1);
        }
        //////////////sType="O";     //Default for now 04/19/07 Enabled for use with 'Order Not saved':
       
        try
        { 
            sMessage=document.getElementById("tbxPopupMsg").value; 
        }
        catch(e)
        { 
            sMessage=document.getElementById("MessageBox").value;  
        } 
        //alert("82 sMessage = " + sMessage); 
        
        document.getElementById("tbxPopupAttribs").value="";  
   }
   else
   {
        //Initiated client side:
        //////////sType="O" 
   }

   if (sType=="") {sType="O";}    //04/19/07
   //alert("93 Utils sType = " + sType);
       
    //Display (including code, if there is one):
    //01/16/09 Need a parameter to identify which ifrmPopupMsg:
    var temp = sMessage + "*" + sColor + "*" + sType + "*"; //^ passed as escape char:
    
    //08/01/08 Added escape(). Doesn't pass #:
    //alert("117 Utils PopupMsg temp = " + "WinMessage.aspx?" + escape(temp));
    document.getElementById("ifrmPopupMsg").src="WinMessage.aspx?" + escape(temp); 

}


function fnPopupMsg2(sMessage, sColor, sType)
{
    //11/29/08 Called from fnYes3():
   blnReturn="OK";
   if (sMessage=="")
   {
        //Initiated server side:
        sPopupAttribs =  document.getElementById("tbxPopupAttribs").value;
        //alert("44 sPopupAttribs = " + sPopupAttribs); 
        if (sPopupAttribs=="") {return;}  //Ensures called up on first round trip only:

        //Attribs: For now only color:
        sColor=sPopupAttribs;
        //////////////sType="O";     //Default for now 04/19/07 Enabled for use with 'Order Not saved':
       
        try
        { 
            sMessage=document.getElementById("tbxPopupMsg").value; 
        }
        catch(e)
        { 
            sMessage=document.getElementById("MessageBox").value;  
        } 
        //alert("58 sMessage = " + sMessage); 
        
        document.getElementById("tbxPopupAttribs").value="";  
   }
   else
   {
        //Initiated client side:
        //////////sType="O" 
   }

   if (sType=="") {sType="O";}    //04/19/07
   //alert("139 Utils sType = " + sType);

      
    //Display (including code, if there is one:
    //01/16/09 Need a parameter to identify which ifrmPopupMsg:
    var temp = sMessage + "*" + sColor + "*" + sType + "*2"; //^ passed as escape char:
    
    //08/01/08 Added escape(). Doesn't pass #:
    //alert("117 Utils PopupMsg temp = " + "WinMessage.aspx?" + escape(temp));
    document.getElementById("ifrmPopupMsg2").src="WinMessage.aspx?" + escape(temp);  
}

function fnUpdatePopupMsg()
{
    //alert("130 Utils fnUpdatePopupMsag()");
}


//These functions are called from WinMessage (located in the iFrame). They call fnYes3(), fnNo3() in PARENT page.
function fnCancel2(val) {
    //alert("135 Utils fnCancel2 val = " + val);
    document.getElementById("ifrmPopupMsg").style.display = "none"; 
} 

function fnOK2(val) 
{ 
    //11/29/08 Also called from fnPopupMsg2:
    //07/26/08 Revised. try/catch because tbxPopupMsg currently only on _OrderEntry pages:
    //alert("140 Utils fnOK2 val = " + val);
    
    var pos=val.indexOf(":",0);
    var sMsgCode="0";
    if (pos>-1)
    {
        //Must have a code:
        sMsgCode=val.substr(0,pos);
    
    } 
    //alert("219 Utils sMsgCode = " + sMsgCode);
    var which=parseInt(sMsgCode,10)>=30?"2":"";
    document.getElementById("ifrmPopupMsg" + which).style.display="none";
    try
    { 
        //07/28/08 _OrderEntry pages pages only:
        document.getElementById("tbxPopupMsg" + which).value=""; 
        fnSetFocus();
    }
    catch(e) {}

    try {
        parent.fnOK3(val);  //Needed for DragNDrop to restart document.onmousedown = ......:
    }
    catch (e) { }
}


function fnYes2(val) 
{
    //11/29/08 Also called from fnPopupMsg2:
    //alert("108 Utils fnYes2 val = " + val);
    var pos=val.indexOf(":",0);
    var sMsgCode="0";
    if (pos>-1)
    {
        //Must have a code:
        sMsgCode=val.substr(0,pos);
    
    } 
    var which=parseInt(sMsgCode,10)>=30?"2":"";
    try
    {
        parent.fnYes3(val);
    }
    catch(e) {}
    document.getElementById("ifrmPopupMsg" + which).style.display="none"; 
}


function fnNo2(val) 
{
    //11/29/08 Also called from fnPopupMsg2:
    //alert("121 Utils fnNo2 val = " + val);
    //04/19/07 parent.fnNo3() now used in Master/CallList_OrderEntry.
    //try/catch to override all places currently used.
    var pos=val.indexOf(":",0);
    var sMsgCode="0";
    if (pos>-1)
    {
        //Must have a code:
        sMsgCode=val.substr(0,pos);
    
    } 
    var which=parseInt(sMsgCode,10)>=30?"2":"";
    try
    {
        parent.fnNo3(val);
    }
    catch(e) 
    {
        //alert("104 Utils Catch");
    }  
    document.getElementById("ifrmPopupMsg" + which).style.display="none"; 
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////// END POPUPMSG ///////////////////////////////
//////////////////////////////////////////////////////////////////////////////



function fnValPrice(thisId)
{
    //Called from BreadItems.ascx:
    //12/06/06 Added validation - validates and rounds off:
    //alert("145 Utils.js fnValPrice thisId = " + thisId); 
    var myCharge=document.getElementById(thisId).value; 
    //alert("147 myCharge = " + myCharge); 
    if (isNaN(myCharge)==true)
    {
        alert("Illegal entry - " + myCharge);
        return;
    }   
    document.getElementById(thisId).value=fnMyRound2(myCharge,2); 
}


function fnMyRound2(val,dp)
{
	//06/14/09 Added,10 to parseFloat() - 3 instances:
	//12/06/06 Exactly the same as (copied) Cusanos.fnMyRound().
	//cusanos.js also requires fnMyRound().
	//db NEEDS TO BE NUMERIC NOT A STRING:
	//RETURNS A STRING
	//alert("275 Utils: fnMyRound2(" + val + ", " + dp + ")");
	//Parameter dp: round out val to dp decimal places:
	
	//Allow for val being wiped out:
	if (val.toString()=="") 
	{
		return "";
	}	
	
	var newVal=parseFloat(val,10);	//Convert to numeric so that number gets normalized
	var newVal2=0;
	var pad="000000";
	var dec=Math.pow(10,dp);
	//Interesting situation: Always round up Ok with +ve numbers. -ve numbers happen when 
	//	decrementing Qty in which case number rounds down! So, if -ve number
	var roundUpDn=((newVal>=0)?+1:-1);	//09/23/04 >= was >
	//alert("roundUpDn = " + roundUpDn);
	
	newVal=newVal.toString();		//Convert back to string:
	//alert("294 newVal = " + newVal);
	
	//Determine if whole number, or not: 
	var pos= newVal.indexOf(".",0);
	//alert("300 pos = " + pos);
	if (pos>-1)
	{
		//Is not a whole number:
		//Round up to dp places:
		newVal2=(parseFloat(newVal,10)*dec) + 0.5*roundUpDn;		//For testing:
		//alert("304 newVal2 round A3 = " + newVal2);
		newVal2=((parseFloat(newVal,10)*dec + (0.5*roundUpDn))/dec).toString();
		//alert("306 newVal2 = " + newVal2);
		
		////////////////////////////////////////////////////
		//NOTE: Cusanos.fnMyRound() was corrected on 12/14/06: 
		//01/16/10 CORRECTION! 16.990 becomes 1699.499 BUT 16.995 becomes 17 - no decimal point!!! So .... 
		if (newVal2.indexOf(".",0)==-1) {newVal2+=".";}
		////////////////////////////////////////////////////
		
		newVal2 += "000000";	//Pad incase not enough digits to trim to dp places:
		//alert("308 newVal2 = " + newVal2);
		//Truncate: If dp==0, not decimal point either:
		//alert("312 newVal2.substr(0,pos) = " + newVal2.substr(0,pos) + ", newVal2.substr(pos,dp+1)=" + newVal2.substr(pos,dp+1));
		newVal2=newVal2.substr(0,pos) + ((dp>0)?newVal2.substr(pos,dp+1):"");
		//alert("311 newVal2 = " + newVal2);
		newVal=newVal2;	//newVal2 is only used for manipulation. Revert to newVal:
		//alert("313 newVal2 = " + newVal2);
	}
	else
	{
		//Whole number:
		if (dp>0)
		{
			//alert("pad.substr(0," + (dp+1) + ") = " + pad.substr(0,dp+1));
			//newVal2=newVal;
			//alert("newVal2 1 = " + newVal2);
			//07/28/04 Found error - added decimal point when newVal is a whole number!
			var ert=pad.substr(0,(dp+1));
			newVal += "." + pad.substr(0,dp);	
		}
		
	}
	//parseFloat removes extra 0s, so return a string:
	//alert("332 fnMyRound2 newVal returned = " + newVal);
	return newVal;
}


function fnGetFocusYPos(thisId)
{
    //08/21/07 Looks like thisId is never used!
    //08/21/07 FOR THIS TO WORK SMARTNAVIGATION HAS TO BE FALSE: 
    //01/22/07 Common code for report aspx pages where pdf is displayed in an iframe: 
    //alert("334 fnGetFocusYPos thisId = " + thisId);
    //alert("332 Utils document.getElementById('tbxYPos').value = " + document.getElementById("tbxYPos").value); 
    if (document.getElementById("tbxYPos").value.length==2)       //Should be lockControls + ":" 
    {  
        //alert("338 - before fnGetOffset(thisId)"); 
        var XYPos=fnGetOffset(thisId);
        
        var YPos=XYPos.Y; 
        clientHeight=document.body.clientHeight;
        //alert("340 clientHeight = " + clientHeight + "\nYPos = " + YPos);
        try 
        { 
            var temp = document.getElementById("tbxYPos").value;
//            temp=100;
//            YPos=100;
//            clientHeight=100;
            //alert("274 temp = " + temp + ", YPos = " + YPos + ", clientHeight = " + clientHeight); 
            
            //Concatenate YPos, clientHeight to LockControls (which was passed in QueryString): 
            document.getElementById("tbxYPos").value = temp + YPos + "*" + clientHeight;
            //alert("354 utils.js: document.getElementById('tbxYPos').value = " + document.getElementById("tbxYPos").value); 
        }
        catch(e) 
        {}
    }  
    else
    {
        //alert("361 Utils focus"); 
        //Focus on tbxYPos:
        //05/07/07 Override to stop repositioning on autopostback of txtEventDate# or ddls: 
        //  Need to use try/catch to handle reports that don't have tbxFiredBy:
        //   REFER Customer Invoice Summary for template usage:   
        var sFiredBy="1"
        try
        {
            sFiredBy=document.getElementById("tbxFiredBy").value;
        }
        catch(e) {} 
        //alert("372 Utils sFiredBy = " + sFiredBy);
        if (sFiredBy=="1") 
        { 
            //12/27/08 This has never been used.
            //  document.getElementById("tbxYPos").value.substr(0,1) contains parameter passed from Reports.aspx:
            //  Parameter is always 0:
            
            //alert("379 Utils Before set focus");
            var sLockControls = document.getElementById("tbxYPos").value.substr(0, 1);
            //alert("380 fnGetFocusYPos Before focus");
            if (sLockControls != "1") {
                //alert("381 fnGetFocusYPos Before focus");
                document.getElementById("tbxYPos").focus(); 
            }
            //alert("382 fnGetFocusYPos After focus");
        } 
        //alert("383 Utils End - after focus");
    }
    //alert("390 End fnGetFocusYPos");  
}  
   
   
function fnGetOffset(thisId)
{
    //alert("309 fnGetOffset thisId = " + thisId);
    //04/05/2011 Also return height of thisId. First used to position DatePickerGR under its calling textbox (tbxName):
    //02/01/07 If value for Y and resulting clientHeight is way off, check page header: HTML, HEAD, DocType statements and tags:
    //01/22/07 Was in Cusanos.js. Moved here to use it from Report aspx pages: 
    //08/10/06 Returns X,Y positions for thisId:
    //alert("320 fnGetOffset thisId = " + thisId);  
    
    var offsetTrail=document.getElementById(thisId);
    //alert("316 offsetTrail = " + offsetTrail);

    //04/26/10 Added new. Width of element thisId.
    //  First used in DragNDrop. Could have been useful other places.
    //  Get thisIdWidth first, before value of offsetTrail is changed in loop.
    //  Try/Catch for safety:
    var thisIdWidth = 0;
    var thisIdHeight = 0;   //04/05/2011 new:
    try {
        thisIdWidth = offsetTrail.offsetWidth;
        thisIdHeight = offsetTrail.offsetHeight;
    }
    catch (e) { }
    
    var offsetLeft=0;
    var offsetTop=0;
    winHeight=0;
    var elementHeight = 0;
    var sMsg = "";
    while (offsetTrail)
    {
        offsetLeft += offsetTrail.offsetLeft;
        offsetTop += offsetTrail.offsetTop;
        offsetTrail = offsetTrail.offsetParent;
        //alert("325 fnGetOffset thisId = " + thisId + ", offsetLeft = " + offsetLeft + ", offsetTop = " + offsetTop);
        //sMsg +="\n325 fnGetOffset thisId = " + thisId + ", offsetLeft = " + offsetLeft + ", offsetTop = " + offsetTop;
    }

    if ((navigator.userAgent.indexOf("Mac") !=-1) && (typeof document.body.leftMargin != "undefined"))
    {
        //alert("fnGetOffset - Mac only presumably:"); 
        offsetLeft += document.body.leftMargin;
        offsetTop=document.body.topMargin;
    }
//    if (thisId.indexOf("tbxItemID",0)>-1) {
//        alert(thisId + "\n" + sMsg);
    //    }

    //alert("330 fnGetOffset thisIdHeight (" + thisId + ") = " + thisIdHeight);
    return { X: offsetLeft, Y: offsetTop, W: thisIdWidth, H: thisIdHeight }; 
} 





///////////////////////////////////////////////////////////////////////
/////////////////// DIALER RELATED FUNCTIONS //////////////////
///////////////////////////////////////////////////////////////////////
function fnDialer(which)
{
        //which: 1 - customer, 2 - driver, 10/07/08 Added Customer.tbxAPPhNo -3: 
        //alert("314 fnDialer which = " + which);  
        document.getElementById("tbxCallWhich").value = which;

        //1.  Build Array:
        var sPhone=fnGetPhone(which);
        if (sPhone=="")
        {
            fnWriteMsg("Require a phone number",0);
            return; 
        } 
        var sPhoneExt=document.getElementById("tbxLoggedUserOfficePhoneExt").value;
        if (sPhoneExt=="")
        {
            fnWriteMsg("Require user's phone Ext.",0);
            return; 
        } 
        
        var aryDialer = new Array();
        aryDialer[0]= sPhone;
        aryDialer[1]=sPhoneExt 

        //2.  Build Attribs:
        var XY=fnGetOffset("Master_menu1_btnMenu_Customer"); 
        var XPos=XY.X; 
        var YPos=XY.Y; 
        YPos +=80; 
        //alert("247 btnCustomer XPos = " + XPos + " YPos= " + YPos); 
        XPos=0;     //03/15/07 Micheal requests:
        YPos=0; 
        var attribs="unadorned:no; resizable:yes; status:no; scroll:no; dialogLeft:0px; dialogTop:" + YPos.toString() + "px;dialogHeight:100px; dialogWidth:360px"; 
        //alert("354 attribs = " + attribs);
      
        //3.   Open window: 
        window.showModelessDialog("etQuickDial.htm",aryDialer,attribs);
 
}


function fnGetPhone(which)
{
    //10/07/08 Added Cusomer.tbxAPPhNo (which="3"): 
    //Find out which phone number to get from parent page:
    //alert("281 " + location.href);
    //var pos=location.href.indexOf("?",0);
    var whichNumber=document.getElementById("tbxCallWhich").value;
    //alert("368 whichNumber = " + whichNumber);
    var myPhone="";
    var myPhoneLongDistance="";

    //whichNumber=location.href.substr(pos+1);
    //Currently calling only two numbers so.....
    switch (whichNumber)
    {
        case "1":
        {
            //alert("Customer");
            //01/02/09 Modified. Added ddl to provide phone option: 
            //01/21/09 ddlPhone is on _Order_Entry screens only. tbxPhone is on Customer.aspx:
            //  HAS TO BE IN THIS ORDER BECAUSE THERE IS STILL A tbxPhone ON THE Master_OrderEntry page:
            try
            {
                var index=document.getElementById("ddlPhone").selectedIndex;
                myPhone=document.getElementById("ddlPhone").options[index].value; 
                myPhoneLongDistance=document.getElementById("cbxLongDistance").checked;
            }
            catch(e)
            {
                myPhone=document.getElementById("tbxPhone").value; 
                myPhoneLongDistance=document.getElementById("cbxLongDistance").checked;
            }
            break;  
        }
        case "2":
        {
            //alert("Driver");
            myPhone=document.getElementById("tbxDeliveredByWhomPhCell").value;
            myPhoneLongDistance=document.getElementById("cbxLongDistanceDriver").checked;
            break;  
        }
        case "3":
        {
            //alert("CustMaster.tbxAPPhNo");
            myPhone=document.getElementById("tbxAPPhNo").value;
            myPhoneLongDistance=false;  //document.getElementById("cbxLongDistanceDriver").checked;
            break;  
        }  
    }
    
    //alert("304 myPhone = " + myPhone + ", myPhoneLongDistance = " + myPhoneLongDistance);
    if (myPhone == "")
   {
        return myPhone; 
   } 

    myPhone=myPhone.replace(/-/g,"");
    myPhone=myPhone.replace(/ /g,"");     //In case
   
    //Add 9 and possibly 1: 
    //alert("43 myPhone = " + myPhone);
     myPhone="9" + ((myPhoneLongDistance==true)?"1":"") + myPhone;
    //alert("317 myPhone = " + myPhone);
   
    /////////////////////////////////////////////////// 

    if (location.href.indexOf("CusanosVS200",0)>-1)
    {
        myPhone="9545684387";    //FOR TESTING:
    }
    /////////////////////////////////////////////////// 
    //alert("326 myPhone = " + myPhone); 

    return myPhone;
}

///////////////////////////////////////////////////////////////////////
/////////////// END DIALER RELATED FUNCTIONS //////////////////
///////////////////////////////////////////////////////////////////////


function isNaD(val)
{
    //10/30/07 Corrected javascript error with parseInt()
    //02/14/07 Date validation:
    //alert("407 Utils isNaD val = " + val); 
    
    //val: mm/dd/yyyy or m/d/yy etc: 
    var aDt = val.split("/");
    //alert("402 aDt = " + aDt); 
    var daysInMonth=31; 
    if ( aDt && (aDt.length == 3) ) 
    { 
        //dt = new Date(parseInt(aDt[2],10),parseInt(aDt[0],10)-1,parseInt(aDt[1],10)); 
        if ( (isNaN(aDt[0])==true) || isNaN(aDt[1])==true || (isNaN(aDt[2])==true) || ( (aDt[2].length!=2)  && (aDt[2].length!=4))  ) {return true;}

        //Months:
        if ( (parseInt(aDt[0],10) < 0) || (parseInt(aDt[0],10) >12) ) 
        {
            return true;
        }
        else
        {
            //31 day Months:
            var sMonths31=",1,3,5,7,8,10,12," 
           //12/04/2007 Correction: 
            if (sMonths31.indexOf(","+ (parseInt(aDt[0],10)).toString() + ",",0)>-1)
            {
                //alert("261 31 day month"); 
                //Is is 31 day month:
            } 
            else
            {
                //All the rest are 30 day months, except February, which is 28 days, except for leap year:
               //alert("267 30 day month - provisional"); 
                daysInMonth=30;
                if (aDt[0]==2) 
                {
                    //February:
                    //alert("271 Feb");
                    daysInMonth=28;
                    //Year in two digits (08) OK for calculating % as 4 digits (2008):
                    var remainder = parseInt(aDt[2],10) % 4;
                    //alert("274 remainder = " + remainder);
                    if ( remainder == 0)
                    {
                       //alert("275 leap year");
                        //Is a leap year:
                        daysInMonth=29; 
                    }    
                } 
            }
            var days=parseInt(aDt[1],10);
            //alert("449 aDt[1] = " + aDt[1].toString() + ", days = " + days + ", daysInMonth = " + daysInMonth); 
            if ( (days <1) || (days>daysInMonth) ) 
            {
                return true;
            }
            else
            {
                return false;        //OK good date:
            }  
        }    
    } 
    else
    {
        return true;       
    }
}
       


function fnOpenDatePickerGR(val)
{
    //alert("478 Utils fnOpenDatePickerGR val = " + val); 
    /*
    Calls AND fires DatePickerGR.htm:
    10/08/08 Revised for use from datagrid. Can now be used for ANY textbox instead of just txtEventDate#:
 
    val: ORIGINAL use - INDEX of associated TextBox txtEventDate# i.e. fnOpenDatePickerGR('#')
        For txtEventDate: fnOpenDatePickerGR(''):
        Otherwise txtEventDate#: fnOpenDatePickerGR('#'):
        Option to select dates in future only fnOpenDatePickerGR('#+'):

    10/09/08 EXTENDED use:
    val will now be 3 parameters concatenated together : delimited.
  
    Associated text box name (Required): i.e. txtEventDate# or DGFlourContract__ctl#_tbxContractDate" 
    Future Dates Only (Optional): '+' or nothing:
    Function Name to run after date has been dumped into tbxName (Optional): 
    
    For example sParams would be DGFlourContract__ctl#_tbxContractDate::
    or DGFlourContract__ctl#_tbxContractDate:+:fnNameToCall  
    (05/21/09 FUNCTION NAME - NO PARENTHESIS. CANNOT INCLUDE PARAMETERS. IF PARAMETERS NEEDED THEN HAVE TO CALL UNIQUE FUNCTION. COULD BE PROBLEM FROM THE LIKES OF A DATAGRID*):   
    * 04/25/2011 Get around this by writing thisId to a hidden textbox tbxThisIdRef. Refer to tbxThisIdRef from the function:
    
    sParams will be built up from val (whether for original use or extended use), 
    converted and written into DataPickerGR.tbxParams (textbox):
    
    NOTE was writing sParams into txtEventDateGR. But the oDatePicker class uses txtEventDateGR for its own purposes.
    Now that 3 params are being passed, clearer to add another text box tbxParams:
    */

    /////////////////////////////////////////////
    //Analyse val:
    var XY="";
    var pos=val.indexOf(":",0);
    //var tbxName=""  //04/03/2011 Made public so as to be able to set ReadOnly while DatePicker is open:
    var sFutureDateOnly="";
    var sFunctionToCall="";
    var numDates="";
    sParams="";
    if (pos==-1)
    {
        //ORIGINAL val cannot have any :s in it:
        numDates=val;   //Default:
        pos=val.indexOf("+",0);
        if (pos>-1)
        {
            numDates = val.substr(0,pos);
            sFutureDateOnly="+";
        }
        //alert("592 numDates = " + numDates);
        tbxName = "txtEventDate" + numDates;
        sParams=tbxName + ":" + sFutureDateOnly + ":";
        
    }
    else
    {
        //EXTENDED use. Get text box name from val - for calendar location on screen:
        tbxName = val.substr(0, pos);
        //alert("600 tbxName = " + tbxName);
        sParams=val;
    }
    
    //04/25/2011 Added for use in datagrids. First used: DaysRestrictedOrdering:
    try {
        document.getElementById("tbxThisIdRef").value = tbxName;
    }
    catch (e) { }
    
    //alert("699 tbxName = " + tbxName + ", sParams = " + sParams);
    //alert("700 tbxName height = " + document.getElementById(tbxName).style.height);
    ////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////
    ///////////////// CLOSE - if already open ////////////// 
    ////////////////////////////////////////////////////////
    //alert("659 document.getElementById('ifrmDatePickerGR').style.height = " + document.getElementById("ifrmDatePickerGR").style.height); 
    if (parseInt(document.getElementById("ifrmDatePickerGR").style.height, 10) > 0) {
        //Close it:
        fnUnLoadDatePickerGR();
        return;
    }
    ////////////////////////////////////////////////////////


    //CONTINUE:
    ////////////////////////////////////////////
    ////////////////////////////////////////////
    //03/19/10 Problem here with ../Reports:
    //1. WRITE sParams into DatePickerGR.tbxParams:
    window.frames["ifrmDatePickerGR"].document.getElementById("tbxParams").value = sParams;
    //alert(window.frames["ifrmDatePickerGR"].document.getElementById("tbxParams").value);  
    ////////////////////////////////////////////////////////////
    //alert("600");
    
    /////////////////////////////////////////////
    /////////////////////////////////////////////
    //2. LOCATE calendar on screen:       
    XY=fnGetOffset(tbxName); 
    var XPos=XY.X;
    var YPos = XY.Y;
    var WPos = XY.W
    var HPos = XY.H; ;
    //alert("612 Utils XPos= " + XPos + " YPos= " + YPos + " WPos= " + WPos + " HPos= " + HPos); 
    
    //Get screen dimensions:
    var scrWidth=screen.availWidth;
    var scrHeight=screen.availHeight;
    var clientWidth = document.body.clientWidth;   //Allows for existence of scrollbars presumably:
    var clientHeight = document.body.clientHeight;
    //alert("621 scrWidth = " + scrWidth + "\ndocument.body.clientWidth = " + document.body.clientWidth + "\ndocument.documentElement.clientWidth = " +document.documentElement.clientWidth);

    //04/05/2011 There is an issue here. MOST iframe ifrmDatePickerGR do NOT have frameborder="0". (Master_OrderEntry DOES).
    //  WITHOUT frameborder="0", there is a border on the iframe. So, need to accomodate it (unfortunate):

    ////////////////////////////////////////////////
    //04/06/2011 iframe dimensions are different with IE, Safari and Firefox. So have to find out which browser is being used:
    var sBrowserName = fnGetBrowserName();
    //alert("625 Utils sBrowserName = " + sBrowserName);
    var isIE = false;
    /////////////////////////////////////////////////

    var popupWidth = (sBrowserName == "IE") ? 256 : 252;   //IE: 250 (good for frameborder=0). Originally 255;
    var popupHeight = (sBrowserName == "IE") ? 198 : (sBrowserName == "Safari")?196:192;  //IE: 178 (good for frameborder=0). Originally 169;
    //alert("670 sBrowserName = " + sBrowserName + "\npopupHeight = " + popupHeight.toString());
    
    var adjustVert = 3;     //04/05/2011 Allow 3px vertical spacing below tbxName (tbxName height now calculated in fnGetOffSet()):
    var adjustHorizontal = 0;   //04/05/2011 Was 1;
    XPos+=adjustHorizontal;
            
    //10/08/08 XPos, YPos could place calendar too low or too far to right on screen.
    //   So place above or to 'left' of date control (tbxName). Therefore get tbxName width and height:
    //  NOTE: To read width and height values for tbxName, they have to be specified in the HTML tag
    //  This was never considerd with original val:
    
    var sTemp=document.getElementById(tbxName).style.width;     //Width:
    if (sTemp=="") {sTemp="0";}
    sTemp=sTemp.replace(/px/g,"");
    var iTextBoxWidth=parseInt(sTemp,10);
    
//////    sTemp=document.getElementById(tbxName).style.height;     //Height:
//////    if (sTemp=="") {sTemp="0";}
//////    sTemp=sTemp.replace(/px/g,"");
    var iTextBoxHeight=parseInt(HPos,10);
    //if(iTextBoxHeight==0) {iTextBoxHeight=22;}      //Default height for tbxName:

    //alert("628 iTextBoxWidth=" + iTextBoxWidth + ", iTextBoxHeight = " + iTextBoxHeight);
    
    
    if ( (XPos+popupWidth)>clientWidth)
    {
        //Place to 'left' of date control:
        XPos-=(popupWidth-iTextBoxWidth); 
    }
    
    YPos += (iTextBoxHeight+adjustVert); //Default to below text box control (tbxName):
    if ( (YPos+popupHeight)>clientHeight)
    {
        //Place above date control:
        YPos-=(popupHeight+iTextBoxHeight); 
    }
    //alert("752 Utils ADJUSTED XPos= " + XPos + " YPos= " + YPos + ", scrollTop = " + document.body.scrollTop + ", scrollWidth = " + document.body.scrollWidth);

    //04/03/2011 NOTE: HTML iframe defined as:
    //  src="DatePickerGR.htm"  scrolling="no" marginheight="0" frameborder="0" marginwidth="0" style="position:absolute;height:0;width:0; z-index:10"
    //  Means that size, width have to be redefined here:
    
    //Set size, position of iframe:  
    //  scrollTop not required here. Always reads current scrTop:
    var scrLeft=XPos;  
    var scrTop=YPos;  
    document.getElementById("ifrmDatePickerGR").style.left=(scrLeft);
    document.getElementById("ifrmDatePickerGR").style.top=(scrTop);
    document.getElementById("ifrmDatePickerGR").style.width=popupWidth;
    document.getElementById("ifrmDatePickerGR").style.height = popupHeight;

    //04/05/2011 None of thse work. Controlled by <iframe framborder="0" (html):
    //document.getElementById("ifrmDatePickerGR").style.backgroundColor = "red";    //Doesn't work!
    //document.getElementById("ifrmDatePickerGR").style.borderColor = "transparent";
    //document.getElementById("ifrmDatePickerGR").style.frameBorder = "5";
    //document.getElementById("ifrmDatePickerGR").style.border = "none";
    //////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    //3. 04/03/2011 DISABLE (Set Readonly=true) while DatePicker is open:
    blnDisabled = document.getElementById(tbxName).disabled;
    blnReadOnly = document.getElementById(tbxName).readOnly;
    if (blnDisabled != true) {
        if (blnReadOnly != true) {
            document.getElementById(tbxName).readOnly = true;
            blnResetReadOnly = true;    //Public variable:
        }
    }
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    //4. FIRE DatePickerGR.htm:
    //alert("340 Utils");
    window.frames["ifrmDatePickerGR"].document.getElementById("Button1").click();
    
    //04/06/2011 Tried this. IE objects. Forget it!
    //window.frames["ifrmDatePickerGR"].document.showDP();
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////

    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    //4. DISPLAY calendar: 
    document.getElementById("ifrmDatePickerGR").style.display="";  
    //alert("350 Utils.js After ifrmDatePickerGR displayed");
    ////////////////////////////////////////////////////
    //////////////////////////////////////////////////// 
    
}


function fnUnLoadDatePickerGR()
{
    //Use height to signal toggling in fnLoadDatePickerGR:
    document.getElementById("ifrmDatePickerGR").style.display="none";
    document.getElementById("ifrmDatePickerGR").style.height=0; 
    //alert("440 document.getElementById('ifrmDatePickerGR').style.display = " + document.getElementById("ifrmDatePickerGR").style.display);

    //04/03/2011 Reset tbxName.ReadOnly;
    //blnResetReadOnly is public variable:
    //alert("450 tbxName = " + tbxName + ", blnResetReadOnly = " + blnResetReadOnly);
    if (blnResetReadOnly == true) {
        document.getElementById(tbxName).readOnly = false;
        blnResetReadOnly = false;
    }
} 
       

function fnGetPublicHolidays(mMonth, mYear)
{
    //alert("4550 Utils fnGetPublicHolidays mMonth = " + mMonth + ", mYear = " + mYear); 
    /* 03/24/07 Pass in Month, Date off calendar:

   // Returns string of dates coresponding date. i.e. 7,25. The most for a given month is 2. Least is 0: 
    January: 
        New Years Day: 01/01/mYear:
        Martin Luther King: 3rd Monday Jan
    February:
       Valentines Day: 2/14/mYear 
    March:
       St. Patrick's Day: 3/17/mYear 
    April:
       Earth Day: 4/22/mYear     
    May:
        Mother's Day 2nd Sunday May 
        Memorial Day Last Monday in May 
    June:
       Fathers Day 3rd Sunday June 
    July:
       Independence Day July 4 mYear 
    August:
    September:
       Labor Day: First Monday in September  
    October:
       Halloween 10/31/mYear 
    November:
        Veterans Day: Nov 11th mYear
        Thanksgiving Day: 4th Thursday November 
    December:
       Christmas Day: 12/25/mYear 
       
    Easter/Jewish: (Refer: Misc/EasterJewish032807.txt) Add to arrays:
    "2008:3/23","2010:3/30","2013:3/26","2013:3/31","2016:3/27","2024:3/31","2027:3/28"
    "2007:4/8","2008:4/20","2009:4/9","2009:4/12","2010:4/4","2011:4/19","2011:4/24","2012:4/7","2012:4/8","2014:4/15","2014:4/20","2015:4/4","2015:4/5","2016:4/23","2017:4/11","2017:4/16","2018:4/1","2019:4/21","2020:4/12","2021:4/4","2022:4/17","2023:4/9","2025:4/20","2026:4/5","2028:4/16","2029:4/1

    "2007:9/13","2007:9/22","2008:9/30","2009:9/19","2009:9/28","2010:9/9","2010:9/18","2011:9/29","2012:9/17","2012:9/26","2013:9/5","2013:9/14","2014:9/25","2015:9/14","2015:9/23","2017:9/21","2017:9/30"
    "2008:10/9","2011:10/8,2014:10/4","2016:10/3","2016:10/12" 
   
    Easter Orthodox: 3/14/08 http://users.sa.chariot.net.au/~gmarts/eastcalc.htm
    2007:4/8,2008:3/23,2009:4/12,2010:4/4,2011:4/24,2012:4/8,2013:3/31,2014:4/20,2015:4/5,2016:3/27,2017:4/16,2018:4/1,2019:4/21,2020:4/12,2021:4/4,2022:4/17,2023:4/9,2024:3/31,2025:4/20,2026:4/5,2027:3/28,2028:3/16, 2029:4/1,2030:4/21
    
   
    Chanukah:
    "2013:11/28"
    "2007:12/5","2008:12/22","2009:12/12","2010:12/2","2011:12/21","2012:12/9","2014:12/17","2015:12/7","2016:12/25","2017:12/13"
    */
     
    //Formats: Type 1: i.e. '12/25'. Type 2: 03Sun: 3rd Sun of specified month. Exception Last: 
    var aryHolsInMonth = new Array();
    aryHolsInMonth[0]=new Array("01/01","03Mon");           //Jan
    aryHolsInMonth[1]=new Array("02/14");                       //Feb
    aryHolsInMonth[2]=new Array("03/17","2008:3/23","2010:3/30","2013:3/26","2013:3/31","2016:3/27","2024:3/31","2027:3/28");                       //Mar
    aryHolsInMonth[3]=new Array("04/22","2007:4/8","2008:4/20","2009:4/9","2009:4/12","2010:4/4","2011:4/19","2011:4/24","2012:4/7","2012:4/8","2014:4/15","2014:4/20","2015:4/4","2015:4/5","2016:4/23","2017:4/11","2017:4/16","2018:4/1","2019:4/21","2020:4/12","2021:4/4","2022:4/17","2023:4/9","2025:4/20","2026:4/5","2028:4/16","2029:4/1");                       //Apr
    aryHolsInMonth[4]=new Array("02Sun","LastMon");        //May
    aryHolsInMonth[5]=new Array("03Sun");                      //Jun
    aryHolsInMonth[6]=new Array("07/04");                       //Jul
    aryHolsInMonth[7]=new Array("");                               //Aug
    aryHolsInMonth[8]=new Array("01Mon","2007:9/13","2007:9/22","2008:9/30","2009:9/19","2009:9/28","2010:9/9","2010:9/18","2011:9/29","2012:9/17","2012:9/26","2013:9/5","2013:9/14","2014:9/25","2015:9/14","2015:9/23","2017:9/21","2017:9/30");                      //Sep 
    aryHolsInMonth[9]=new Array("10/31","2008:10/9","2011:10/8,2014:10/4","2016:10/3","2016:10/12");                       //Oct  
    aryHolsInMonth[10]=new Array("11/11","04Thu","2013:11/28");          //Nov
    aryHolsInMonth[11]=new Array("12/25","2007:12/5","2008:12/22","2009:12/12","2010:12/2","2011:12/21","2012:12/9","2014:12/17","2015:12/7","2016:12/25","2017:12/13");                     //Dec   

    //Create two dim array:      
    var aryHolsInCurMonth = new Array();
    aryHolsInCurMonth=aryHolsInMonth[mMonth];                   //Working:
    //alert("507 aryHolsInMonth[mMonth] = " + aryHolsInMonth[mMonth]); 
    var aryDayNames = new Array("Sun","Mon","Tue" ,"Wed","Thu","Fri","Sat");
    var dow = 0;  
   
    var dDateFirstOfMonth = new Date((mMonth + 1).toString() + "/01/" + mYear);
    var iDayFirstOfMonth = dDateFirstOfMonth.getDay(); 
    //alert("513 dDateFirstOfMonth = " + dDateFirstOfMonth + "\niDayFirstOfMonth = " + iDayFirstOfMonth); 

    var sReturn="";
    blnLast=false; 
    //alert("510 aryHolsInCurMonth.length = " + aryHolsInCurMonth.length);  
    for (var i=0;i<aryHolsInCurMonth.length;i++)
    {
        //alert("514 aryHolsInCurMonth[" + i.toString() + "] = " + aryHolsInCurMonth[i]); 
        var sDate =aryHolsInCurMonth[i].toString();
        //alert("515 i = " + i + ", sDate = " + sDate);  
        var sDayName=""; 
        var sWhichDay=""; 
       
        
        //////////////////////////////////////////////////////////////
        //03/28/07 Easter, Jewish Holidays: Passover,  Rosh HaShanah, Yom Kippur:
        var pos=sDate.indexOf(":",0);
        if (pos>-1)
        {
            var sYear=sDate.substr(0,pos);
            var iYear = parseInt(sYear,10);
            //alert("620 iYear = " + iYear); 
            if (iYear>mYear) {break;}
            if (iYear==mYear)
            {
                sDate=sDate.substr(pos+1);
            }
            else
            {
                sDate=""; 
            }        
        } 
       
       
        //////////////////////////////////////////////////////////////
        if (sDate!="")
        { 
            pos=sDate.indexOf("/",0);
            if (pos>-1)
            {
                //First type - date known. i.e. 12/25/2007 - easy:
              sDate=sDate.substr(pos+1); 
              var iDate=parseInt(sDate,10);
              //alert("531 sDate = " + sDate + ", iDate = " + iDate);
              sReturn += "," + iDate.toString();  
            }
            else
            {
                //Second type:
                if  (sDate.length==5)
                {
                    //i.e. 04Mon - 4th Monday in month: 
                    sWhichDay=sDate.substr(0,2); 
                    sDayName=sDate.substr(2); 
                }
                else
                {
                    //i.e. LastMon. There is only one like this - Memorial Day:
                    blnLast=true; 
                    //alert("546 sDate = " + sDate); 
                    if (sDate.substr(0,4) == "Last") {sWhichDay="04";}         //Try it and see. May turn out to be 5 days in month: 
                    //alert("548 sWhichDay = " + sWhichDay);
                    sDayName=sDate.substr(4)        //i.e. Mon:  
                    //alert("550 sDayName = " + sDayName);  
                }
                //alert("553 sWhichDay = " + sWhichDay); 
                for (var j=0;j<aryDayNames.length;j++)
                {
                    //alert("556 aryDayNames[" + j + "] = " + aryDayNames[j].toString()); 
                    if (aryDayNames[j].toString()==sDayName) 
                    {
                         
                        dow=j;
                        //alert("560 dow = " + dow);
                        break;
                    }
                } 
                //alert("563 dow = " + dow);
                //Find first date having required day name. That will be the first instance: 
                var daysToDate=dow- iDayFirstOfMonth;
                //alert("566 daysToDate 1 = " + daysToDate); 
                if (daysToDate<0) {daysToDate+=7;}
                daysToDate+=1; 
                //alert("568 daysToDate 2 = " + daysToDate);  
              
                //Add weeks to increment to the required week of the month:
                //alert("571 parseInt(sWhichDay,10) = " + parseInt(sWhichDay,10)); 
                daysToDate += (parseInt(sWhichDay,10)-1)*7;
                //alert("573 daysToDate = " + daysToDate);
                
                if (blnLast==true)
                {
                    //alert("581 daysToDate = " + daysToDate); 
                    var dTemp = new Date((mMonth + 1) + "/" + daysToDate.toString() + "/" + mYear);
                    //alert("582 dTemp = " + dTemp); 
                    dTemp.setDate((dTemp.getDate() + 7));
                    //alert("584 dTemp = " + dTemp + "\ndTemp.getMonth() = " + dTemp.getMonth());   
                    if (dTemp.getMonth() == mMonth) 
                    {
                        //Turns out to be 5th dayName in month:
                        daysToDate += 7;
                        //alert("590 daysToDate = " + daysToDate); 
                    } 
                }
                //alert("593 ***** daysToDate = " + daysToDate);  
                sReturn += "," + daysToDate.toString(); 
           } 
           //alert("594 before end of loop");  
        } 
    }
    //alert("597 sReturn = " + sReturn);    
    return sReturn; 
}


function fnGetIdIndex(thisId)
{
    //08/10/07 Useful for several places?
   var pos=thisId.indexOf("ctl",0); 
   var pos2=thisId.indexOf("_",pos+1);
   var sIndex = thisId.substr(pos+3,pos2-pos-3);
   //alert("715 Utils sIndex = " + sIndex);
   return sIndex;
}


function fnConvertToDecimal(val)
{
	//10/30/07 Moved here from cusanos.js For use with CreditLimit.ascx:
	//alert("752 fnConvertToDecimal val = " + val);
	//sMsg+="\nfnConvertToDecimal val = " + val;
	var returnDecimal=val;
	if (returnDecimal=="") {returnDecimal="0";}
	
	//returnDecimal=fnReplace(returnDecimal,",","")        //05/20/07 Could have used .replace(/,/g,"")
	//10/31/7 is in cusanos.js - which is not linked here:
	returnDecimal=returnDecimal.replace(/,/g,"")
	//alert("758: returnDecimal = " + returnDecimal);
	if (returnDecimal.substr(0,1)=="$") {returnDecimal=returnDecimal.substr(1);}
	//alert("760: returnDecimal = " + returnDecimal);
	//04/05/04 Needs to return a decimal:
	//returnDecimal=parseFloat(returnDecimal);
	//alert("763: returnDecimal = " + returnDecimal);
	//sMsg+="\n764: returnDecimal = " + returnDecimal
	return returnDecimal;
}


function fnTrimB(val,all)
{
	//alert("fnTrimB");
	//02/16/08 Added additional value for all:
	//11/02/07 Moved here from cusanos.js
	//Remove trailing, leading spaces.
	//all: 1 reduce inner spaces to 1. 0: remove ALL inner spaces. 2: Leave internal spacing as is:
	//NOTE: When there is more then one space, they could be shown with &nbsp;!
	//	&nbsp; are removed with fnRemoveSpaceCode().
	var sMsg="";
	sMsg="1095: fnTrimB val = " + val + ", all = " + all;
	var newVal="";
	var newVal2="";
	var cnt=0;
	sMsg += "\n";
	var i = 0;
	var myCharCode = "";
	var myChar = "";
	if (val.length>0)
	{
		//11/04/04 Seems like Macs recognizes char(9) (horiz tab) and chr(13).
		//	So first convert all non-printable chars to spaces: 
		for (i=0;i<val.length;i++)
		{
			myCharCode=val.charCodeAt(i);
			myChar=val.charAt(i);
			//alert("myChar " + i + " = " + myChar);
			
			if (myCharCode<32)
			{
				sMsg+="1113: i = " + i + ", charAt = " + myCharCode + ", myChar = " + myChar; 
				//alert("i = " + i);
				newVal2+=" ";
				//alert("newVal 1 = " + newVal2);
			}
			else
			{
				newVal2+=myChar;
			}
		}
		//alert("821: " + newVal2);
		//Remove leading spaces:
		myCharCode = "";
		myChar = "";
		for (i=0;i<newVal2.length;i++)
		{
			myCharCode=newVal2.charCodeAt(i);
			myChar=newVal2.charAt(i);
			//alert("myChar " + i + " = " + myChar);
			
			if (myCharCode>32)
			{
				sMsg+="1113: i = " + i + ", charAt = " + myCharCode + ", myChar = " + myChar; 
				//alert("i = " + i);
				newVal=newVal2.substr(i);
				//alert("newVal 1 = " + newVal);
				break;
			}
		}
		//alert("fnTrimB newVal 1 = " + newVal);
		//Remove trailing spaces:
		sMsg+="1122\n";
		for (i=newVal.length-1;i>-1;i--)
		{
			myCharCode=newVal.charCodeAt(i);
			myChar=newVal.charAt(i);
			//alert("myChar " + i + " = " + myChar);
			sMsg+="i = " + i + ", charAt = " + myCharCode + ", myChar = " + myChar; 
			if (myCharCode>32)
			{
				sMsg+=": 1131: i = " + i + ", charAt = " + myCharCode + ", myChar = " + myChar;
				newVal2=newVal.substr(0,i+1);
				//alert("newVal2 2 = " + newVal2);
				break;
			}
		}
		//alert("854 fnTrimB newVal 2 = " + newVal2);
		//Depending on value of all, remove inner spaces:
		newVal=newVal2;     //02/16/08 default for all=2:
		if (all!=2)
		{
		    newVal="";
		    sMsg+="\n";

		    for (i=0;i<newVal2.length;i++)
		    {
			    myCharCode=newVal2.charCodeAt(i);
			    myChar=newVal2.charAt(i);
			    //alert("myChar " + i + " = " + myChar);
    			
			    if (myCharCode<33)
			    {
				    sMsg+="1149: i = " + i + ", charAt = " + myCharCode + ", myChar = " + myChar; 
				    cnt+=1;
				    //alert("myChar = " + myChar + ", all = " + all + ", cnt = " + cnt);
				    if ((cnt==1) & (all==1))
				    {
					    newVal+=myChar;
					    //alert("fnTrimB space addedfnTrimB  = " + newVal);
				    }
			    }
			    else
			    {
				    cnt=0;
				    newVal+=myChar;
			    }
		    }	
		}
	}
	sMsg+="\n1165: fnTrimB return = " + newVal;
	//alert(sMsg);
	//alert("888 fnTrimB newVal = " + newVal);
	return newVal;
}


function fnValRanges(sWhich, showOKMsg, from)
{
    //06/07/08 Use to also validate tbxSalesCommissionRanges:
    //11/10/07 from: 0- customer,1 - Setup:
    //11/01/07 New - Used in Setup.aspx, Customer.aspx:
    //alert("1091  fnValRanges"); 
   
    /*
    Parameters: 
    sWhich - TextBox being validated:
    showOKMsg: (integer) 1 yes, 0 no:
    from: (integer) 0 - customer,1 - Setup: 
    //Returns error message (sMsg) or 'OK': 
    */
     
    var i=0, j=-1;  
    var aryRows =new Array()
    var aryCols=new Array()
    ///////////var sVals =document.getElementById("tbxFuelSurcharge").value;
    var sVals =document.getElementById(sWhich).value; 
  
    //alert("890 sVals = " + sVals);
    if (sVals=="") {return "OK";}
     
    ////////////////////////////////////
    ////////////////////////////////////  
    var dp=0;
    var dpVal=0; 
    sMsgPrefix="";
    switch(sWhich)
    {
        case "tbxFuelSurcharge":
        {
            dp=0;
            dpVal=1; 
            sMsgPrefix="Delivery Charge" 
            break; 
        }
        case "tbxSalesCommissionRanges":
        {
            dp=2;
            dpVal=0.01; 
            sMsgPrefix="Sales Commission Ranges"  
            break; 
        }      
    }
    ////////////////////////////////////
    ////////////////////////////////////   
         
    sVals=fnTrimB(sVals,0); 
    //document.getElementById(sWhich).value=sVals; 
    aryRows= sVals.split(",");
    var cntRows=aryRows.length;
    //alert("897 aryRows = " + aryRows.toString() + "\ncntRows = " + cntRows.toString());  
    var pos=0, pos2=0;
    var sMinVal="", sMaxVal="", sMoney=""; 
    var sMsgError="", sMsgOK="";
    //alert("901 aryRows.length = " + aryRows.length);
   
    ///////////////////////////////////////////////////////////////
    //////////////////// VALIDATE ENTRIES ////////////////////
    /////////////////////////////////////////////////////////////// 
    for (i=0; i<aryRows.length; i++)
    {
        //alert("904 i = " + i.toString()); 
        pos=aryRows[i].toString().indexOf("-",0);
        pos2=aryRows[i].toString().indexOf("/",0);
        //alert("907 i = " + i.toString() + ", " + aryRows[i].toString() + ", pos = " + pos + ", pos2 = " + pos2);  
        if (i<(aryRows.length-1))
        {
            if ( (pos>-1) & (pos2>-1) )
            {
                //OK: 
                sMinVal=aryRows[i].toString().substr(0,pos);
                sMaxVal= aryRows[i].toString().substr(pos+1,pos2-pos-1);
                sMoney=aryRows[i].toString().substr(pos2+1);
                //alert("917 from = " + from + ", i = " + i + ", sMinVal = " + sMinVal + ", sMaxVal = " + sMaxVal + ", sMoney = " + sMoney + ", isNaN(sMoney) = " + isNaN(sMoney)); 
                if (isNaN(sMinVal)==true) {sMsgError += "," + sMinVal;}
                if (isNaN(sMaxVal)==true) {sMsgError += "," + sMaxVal;}
                if ( (isNaN(sMoney)==true) || ((parseFloat(sMoney,10)==0) & (from==1)) || (sMoney=="") ) {sMsgError += "," + sMoney;}
                if (sMsgError!="")
                {
                    sMsgError=sMsgError.substr(1) + ": illegal entries";
                    break;
                }
          
                j+=1; 
                aryCols[j]=sMinVal;
                j+=1; 
                aryCols[j]=sMaxVal; 
                j+=1; 
                aryCols[j]=sMoney;  
            }
            else
            {
                //Entry error: 
                //alert("936 i = " + i.toString() + ", pos = " + pos + ", pos2 = " + pos2); 
                sMsgError="Syntax error " + aryRows[i].toString();
                break; 
            }     
        }
        else
        {
            j+=1;
            aryCols[j]=aryRows[i].toString(); 
            sMoney=aryCols[j].toString();
           //alert("947 from = " + from + ", i = " + i + ", sMinVal = " + sMinVal + ", sMaxVal = " + sMaxVal + ", sMoney = " + sMoney + ", isNaN(sMoney) = " + isNaN(sMoney)); 
            if ( (isNaN(sMoney)==true) || ((parseFloat(sMoney,10)==0) & (from==1)) || (sMoney=="") ) 
            {
                sMsgError = sMoney + ": illegal entry";
                break;
            }
        }  
    }
    
    //alert("955 sMsgError = " + sMsgError); 
    if (sMsgError!="") 
    {
        //alert("958 before fnWriteMsg"); 
        //fnWriteMsg("Fuel Surcharge: " + sMsgError,0);
        return "Delivery Charge: " + sMsgError;
    }
   else
   {
        ///////////////////////////////////////////////////////////////
        /////////////// VALIDATE VALUES IN RANGE ///////////////
        ///////////////////////////////////////////////////////////////
        //alert("964 aryCols syntax, numbers OK = " + aryCols.toString());
        sMsgError=""; 
        var algo=-1; //As in algorithm:
        for (j=0;j<aryCols.length;j++)
        {
            if (j<(aryCols.length-1))
            {
                algo=(j-(parseInt((j/3),10)*3));
                //sMsgError += "," + j.toString() + ":" + (j-(parseInt((j/3),10)*3)).toString(); 

                switch (algo)
                {
                    case 0:
                    {
                        //Min: 
                        if (j==0)
                        {
                           if (parseFloat(aryCols[j].toString(),10)!=0)
                           {
                                sMsgError = "Ranges must start with 0";
                           }  
                        } 
                        else
                        {
                            //alert("1012 parseFloat(aryCols[j].toString(),10) - parseFloat(aryCols[j-2].toString(),10) = " + ( parseFloat(aryCols[j].toString(),10) - parseFloat(aryCols[j-2].toString(),10)) );
                            var diff=( parseFloat(aryCols[j].toString(),10) - parseFloat(aryCols[j-2].toString(),10))
                            diff=parseFloat(fnMyRound2(diff,dp),10);
                            //alert("1015 diff = " + diff);
                            
                            //if ( parseFloat(aryCols[j].toString(),10) - parseFloat(aryCols[j-2].toString(),10)!=0.01)
                            if ( diff!=dpVal)
                            {
                                sMsgError="Conflict - gap or overlap in range " + aryCols[j-2].toString() + ", " + aryCols[j].toString();
                            }
                        }
                        if (sMsgError=="") {sMsgOK += "\n" + aryCols[j].toString();}
                        break;
                    } 
                    case 1:
                    {
                        //Max:
                        if ( parseFloat(aryCols[j].toString(),10) <= parseFloat(aryCols[j-1].toString(),10))
                        {
                            sMsgError="Conflict2 " + aryCols[j-1].toString() + ", " + aryCols[j].toString();
                        } 
                        else
                        {
                            sMsgOK += " - " + aryCols[j].toString();
                        }   
                        break;
                    } 
                    case 2:
                    {
                        //$$:
                        //11/03/07 Don't want to validate 'decrementing' $ values:
                        if (j>4)
                        {
                            //alert("1014 parseFloat(aryCols[j].toString(),10) = " + parseFloat(aryCols[j].toString(),10) + ", parseFloat(aryCols[j-3].toString(),10) = " + parseFloat(aryCols[j-3].toString(),10) );
                            //if ( parseFloat(aryCols[j].toString()) >= parseFloat(aryCols[j-3].toString()) )
                            //{
                            //    sMsgError="Conflict3 " + aryCols[j-3].toString() + ", " + aryCols[j].toString();
                            //}
                        }
                        if (sMsgError=="")
                        {
                            sMsgOK += "  $" + aryCols[j].toString() + "%";
                        } 
                        break;
                    }
                } 
            } 
            else
            {
                //Last row. $$ only: 
                //alert("1031 parseFloat(aryCols[j].toString(),10) = " + parseFloat(aryCols[j].toString(),10) + ", parseFloat(aryCols[j-1].toString(),10) = " + parseFloat(aryCols[j-1].toString(),10) );
                sMsgOK += "\n$" + aryCols[j].toString() + "%";
            }
             
            if (sMsgError!="") 
            {
                break;
            } 
                
        }  //loop:
        
        ///////////////////////////////////////////////////////////////
        ////////////////////// MESSAGING //////////////////////////
        ///////////////////////////////////////////////////////////////
        //alert("1301 sMsgError = " + sMsgError);
        if (sMsgError!="") 
        {
            fnWriteMsg("1055: " + sMsgError,0);
            //alert("1056: " + sMsgError);  
            return sMsgPrefix + ": "  + sMsgError;
        } 
        else
        {
            document.getElementById(sWhich).value=sVals;
           //alert("1307 sMsgOK = " + sMsgOK);
            switch(sWhich)
            {
                case "tbxFuelSurcharge":
                {
                    sMsgOK=sMsgOK.replace(/\%/g,"");
                    break; 
                }
                case "tbxSalesCommissionRanges":
                {
                     sMsgOK=sMsgOK.replace(/\$/g,"");
                    break; 
                }      
            }
          
            //06/02/09 Abandoned. Trips user up. When Save button clicked, this may fire first and save doesn't get fired:
            //if (showOKMsg=="1") { alert(sMsgPrefix + " OK\n" + sMsgOK); }
            //alert("1062 sMsgOK before return OK = " + sMsgOK); 
            return "OK";             
        }  
    } 
}



function fnValNumber(thisId,dp)
{
    //alert("1575 fnValNumber thisId = " + thisId + ", dp = " + dp); 
        
    //04/06/09 Added +ve values only condition and option to show -ve msg.
    //  dp: '+2' STRING - value +ve values only, to two decimal places. Show -ve msg:
    //  dp: '++2' STRING - value +ve values only, convert to +ve value. Do not show -ve msg:
    //10/13/07 Shouldn't this be in Utils.js?
    //05/21/07 Generalized function to validate numbers and reset the dp dec places::

    var sVal=document.getElementById(thisId).value;
    
    //04/21/09 Correction. dp is also a numeric. Needs .toString():
    var dpLength=dp.toString().length;
    //alert("1572 Utils dpLength = " + dpLength);
    
    blnPositiveValuesOnly=false;
    blnShowNegMsg=true;
    
    switch (dpLength)
    {
        case 0:
        {
            dp=0;   //default:
            break;
        }
        case 1:
        {
            //No action:
            break;
        }
        case 2:
        {
            if (dp.substr(0,1)=="+")
            {
                 blnPositiveValuesOnly=true;
            }
            break;
        }
        default:
        {
            //dp length 3 or more. Not not likely to ever be more than 3:
            if (dp.substr(0,1)=="+")
            {
                 blnPositiveValuesOnly=true;
            }
            if (dp.substr(1,1)=="+")
            {
                blnShowNegMsg=false;
            }
            dp=dp.substr(2);
            break;
        }
    }
       
    //06/04/07: Commas cause 'Illegal entry. So: 
    sVal=sVal.replace(/,/g,"");
    sVal=sVal.replace(/ /g,"");      //11/06/07:
    if (isNaN(sVal)) 
    {
        alert("Illegal entry");
       	return false; 
    }
    else
    {
        if (parseFloat(sVal,10)<0)
        {
            if (blnPositiveValuesOnly==true)
            {
                if (blnShowNegMsg==true)
                {
                    alert("Positive values only");
                    return false;
                }
                else
                {
                    sVal=sVal.substr(1);    //Remove -ve sign:
                }
            }
        }
        
        //Format the number:
        
        //06/14/09 Correction. If dp is like ++2 it has to be a STRING. Even though ++ has been stripped off
        //  before getting to here, it is still a string! fnMyRound2() requires dp be a NUMBER:
        
        var dp2=parseInt(dp,10);
        //alert("1655 Utils sVal = " + sVal + ", dp2 = " + dp2);
        sVal=fnMyRound2(sVal,dp2); 
        //alert("1651 Utils sVal = " + sVal);
        document.getElementById(thisId).value=sVal;
        return true; 
   } 
}
	
	
function fnGetDateEndOfMonth(sDate)
{
    //sDate data type is a string:
   var dDate=new Date(sDate);
   
   //Get first date of month:
   var days=dDate.getDate();
   dDate.setDate(dDate.getDate()-days);
   
   //Add 45 days (Move to a day in the middle of next month):
   dDate.setDate(dDate.getDate()+45);
   
    //Get Last date of this month:
    //1. Get first date of next month:
   days=dDate.getDate();
   //2. Substract (days+0) days:
   dDate.setDate(dDate.getDate()-(days+0));
   
    //alert("1107");
    //Format: 
    var sDateEndOfMonth=fnFormatDate(dDate);
    //alert("1113 sDateEndOfMonth = " + sDateEndOfMonth);
    return sDateEndOfMonth;
}



function fnGetFirstDateOfWeek(sDate)
{
    //12/08/07 New:
    //sDate data type is a string:
    var dDate=new Date(sDate);

    var dayOfWeek=dDate.getDay();
    dDate.setDate(dDate.getDate()-dayOfWeek);

    //Format:
    var sFirstDateOfWeek=fnFormatDate(dDate);
    
    //alert(" 1126 sFirstDateOfWeek = " + sFirstDateOfWeek);
    return sFirstDateOfWeek;
}
	
	
function fnFormatDate(dDate)
{
    //12/08/07 New:
    //dDate datatype is date:
    //alert("1718 Utils fnFormatDate dDate = " + dDate.toString()); 
    var sMonth= "0" + (dDate.getMonth() +1).toString();
    //alert("1720 sMonth = " + sMonth); 
    var sDate= "0" + dDate.getDate().toString();
    //alert("1722 sDate = " + sDate); 
    
    //10/22/09. This is optional - more elegant?!:
    //var myDateFormat=((parseInt(sMonth,10)<10)?"0":"") + sMonth.toString() + "/" + ((parseInt(sDay,10)<10)?"0":"") + sDay.toString() + "/" + sYear.toString();
       
    var sDateFormat = sMonth.substr(sMonth.length-2) + "/" + sDate.substr(sDate.length-2) + "/" + dDate.getFullYear().toString() ;
    return sDateFormat;  //returns a string:
}

	
function fnPeriod()
{
    //12/08/07 Used for ytd Comparison reports. Handles 4 dates:
    //   Called from onchange_ddlPeriod: 
    //alert("1155 fnPeriod");
    var index=document.getElementById("ddlPeriod").selectedIndex; 
   
    if (index==0)
    {
        document.getElementById("txtEventDate3").value="";
        document.getElementById("txtEventDate4").value="";   
        return; 
    } 
   
   
    var sPeriod=document.getElementById("ddlPeriod").options[index].value;   
    //alert("38 sPeriod = " + sPeriod); 

    var today=new Date(); 
    //alert("43 today = " + today); 
    var dayToday=today.getDate();
    var monthToday=today.getMonth();
    var fullYearToday=today.getFullYear(); 
    var weekDayToday = today.getDay(); 
    
    var sEventDate=document.getElementById("txtEventDate").value; 
    var sEventDate2=document.getElementById("txtEventDate2").value; 
    var sEventDate3="";
    var sEventDate4="";   
   
    var dEventDate=new Date(sEventDate);
    //alert("55 dEventDate = " + dEventDate + ", today = " + today + "\n" + (dEventDate-today)); 
    if (dEventDate>today)
    {
        fnWriteMsg("Start dates cannot be in the future","0");
        return false; 
    }   
    var day=dEventDate.getDate();
    var month=dEventDate.getMonth();
    var fullYear=dEventDate.getFullYear();
    var weekDay = dEventDate.getDay();  
     
    var dEventDate2=new Date(sEventDate2); 
    if (dEventDate2>today)
    {
        fnWriteMsg("Start dates cannot be in the future","0");
        return false; 
    }  
    var day2=dEventDate2.getDate();
    var month2=dEventDate2.getMonth();
    var fullYear2=dEventDate2.getFullYear(); 
    var weekDay2 = dEventDate2.getDay();  
   
    //getDay() indexed from 0:
    //alert("64 weekDayToday = " + weekDayToday + "\nweekDay = " + weekDay + "\nweekDay2 = " + weekDay2); 

    switch (sPeriod)
    {
        case "D":
        {
            //alert("Switch Day");
            //Use dates dumped in:
            sEventDate3=sEventDate;
            sEventDate4=sEventDate2;   
            break; 
        } 
        case "Wtd":
        {
            //alert("Switch Wtd");
            //Use first Sunday of week dumped in:
            sEventDate=fnGetFirstDateOfWeek(dEventDate.toString());
            dDate=new Date(sEventDate);
            dDate.setDate(dDate.getDate() + 6);
            sEventDate3=fnFormatDate(dDate);
            
            sEventDate2=fnGetFirstDateOfWeek(dEventDate2.toString());
            dDate=new Date(sEventDate2);
            dDate.setDate(dDate.getDate() + 6);
            //alert("102 sEventDate3 = " + sEventDate3 + ", sEventDate4 = " + sEventDate4);
            sEventDate4=fnFormatDate(dDate);
            break; 
        }
        case "Mtd":
        {
            //alert("Switch Mtd");
            //Use first date of month for date dumped in:
            sEventDate=(month+1).toString() + "/01/" + fullYear.toString();
            dDate=new Date(sEventDate);
            sEventDate=fnFormatDate(dDate);
            
            sEventDate2=(month2+1).toString() + "/01/" + fullYear2.toString();
            dDate=new Date(sEventDate2);
            sEventDate2=fnFormatDate(dDate);
            
            sEventDate3=fnGetDateEndOfMonth(sEventDate);  
            sEventDate4=fnGetDateEndOfMonth(sEventDate2); 
            //alert("119 sEventDate3 = " + sEventDate3 + ", sEventDate4 = " + sEventDate4);
            break; 
        }
        case "Ytd":
        {
            //alert("Switch Ytd");
            //Use first date of year for date dumped in. Might default it to previous year:
            sEventDate="01/01/" + fullYear.toString();
            sEventDate2="01/01/" + fullYear2.toString(); 
            
            //End dates: 
            //getFullYear or getYear() adds to days not month?!
            dDate=new Date(sEventDate)
            var year=dDate.getFullYear()+1;
            //alert("113 year = " + year.toString());
            dDate= new Date("01/01/" + year.toString());
            dDate.setDate(dDate.getDate()-1);
            //alert("114 dDate = " + dDate.toString());
            sEventDate3=fnFormatDate(dDate);
            
            dDate=new Date(sEventDate2)
            year=dDate.getFullYear()+1;
            //alert("123 year = " + year.toString());
            dDate= new Date("01/01/" + year.toString());
            dDate.setDate(dDate.getDate()-1);
            //alert("126 dDate = " + dDate.toString());
            sEventDate4=fnFormatDate(dDate);
            break; 
        }     
    }
       
    if (sPeriod!="X")
    { 
        //Start Dates:
        document.getElementById("txtEventDate").value=sEventDate; 
        document.getElementById("txtEventDate2").value=sEventDate2; 
      
        //End Dates: 
        dDate=new Date(sEventDate3);
        if (dDate>today) 
        { 
            sEventDate3 =fnFormatDate(today); 
            dDate=new Date(sEventDate3);
            day=dDate.getDate();
            month=dDate.getMonth();

            //alert("162 month = " + month.toString() + ", day = " + day.toString());
            dDate=new Date(sEventDate4);
            fullYear=dDate.getFullYear();
            dDate=new Date((month+1).toString() + "/" + day.toString() + "/" + fullYear.toString());
            //alert("166 dDate=" + dDate);
            sEventDate4=fnFormatDate(dDate);
        }
        document.getElementById("txtEventDate3").value=sEventDate3;   
        document.getElementById("txtEventDate4").value=sEventDate4;   

   } 
} 


function fnFindBy(sDdlName, sFindBy, sFind, idPart)
{
    //07/05/2011 Modified to address a 'window.opener' drop down. sDdlName would be passed as (i.e. window.opener.ddlName):
    //02/20/2011 Revised for more general usage:
    //05/03/08: Equivalent to vb.net FindByValue/Text():
    //sFindBy: value or text: 
    //idPart. For concatenated IDs: 0 first part, 1, second part. DELIMITER IS * ONLY:
    //alert("1631 utils.fnFindBy: sDdlName = " + sDdlName + ", sFindBy = " + sFindBy + ", sFind = " + sFind + ", idPart = " + idPart);
    blnFound=false;
    var i = 0;

    ////////////////////////////////////////////
    var sPrefix = "";
    var ddl = "";
    var pos = sDdlName.lastIndexOf(".", sDdlName.length);
    if (pos > -1) {
        sPrefix = sDdlName.substr(0, pos);
        sDdlName = sDdlName.substr(pos + 1);
        //alert("sPrefix = " + sPrefix + ", sDdlName = " + sDdlName);
        ddl=window.opener.document.getElementById(sDdlName);
    }
    else {
        ddl = document.getElementById(sDdlName);
    }
    ////////////////////////////////////////////

    for (i=0;i<ddl.length;i++)
    {
        //02/20/2011 Revised. Use aryValue. substr(0,pos) etc too limiting:
        var sValue = ddl.options[i].value; 
        //alert("1329 sValue = " + sValue); 
        var sValue = eval("ddl.options[i]." + sFindBy);
        //alert("1640 sValue (eval) = " + sValue);
        var aryValue = new Array();
        aryValue = sValue.split("*");
        
        //02/20/2011 Keep this. Value for index 0 is probably 0:
        pos=sValue.indexOf("*",0); 
        if (pos>-1)
        {
            //////if (idPart==0) {sValue=sValue.substr(0,pos);}
            //////if (idPart==1) {sValue=sValue.substr(pos+1);}

            /////////////////////////////////////
            //02/20/2011:
            sValue = aryValue[idPart].toString();
            /////////////////////////////////////
        }
        //alert("1647 sFind = " + sFind + ", sValue = " + sValue);
        //02/20/2011. Added toLowerCase():
        if (sValue.toLowerCase() == sFind.toLowerCase())
        {
            //alert("1650 selected " +  i.toString());
            blnFound=true;  
            break;
        }
    }
    //alert("1660 i Return = " + i);
    if (blnFound!=true) {i=0;} 
    return i; 
}


///////////////////////////////////////////////////////////////////////////
/////////////// MAINTAIN DATAGRID SCROLL POSITION //////////////
///////////////////////////////////////////////////////////////////////////
function fnGetScrollTop(val,val2)
{
    //08/14/08 STILL USED FOR CALLISTS:
    //01/09/08 Reads scroll position of <div> tag:
    //val2: Generally 0 but for adding items (i.e. Bread Items), set to -1: 
    //alert("1660 Utils fnGetScrollTop val = " + val + ", val2 = " + val2);
    var scrollTop="-1";
    if (val2=="0")
    {  
        scrollTop=document.getElementById("div" + val).scrollTop; 
    } 
    document.getElementById("tbxScrollTop" + val).value=scrollTop;   
    //alert("1667 utils.js scrollTop = " + scrollTop); 
    return true; 
}


function fnGetScrollTop2()
{
    //08/12/08 Partially replaces fnGetScrollTop(). Gets over slow performance when datagrid has large nmber of rows.
    //  Still also using fnGetScrollTop() to pass parameters. Called from fnAddOnClickAttribute() on Products.aspx page:
    //  Cannot pass parameters, so ......
    //01/09/08 Reads scroll position of <div> tag:
    var val=document.getElementById("tbxWhichControl").value;
    //var scrollTop="-1";
    var scrollTop=document.getElementById("div" + val).scrollTop; 
    document.getElementById("tbxScrollTop" + val).value=scrollTop;   
    //alert("1667 scrollTop = " + scrollTop); 
    return true; 
}


function fnSetScrollTop(val)
{
    //01/09/08 Sets scroll position of <div> tag: 
    //alert("1954 Utils fnSetScrollTop val = " + val);
    var scrollTop=document.getElementById("tbxScrollTop" + val).value; 
    //alert("1956 scrollTop = " + scrollTop);
    if (scrollTop!="-1")
    { 
        document.getElementById("div" + val).scrollTop=scrollTop; 
    }
    //alert("1961 End fnSetScrollTop"); 
}

////////////////////////////////////////////////////////////////////////
//////////// END MAINTAIN DATAGRID SCROLL POSITION //////////
////////////////////////////////////////////////////////////////////////


function fnInitializePageSize(val)
{
    //alert("2000 fnInitializePageSize val = " + val);
    //08/01/08 They don't like it!
    //06/21/08 new:
    //Called from fnOnLoad() to resize the page, according to screen resolution: 

    //val: 0 - Maintain aspect ratio of 800x600, 1024x768 (1.33)
    //      1 - Stretch to full screen height  -   DEFAULT:
    //      2 - Stretch to full screen width (future)
     
    var scrWidth=screen.availWidth;
    var scrHeight=screen.availHeight;
    var pageWidth=scrWidth;
    var pageHeight=scrHeight;
    var scrLeft=0; //(scrWidth-pageWidth)/2;
    var scrTop=0; //(scrHeight-pageHeight)/2;
 
//    if (scrWidth>1024)
//    {
//        pageWidth=1024;
//        scrLeft=(scrWidth-pageWidth)/2;
//        if (val==0) 
//        { 
//            pageHeight=768;          
//            scrTop=(scrHeight-pageHeight)/2;
//        } 
//    }
    
    try
    {
        //////////////var winX = (document.all) ? window.screenLeft : window.screenX;

        /////////////var winY = (document.all) ? window.screenTop : window.screenY;


        //alert("fnInitializePageSize() left, top = " + window.screenLeft + ", " + window.screenTop);
        window.moveTo(scrLeft,scrTop);        //07/28/08 Has to be after moveTo(). Tried that and got into other issues with RouteSchedule:
        window.resizeTo(pageWidth, pageHeight);
        
        //alert("1718  scrLeft=" + scrLeft.toString() + ", scrTop=" + scrTop.toString() + ", pageWidth="+pageWidth.toString() + ", pageHeight="+pageHeight.toString());
    }
    catch(e) {/*alert("1719 Catch window.moveTo()fnInitializePageSize()");*/}   
} 


function fnReplaceProblemChars(val)
{
   //07/18/08 new. Need to save AlertNote:
   //alert("1712 replace val = " + val);  
   val=val.replace(/'/g,"^");
   //alert("1714 replace ' val = " + val); 
   val=val.replace(/\r\n/g,"[br]");
   //alert("1713 utils replace val = " + val); 
   return val;
}


function fnRestoreProblemChars(val, how)
{
    //07/18/08 new. Need to edit in AlertNote.aspx (multiline asp:TextBox) or WinMessage (innerHTML):
    //alert("1925 restore val = " + val + ", how = " + how);  
    //how: 0: Convert [BR] to \n\r (AlertNote.aspx multiline asp:TextBox), 1: Convert to <br/> (innerHTML):
    val=val.replace(/\^/g,"'");
    //alert("1931 replace ' val = " + val);  
    if (how==0) 
    {
        val=val.replace(/\[br\]/g,"\r\n");    //r:return char, n: new line:
    }
    else
    { 
        val=val.replace(/\[br\]/g,"<br/>");            //r:return char, n: new line: 
    } 
    //alert("1940 utils restore val = " + val);   
    return val;  
} 


function fnSetElementHeight2(thisId, adjust)
{
    //Set DataGrid heights:
    //07/31/08 Copy of cusanos.fnSetElementHeight(). Want to use it for Product .asc screens:
    
    /////////////////////////////////////////////
    /////////////////////////////////////////////
    //NOTE: DOCTYPE HAS TO BE (04/04/09 NOTE: HTML NOT XHTML): 
    //DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
    
    //AND NOT (Added 04/04/09):
    //DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    //AND NOT
    //DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"

    //10/30/08:
    //A +ve value for adjust REDUCES elementHeight:
    /////////////////////////////////////////////
    /////////////////////////////////////////////
    
    //05/02/08 Moved to cusanos.js:   
    //alert("1790 fnSetElementHeight2 thisId = " + thisId + ", adjust = " + adjust);
    var XYPos=fnGetOffset(thisId);
    
    var YPos=XYPos.Y;
    
    var winHeight  = fnGetClientHeight();    //06/07/10 More rigorous than document.body.clientHeight;
    //alert("1770 utils.js winHeight from fnGetClientHeight() = " + winHeight.H);
    var elementHeight=winHeight.H-YPos;
    //alert("2016 fnSetElementHeight thisId = " + thisId + "\nwinHeight = " + winHeight + "\nYPos = " + YPos + "\nHeight of element " + thisId + " = " + elementHeight);
    //07/14/06 For me: Extend to bottom of scrolled window:
    
    var fudge=0;
 
    elementHeight= (elementHeight - fudge - adjust); 
    /////////elementHeight=0; //01/06/09 Test:
    //alert("1777 New elementHeight = " + elementHeight);  
    try 
    { 
        //alert("203 Current height = " + document.getElementById(thisId).style.height); 
        //alert("2032 before reset"); 
        document.getElementById(thisId).style.height=elementHeight;
        //alert("2033 after reset"); 
        //alert("1787 New height = " + document.getElementById(thisId).style.height);  
    }
    catch(e) {} //alert("2036 catch M_OE fnSetElementHeight");} 
    //alert("2037 End fnSetElementHeight()"); 
    
    //01/02/09 return result for benefit of fnMyDataGridHeight so that totals can be pushed sideways depending on whether (vertical) scrollbar present: 
    return elementHeight;
} 


function fnFormatCommaDelimited(val)
{
    //10/09/08 Assumes val is like 124354657 or 123456787.89:
    //Returns comm delimied - like 1,723,456,787.89:
    //First used from FlourContracts.aspx, FlourHistory.aspx?. Consider for Cusanos.fnConvertToCurrency  
    
    var valRight="";
    var pos =val.indexOf(".",0);
    if (pos>-1)
    {
        valRight=val.substr(pos);     //Includes d.p.:
        val=val.substr(0,pos);
    }
    
    var i=0,j=0;
    var sTemp=""
    var newVal="";
    
    //Loop through from end of val:
    for (i=val.length-1;i>=0;i--)
    {
        sTemp=val.substr(i,1);
        j+=1;
        if ( ( (j%3)==1) & (j>1) ) {newVal = "," + newVal;}
        newVal = sTemp + newVal;
        //alert("1892 val = " + val + ", j = " + j + ",(j%3) = " + (j%3) + ", sTemp = " + sTemp + "\nnewVal = " + newVal);
       
    }
    return newVal + valRight;   //Join decimal part back on:
}


function fnValTime(thisId)
{
   //06/12/09 Moved here from Setup.aspx - for use in Customer.aspx:
   var myTime=document.getElementById(thisId).value; 
   
   if (myTime=="")  {return true;}

    var sTemp = "";
    var sHours = "";
    var sMins = "";
      
    myTime=fnTrimB(myTime,0); 
    //alert("32 myTime = " + myTime);

    //Strip off any leading zeros:
    for (var i = 0; i <= myTime.length; i++) {
        try{
           sTemp = myTime.substr(i, 1);
           if (sTemp != "0") {
               myTime = myTime.substr(i);
               break;
           }
       }
       catch (e) {
        //Handle all 0s:
        myTime = "";
        break;
       }
    }
    //alert("38 myTime = " + myTime);

    myTime = myTime.toLowerCase();
    
    ////////////////////////////////////////
    ////////////////////////////////////////
    //05/10/10 Validation is not rigorous enough. Need to ensure ONLY AM or PM letters:
    var sTest = myTime;
    //alert("40 sTest = " + sTest);
    //No g. replace just once instance:
    sTest = sTest.replace(/:/, "");
    sTest = sTest.replace(/am/, "");
    sTest = sTest.replace(/pm/, "");
    sTest = sTest.replace(/a/, "");
    sTest = sTest.replace(/p/, "");
    //alert("50 sTest = " + sTest + ", isNaN() = " + isNaN(sTest));
    if (isNaN(sTest) == true) {
        alert("Not a legitimate time (i.e. 11:30am)");
        return false;
    }


    //Limiting length here makes things simpler:
    if ( (sTest.length ==0) || (sTest.length > 4) ){
        alert("Not a legitimate time (i.e. 11:31am)");
        return false;
    }
   
   
    /////////////////////////////////////
    /////////////////////////////////////

    //Continue. Validated numbers only. May or may not contain :, ampm:

    blnAssumption = false;
    var pos=-1, pos2=-1, myHours, myMins, ampm="am";
    pos=myTime.indexOf(":",0);

    //Test for ampm:
    pos2 = myTime.toLowerCase().indexOf("am", 0);
    if (pos2 > -1) {
       ampm = "am";
    }
    else{
        pos2 = myTime.indexOf("a", 0);
        if (pos2 > -1) {
            ampm = "am";
        }
        else {
            pos2 = myTime.indexOf("pm", 0);
            if (pos2 > -1) {
                ampm = "pm";
            }
            else {
                pos2 = myTime.indexOf("p", 0);
                if (pos2 > -1) {
                    ampm = "pm";
                }
                blnAssumption = true;
            }
        }
       
    }
    //alert("46 pos = " + pos + ", pos2 = " + pos2 + ", sTest.length = " + sTest.length + ", ampm = " + ampm);
    if ((pos > pos2) & (pos2 > -1)) {
        alert("Not a legitimate time (i.e. 11:32am)");
        return false;
    }

    if (pos == -1) {
        //No :
        switch (sTest.length) {
            case 1:
                {
                    myTime = sTest + ":00";
                    break;
                }
            case 2:
                {
                    myTime = sTest.substr(0, 1) + ":0" + sTest.substr(1, 1);
                    break;
                }
            case 3:
                {
                    sHours = sTest.substr(0, 1); //1 thru 9:
                    sMins = sTest.substr(1);
                    //alert("47 sHours = " + sHours + ", sMins = " + sMins);
                    if (parseInt(sHours, 10) > 12) {
                        //sHours can only be 1 - 9:
                        sHours = sTest.substr(0, 1);
                        sMins = sTest.substr(1);
                    }
                    else {
                        //alert("48");
                        blnAssumption = true;
                    }

                    myTime = sHours + ":" + sMins;
                    break;
                }
            case 4:
                {
                    sHours = sTest.substr(0, 2);
                    sMins = sTest.substr(2);
                    myTime = sHours + ":" + sMins;
                    blnAssumption = true;
                    break;
                }
        }
        //Now fully formatted.
    }
    else {
        if (pos2 > -1) { myTime=myTime.substr(0,pos2); }
    }

    //Start again!
    //alert("54 myTime (Start again) = " + myTime);
    pos = myTime.indexOf(":", 0);
   
    //sHours: ////////////////////////////////
    if (pos==0) 
    { 
        sHours="00";
    }
    else
    {  
        sHours=myTime.substr(0,pos);
    }
    //alert("56 sHours = " + sHours);
   
    if (parseInt(sHours,10)>12)
    {
        alert("Not a legitimate time (i.e. 11:33am)");
        return false;
    }



    //sMins: /////////////////////////////////
 
    sMins = myTime.substr(pos + 1);
    //alert("55 sMins = " + sMins);
    
    sMins="00" + sMins;
    sMins=sMins.substr(sMins.length-2);
    //alert("58 sMins = " + sMins);

    if (parseInt(sMins, 10) > 59) {
        alert("Not a legitimate time (i.e. 11:34am)");
        return false;
    }
  

   //alert("2197 utils " + sHours + ":" + sMins + ampm);
   document.getElementById(thisId).value = sHours + ":" + sMins + ampm;
   //if (blnAssumption == true) { alert("Assumption"); } 
   return true;
}


function fnValTime24hr(thisId) {
    //alert("2200 fnValTime24hr thisId = " + thisId);
    //0307/2011 new. Enter 24hour time - 4 digits only. Convert to ampm:
    var sMsg = "Enter as 24 hour (military) time\n4 digits required.\n\nExamples:\n0005    12:05am\n0015    12:15am\n0115    1:15am\n1400    2:00pm\n2330    11:30pm";   
    var myTime = document.getElementById(thisId).value;

    if (myTime == "") { return true; }

    var sTemp = "";
    var sHours = "";
    var iHours = 0;
    var sMins = "";
    var ampm = "am";

    myTime = fnTrimB(myTime, 0);
    //alert("32 myTime = " + myTime);
 
    if (isNaN(myTime) == true) {
        alert(sMsg);
        return false;
    }


    //Limit length to 4 digits!
    if (myTime.length != 4) {
        alert(sMsg);
        return false;
    }

    /////////////////////////////////////
    /////////////////////////////////////

    if (parseInt(myTime, 10) > 2400) {
        alert(sMsg);
        return false;
    }

    sHours = myTime.substr(0, 2); 
    sMins = myTime.substr(2);

    //Convert to a number. Strip off any leading zero. DO NOT do this for sMins. Need top retain leading zeros:
    iHours = parseInt(sHours, 10);  
    
    if (iHours > 24) {
        alert(sMsg);
        return false;
    }

    if (parseInt(sMins, 10) > 59) {
        alert(sMsg);
        return false;
    }

    //alert("2197 utils " + iHours.toString() + ":" + sMins);

    if (iHours >= 12) {
        if (iHours < 24) { ampm = "pm"; }
        if (iHours > 12) { iHours = (iHours - 12).toString(); }
    }
    else {
         if (iHours==0) {iHours=12;}
    }
    
    sHours = iHours.toString();
    //alert("2197 utils " + sHours + ":" + sMins + ampm);
    document.getElementById(thisId).value = sHours + ":" + sMins + ampm;
    return true;
}


function fnConvert12To24HrTime(sTime) {
    //alert("2198 utils sTime = " + sTime);
    //02/25/2011 Not good enough. Problems when comparing times:
    //06/19/09 Convert 12hour time into 24hour time. Returns numeric ('decimal') value:
    //REQUIRED sTime format: 3:30am, 12:03pm 
    //  NOTE: Time between 12noon and 1pm (i.e. 12:30pm) will be returned as 24.30 - which may or may not be OK:

    //////////////////////////////
    /*
    12:00AM (midnight) - 0.0
    12:30am - 0.5
    2:30am - 2.5
    12:00PM (midday) - 12.0
    12:30pm - 12.5
    2:30pm - 14.5
    */
    //////////////////////////////
    var sTime2 = sTime;
    var ampm = "";
    var sHours = "";
    var sMins = "";
    var decHours = 0;
    var decMins = 0;
    var myTime=0;
    
    var pos=sTime2.indexOf("am",0);
    var addHours=0;
    if (pos>-1)
    {
        sTime2 = sTime2.substr(0, pos);
        ampm = "am";
    }
    else
    {
        pos=sTime2.indexOf("pm",0);
        if (pos>-1)
        {
            //addHours=12;
            Time2 = sTime2.substr(0, pos);
            ampm = "pm";
        }        
    }
    pos = sTime2.indexOf(":", 0);
    sHours = sTime2.substr(0, pos);
    sMins = sTime2.substr(pos + 1);
    //alert("2200 sHours = " + sHours + ", sMins = " + sMins + ", ampm = " + ampm);
    
    decMins = parseInt(sMins, 10) / 60;
    decHours = parseInt(sHours, 10);
    //alert("2205 decHours = " + decHours + ", decMins = " + decMins)

    //Deal with anomalies:
    if (ampm == "pm") {
        //pm:
        if ( (decHours < 12) ) { decHours += 12; }
    }
    else {
        //am:
        //if ((decHours == 12) & (decMins > 0)) { decHours -= 12; }
        if (decHours == 12) { decHours -= 12; }
    }
    //sTime2=sTime2.replace(/:/g,".");
    //myTime=parseFloat(sTime2,10);
    //myTime+=addHours;
    myTime = (decHours + decMins);
    
    //alert("2219 utils (24hr) sTime = " + sTime + ", myTime = " + myTime);
    
    return myTime;
}


function fnCalcProducePrice(sPrice, sMargin)
{
    //01/21/10 FORMULA now used 4 places. See Utilities.CalcProducePrice():
    //SAME FORMULA AS IN Utilities.CalcProducePrice():
    //Called from CustPricing and AddItems:
    //08/29/09 Was in AddItems.aspx:
    //alert("2237 Utils fnCalcProduceItems sPrice = " + sPrice + ", sMargin = " + sMargin);
    //var decPrice = parseFloat(sPrice,10) / (1 - parseFloat(sMargin,10) / 100);
    
    //11/20/09 Calculation changes. sMargin is now to be $ instead of %:
    var decPrice=parseFloat(sPrice,10) + parseFloat(sMargin,10);
    var sPrice2  = fnMyRound2(decPrice.toString(), 2);
    //decPrice = parseFloat(sPrice,10);
    //alert("2241 sPrice2 = " + sPrice2);
    return sPrice2;
}


/////////////////////////////////////////////////////////
///////////// IFRAME DRAG AND DROP FUNCTIONS ////////////
/////////////////////////////////////////////////////////
// JScript File
// ===================================================================
// Author: Matt Kruse <matt@mattkruse.com>
// WWW: http://www.mattkruse.com/
//
// NOTICE: You may use this code for any purpose, commercial or
// private, without any further permission from the author. You may
// remove this notice from your final code if you wish, however it is
// appreciated by the author if at least my web site address is kept.
//
// You may *NOT* re-distribute this code in any way except through its
// use. That means, you can include it in your product, or your web
// site, or any other form where the code is actually being used. You
// may not put the plain javascript up on your site for download or
// include it in your javascript libraries for download. 
// If you wish to share this code with others, please just point them
// to the URL instead.
// Please DO NOT link directly to my .js files from your site. Copy
// the files to your server and use them there. Thank you.
// ===================================================================

// HISTORY
// ------------------------------------------------------------------
// Jan 23, 2004: Fixed problems which caused the script not to work in
//               some framed situations. Improved browser support.
//               Added easier "addHandle" implementation.
// May 25, 2003: Added better event position detection, added caching
//               of IFRAME object references to avoid lookups. Added
//               'move' cursor to handles.
// May 24, 2003: Updated to fix bug with Netscape 7.x
// May 23, 2003: Created
/* 
DESCRIPTION: The purpose of this library is to allow IFRAME objects to be
dragged around the screen in the same way that popup windows or draggable
DIV tags are often used. Since IFRAME objects always cover form objects,
this makes an ideal solution for a simulated "popup window" on a page with
form objects.

COMPATABILITY: Tested successfully with IE 6.x, Netscape 6.2, 7.x, and
Mozilla 1.3. Since this script uses IFRAME objects and DHTML heavily, 
cross-browser compatability is a goal but there may be some quirks in 
various browser versions.

USAGE:
1) Include the source file in your main document which contains the IFRAME
   tags. Make sure each iframe has a unique "ID" attribute. For best browser
   compatability, also include a "NAME" attribute in the IFRAME tag that
   has the same value as the "ID" attribute.
   
2) In the document content of each IFRAME which will be draggable, , do two
	things:
	a) Include the dragiframe.js file in the source
	b) add this code to the <BODY> tag:
   onLoad="addHandle(document.getElementById('toolbar'), window);"
   Where 'toolbar' is the ID of an element on the page which should be the 'handle'
   by which the IFRAME should be dragged (like a toolbar at the top).
   If you want the IFRAME to be draggable by clicking anywhere in the IFRAME
   document, use:
   onLoad="addHandle(document.getElementsByTagName('body').item(0), window);"
   
   NOTE: The code will automatically look up the window.parent tree to find a
   parent document with draggable iframes enabled. It will attach itself to the
   first document it finds, allowing it to work within a framed environment.

In your parent document (containing the IFRAMEs), you may set a couple of options:

// Set to true to always bring the selected IFRAME to the "top" of the zIndex.
// Defaults to false
bringSelectedIframeToTop(true|false);

// Set to true to allow IFRAME objects to be dragged off the screen. This may
// make the handle be no longer reachable by the mouse, causing the IFRAME to
// be stranded.
// Defaults to false
allowDragOffScreen(true|false);

// You may manually set this variable to define the highest zIndex used in 
// your main document. This is used to determine what zIndex to give an IFRAME
// if it is selected and "bringSelectedIframeToTop" is set to true.
// Defaults to 99.
DIF_highestZIndex=4;

NOTES:
1) If you have defined onmousedown or onmouseup event handlers for your "handle"
   object in the IFRAME, they will be over-written.
2) If you have defined an onmousemove handler in either the main document or
   the IFRAME document, they will be over-written.
3) All <IFRAME> objects must have an ID!
*/ 

// Variables used for "Draggable IFrame" (DIF) functions
var DIF_dragging=false;
var DIF_iframeBeingDragged="";
var DIF_iframeObjects=new Object();
var DIF_iframeWindows=new Object();
var DIF_iframeMouseDownLeft = new Object();
var DIF_iframeMouseDownTop = new Object();
var DIF_pageMouseDownLeft = new Object();
var DIF_pageMouseDownTop = new Object();
var DIF_handles = new Object();
var DIF_highestZIndex=99;
var DIF_raiseSelectedIframe=false;
var DIF_allowDragOffScreen=false;

// Set to true to always raise the dragged iframe to top zIndex
function bringSelectedIframeToTop(val) {
	DIF_raiseSelectedIframe = val;
	}
	
// Set to try to allow iframes to be dragged off the top/left of the document
function allowDragOffScreen(val) {
	DIF_allowDragOffScreen=val;
	}

// Method to be used by iframe content document to specify what object can be draggable in the window
function addHandle(o, win) 
{
	//alert("118 o = " + o + ", win = " + win);
	if (arguments.length==2 && win==window) 
	{
		// JS is included in the iframe who has a handle, search up the chain to find a parent window that this one is dragged in
		var p = win;
		while (p=p.parent) {
			if (p.addHandle) { p.addHandle(o,win,true); return; }
			if (p==win.top) { return; } // Already reached the top, stop looking
			}
		return; // If it reaches here, there is no parent with the addHandle function defined, so this frame can't be dragged!
		}
	var topRef=win;
	var topRefStr = "window";
	while (topRef.parent && topRef.parent!=window) {
		topRef = topRef.parent;
		topRefStr = topRefStr + ".parent";
		}
	// Add handlers to child window
	if (typeof(win.DIF_mainHandlersAdded)=="undefined" || !win.DIF_mainHandlersAdded) {
		// This is done in a funky way to make Netscape happy
		with (win) { 
			eval("function OnMouseDownHandler(evt) { if(typeof(evt)=='undefined'){evt=event;}"+topRefStr+".parent.DIF_begindrag(evt, "+topRefStr+") }");
			eval("document.onmousedown = OnMouseDownHandler;");
			eval("function OnMouseUpHandler(evt) { if(typeof(evt)=='undefined'){evt=event;}"+topRefStr+".parent.DIF_enddrag(evt, "+topRefStr+") }");
			eval("document.onmouseup = OnMouseUpHandler;");
			eval("function OnMouseMoveHandler(evt) { if(typeof(evt)=='undefined'){evt=event;}"+topRefStr+".parent.DIF_iframemove(evt, "+topRefStr+") }");
			eval("document.onmousemove = OnMouseMoveHandler;");
			win.DIF_handlersAdded = true;
			win.DIF_mainHandlersAdded = true;
			}
		}
	// Add handler to this window
	if (typeof(window.DIF_handlersAdded)!="undefined" || !window.DIF_handlersAdded) {
		eval("function OnMouseMoveHandler(evt) { if(typeof(evt)=='undefined'){evt=event;}DIF_mouseMove(evt, window) }");
		eval("document.onmousemove = OnMouseMoveHandler;");
		window.DIF_handlersAdded=true;
		}
	o.style.cursor="move";
	var name = DIF_getIframeId(topRef);
	if (DIF_handles[name]==null) {
		// Initialize relative positions for mouse down events
		DIF_handles[name] = new Array();
		DIF_iframeMouseDownLeft[name] = 0;
		DIF_iframeMouseDownTop[name] = 0;
		DIF_pageMouseDownLeft[name] = 0;
		DIF_pageMouseDownTop[name] = 0;
		}
	DIF_handles[name][DIF_handles[name].length] = o;
	}

// Generalized function to get position of an event (like mousedown, mousemove, etc)
function DIF_getEventPosition(evt) {
	var pos=new Object();
	pos.x=0;
	pos.y=0;
	if (!evt) {
		evt = window.event;
		}
	if (typeof(evt.pageX) == 'number') {
		pos.x = evt.pageX;
		pos.y = evt.pageY;
	}
	else {
		pos.x = evt.clientX;
		pos.y = evt.clientY;
		if (!top.opera) {
			if ((!window.document.compatMode) || (window.document.compatMode == 'BackCompat')) {
				pos.x += window.document.body.scrollLeft;
				pos.y += window.document.body.scrollTop;
			}
			else {
				pos.x += window.document.documentElement.scrollLeft;
				pos.y += window.document.documentElement.scrollTop;
			}
		}
	}
	return pos;
}

// Gets the ID of a frame given a reference to a window object.
// Also stores a reference to the IFRAME object and it's window object
function DIF_getIframeId(win) {
	// Loop through the window's IFRAME objects looking for a matching window object
	var iframes = document.getElementsByTagName("IFRAME");
	for (var i=0; i<iframes.length; i++) {
		var o = iframes.item(i);
		var w = null;
		if (o.contentWindow) {
			// For IE5.5 and IE6
			w = o.contentWindow;
			}
		else if (window.frames && window.frames[o.id].window) {
			w = window.frames[o.id];
			}
		if (w == win) {
			DIF_iframeWindows[o.id] = win;
			DIF_iframeObjects[o.id] = o;
			return o.id; 
			}
		}
	return null;
	}

// Gets the page x, y coordinates of the iframe (or any object)
function DIF_getObjectXY(o) {
	var res = new Object();
	res.x=0; res.y=0;
	if (o != null) {
		res.x = o.style.left.substring(0,o.style.left.indexOf("px"));
		res.y = o.style.top.substring(0,o.style.top.indexOf("px"));
		}
	return res;
	}

// Function to get the src element clicked for non-IE browsers
function getSrcElement(e) {
	var tgt = e.target;
	while (tgt.nodeType != 1) { tgt = tgt.parentNode; }
	return tgt;
	}

// Check if object clicked is a 'handle' - walk up the node tree if required
function isHandleClicked(handle, objectClicked) {
	if (handle==objectClicked) { return true; }
	while (objectClicked.parentNode != null) {
		if (objectClicked==handle) {
			return true;
			}
		objectClicked = objectClicked.parentNode;
		}
	return false;
	}
	
// Called when user clicks an iframe that has a handle in it to begin dragging
function DIF_begindrag(e, win) {
	// Get the IFRAME ID that was clicked on
	//window.frames["ifrmPopupMsg"].document.getElementById("WinMessage.aspx").style.cursor="move";
    //parent.document.getElementById("ifrmPopupMsg").style.cursor="hand";
	var iframename = DIF_getIframeId(win);
	
	//DIF_handles[iframename].style.cursor="move";
	if (iframename==null) { return; }
	// Make sure that this IFRAME has a handle and that the handle was clicked
	if (DIF_handles[iframename]==null || DIF_handles[iframename].length<1) {
		return;
		}
	var isHandle = false;
	var t = e.srcElement || getSrcElement(e);
	for (var i=0; i<DIF_handles[iframename].length; i++) {
		if (isHandleClicked(DIF_handles[iframename][i],t)) {
			isHandle=true;
			break;
			}
		}
	if (!isHandle) { return false; }
	DIF_iframeBeingDragged = iframename;

	//////document.getElementsByTagName("body").style.cursor="move";
	//////document.getElementsByTagName("ifrmPopupMsg").style.cursor="move";
	if (DIF_raiseSelectedIframe) 
	{
		DIF_iframeObjects[DIF_iframeBeingDragged].style.zIndex=DIF_highestZIndex++;
	}
	//DIF_iframeObjects[DIF_iframeBeingDragged].style.cursor="move";
	//alert("281 DIF_iframeObjects[DIF_iframeBeingDragged].style.cursor = " + DIF_iframeObjects[DIF_iframeBeingDragged].style.cursor);
	
	DIF_dragging=true;
	var pos=DIF_getEventPosition(e);
	DIF_iframeMouseDownLeft[DIF_iframeBeingDragged] = pos.x;
	DIF_iframeMouseDownTop[DIF_iframeBeingDragged] = pos.y;
	var o = DIF_getObjectXY(DIF_iframeObjects[DIF_iframeBeingDragged]);
	//o.style.cursor="move";
	DIF_pageMouseDownLeft[DIF_iframeBeingDragged] = o.x - 0 + pos.x;
	DIF_pageMouseDownTop[DIF_iframeBeingDragged] = o.y -0 + pos.y;
	}

// Called when mouse button is released after dragging an iframe
function DIF_enddrag(e) 
{
	//Works (clue!) but not until cursor moved OUT of iframe:
	//var o = document.getElementsByTagName("body").item(0);
	//this.style.cursor="hand";
	//parent.document.getElementById("ifrmPopupMsg").style.cursor="hand";
	//o.style.width="20px"; //works!
	DIF_dragging=false;
	DIF_iframeBeingDragged="";
	}

// Called when mouse moves in the main window
function DIF_mouseMove(e) {
	if (DIF_dragging) {
		var pos = DIF_getEventPosition(e);
		DIF_drag(pos.x - DIF_pageMouseDownLeft[DIF_iframeBeingDragged] , pos.y - DIF_pageMouseDownTop[DIF_iframeBeingDragged]);
		}
	}

// Called when mouse moves in the IFRAME window
function DIF_iframemove(e) {
	if (DIF_dragging) {
		var pos = DIF_getEventPosition(e);
		DIF_drag(pos.x - DIF_iframeMouseDownLeft[DIF_iframeBeingDragged] , pos.y - DIF_iframeMouseDownTop[DIF_iframeBeingDragged]);
		}
	}

// Function which actually moves of the iframe object on the screen
function DIF_drag(x,y) 
{
	var o = DIF_getObjectXY(DIF_iframeObjects[DIF_iframeBeingDragged]);
	// Don't drag it off the top or left of the screen?
	var newPositionX = o.x-0+x;
	var newPositionY = o.y-0+y;
	
	var scrWidth=screen.availWidth;     //02/22/09 I added:
    var scrHeight=screen.availHeight;
    
	if (!DIF_allowDragOffScreen) 
	{
		//02/22/09 Impose right and down limits. Adjust for scrollTop:
		var clientWidth=parseInt(document.body.clientWidth,10);
		var clientHeight=parseInt(document.body.clientHeight,10);
		var scrollTop=parseInt(parent.document.body.scrollTop,10);
		//scrollTop=0;
		var iFrameWidth = DIF_iframeObjects[DIF_iframeBeingDragged].style.width;
		var iFrameHeight = DIF_iframeObjects[DIF_iframeBeingDragged].style.height;
		
		//Limit movement:
		if (newPositionX < 0) { newPositionX=0; }
		if (newPositionY < scrollTop) { newPositionY=scrollTop; }
		
        //alert("318 newPositionX = " + newPositionX + ", iFrameWidth = " + iFrameWidth + "\niFrameWidth = " + (newPositionX + parseInt(iFrameWidth,10)) );
		if ( (parseInt(newPositionX,10) + parseInt(iFrameWidth,10) ) > clientWidth ) { newPositionX=(clientWidth-parseInt(iFrameWidth,10)); }
		if ( (parseInt(newPositionY,10) + parseInt(iFrameHeight,10) ) > (clientHeight + scrollTop) ) { newPositionY=(clientHeight-parseInt(iFrameHeight,10) + scrollTop); }
	}
	DIF_iframeObjects[DIF_iframeBeingDragged].style.left = newPositionX + "px";
	DIF_iframeObjects[DIF_iframeBeingDragged].style.top  = newPositionY + "px";
	DIF_pageMouseDownLeft[DIF_iframeBeingDragged] += x;
	DIF_pageMouseDownTop[DIF_iframeBeingDragged] += y;
}
/////////////////////////////////////////////////////////
/////////// END IFRAME DRAG AND DROP FUNCTIONS //////////
/////////////////////////////////////////////////////////



function getPosition(e) 
{    
    //Generic. Used for divContext in customer Care:
    e = e || window.event;    
    var cursor = {x:0, y:0};    
    if (e.pageX || e.pageY) 
    {        
        cursor.x = e.pageX;        
        cursor.y = e.pageY;    
    }     
    else 
    {        
        cursor.x = e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) -  document.documentElement.clientLeft; 
        cursor.y = e.clientY + (document.documentElement.scrollTop ||  document.body.scrollTop) -   document.documentElement.clientTop;    
    }    
    return cursor;
}


function fnValPhone3(thisId)
{
	//09/20/09 There is already cusanos.js.fnValPhone() and Customer.fnValPhone2():
	//  Want this function for Vendor.aspx:
	
	//alert("2635 fnValPhone3 thisId = " + thisId);

	var phone=document.getElementById(thisId).value;
	//alert("2639 phone = " + phone);
	//Normalize:
	var temp="";
	var sReturn="";
	for (var i=0;i<phone.length;i++)
	{
		var asc=phone.charCodeAt(i);
		//alert("asc = " + asc);
		if ((asc>47 ) & (asc<58))
		{
			temp += phone.charAt(i);
		}
	}
	//alert("2651 Normalized = " + temp + ", " + temp.length);
	switch (temp.length)
	{
		case 7:
		{
			temp=temp.substr(0,3) + "-" + temp.substr(3);
			break;
		}
		case 10:
		{
			temp=temp.substr(0,3) + "-" + temp.substr(3,3) + "-" + temp.substr(6);
			break;
		}
		case 11:
		{
		    if (temp.substr(0,1) !=1)
		    {
		        alert("If there are to be 11 digits, first digit must be a 1");  
		    }
		    else
		    {
			    temp=temp.substr(0,1) + "-" + temp.substr(1,3) + "-" + temp.substr(4,3) + "-" + temp.substr(7);
		    }
			break;
		}
		default:
		{
			temp="";
			alert("Incorrect number of digits in phone #");
			document.getElementById(thisId).focus();
		}
	}
	//alert("Formatted = " + temp);
	if (temp!="") 
	{
		document.getElementById(thisId).value=temp;
	}
	//alert("2688 End fnValPhone3");
}



function fnWriteMsg(val, color)
{
    //04/24/07 Moved to here from Cusanos.js for wider use:
    //07/29/06
    document.getElementById("MessageBox").value=val;
   if (color=="1")
   { 
        //OK
        document.getElementById("MessageBox").style.color="black"; 
   }
   else
   {
        document.getElementById("MessageBox").style.color="red";
   } 
}


/////////////////////////////////////////////////////
// 05/29/09 OPTIONAL FUNCTIONS FOR DropDownMS.asc ///
/////////////////////////////////////////////////////
function fnInitializeDropDownMS(sUserControlIDs)
{
    //Intention of this function is to initialize to selected=true all instances of DropDownMS whose ID is specified in array aryDropDownMS:
    //NOTES:
    //When DropDownMS used in an ascx page,  each member of sUserControlIDs needs prefix of ascx page id. i.e. CustDelivery1_: 
    //To override postback, each member of sUserControlIDs may have a dash (-) appended to it. Used in FaxOrderForm, Associate.aspx, CustDelivery (Customer.js):
    //alert("2652 Utils fnInitializeDropDownMS sUserControlIDs = " + sUserControlIDs);
   
    blnOverridePostBack = false;
    var aryDropDownMS = new Array();
    aryUserControlIDs = sUserControlIDs.split(",");
    //alert("2657 aryUserControlIDs = " + aryUserControlIDs);
 
    var sOverridePostBack = "";
    var sUserControlID = "";
    var ifrmID = "";
    var ifrm = "";
    var pos = 0;
    var sTitle = "";
 
    for (var i = 0; i < aryUserControlIDs.length; i++)
    {
        sTitle = "";
        blnOverridePostBack = false;
        sUserControlID = aryUserControlIDs[i];
        //alert("2658 sUserControlID = " + sUserControlID);
        if (sUserControlID.substr((sUserControlID.length - 1), 1) == "-") {
            blnOverridePostBack = true;
            sUserControlID = sUserControlID.substr(0, (sUserControlID.length - 1));
        }
    
        //alert("2659 fnInitializeDropDownMS sUserControlID = " + sUserControlID + ", blnOverridePostBack = " + blnOverridePostBack);
        //alert("2660 document.getElementById(sUserControlID + '_tbxIsPostBack').value = " + document.getElementById(sUserControlID + "_tbxIsPostBack").value);


        //06/09/2011 Correction. Need to define ifrm here. If no _tbxIsPostBack or blnOverridePostBack, ifrm doesn't get updated on reload:
        ifrmID = sUserControlID + "_ifrmDropDownMS";
        pos = sUserControlID.indexOf("_", 0);
        if (pos > -1) {
            ifrmID = sUserControlID.substr(pos + 1) + "_ifrmDropDownMS";
        }
        ifrm = window.frames[ifrmID];
        //alert("2664 fnInitializeDropDownMS Before fnRefreshSelections\ni = " + i.toString() + ", sUserControlID = " + sUserControlID);
        
        
        if ((document.getElementById(sUserControlID + "_tbxIsPostBack").value != "1") || (blnOverridePostBack == true))
        {
            //10/12/10 Need to fnRefreshSelections() even when tbxSelectedIDs=''. Need to clear values:
            /////////////////////////////////////////
            ifrm.fnRefreshSelections(0);
            /////////////////////////////////////////
            //alert("2666 After fnRefreshSelections");
        }

        sOverridePostBack = (blnOverridePostBack == true) ? "0" : "1";
        document.getElementById(sUserControlID + "_tbxIsPostBack").value = sOverridePostBack;

        //////////////////////////////////////////////
        //////////////////////////////////////////////
        //03/26/2011 Handle title with line breaks.
        //  NOTE: \n in the title when stored in tbxTitle no longer works?!
        sTitle = document.getElementById(sUserControlID + "_tbxTitle").value;
        //alert("2668 sTitle = " + sTitle);
        //pos = sTitle.indexOf("^", 0);
        //alert("pos = " + pos);
        if (sTitle!="") {
            //Has line breaks:
            sTitle = sTitle.replace(/\^/g, "\n");
            //alert("2673 sTitle = " + sTitle);
            ifrm.document.getElementById("ddlDropDownMS").title = sTitle;
            document.getElementById(sUserControlID + "_tbxDropDownMS").title = sTitle;
            document.getElementById(sUserControlID + "_tbxDropDownMS2").title = sTitle;
        }
        //////////////////////////////////////////////
        //////////////////////////////////////////////
        //alert("2670 end of loop");
    }
    //alert("2675 Utils fnInitializeDropDownMS End");
}


function fnInitializeDropDownMS2(sUserControlIDs) {
    //02/04/2011 Now obsolete. Was used with FaxOrderForm only:
    //alert("2680 utils fnInitializeDropDownMS2 sUserControlIDs = " + sUserControlIDs);
    //10/18/10: new. Alternative to fnInitializeDropDownMS() which sometimes has problems firing
    //  ifrm.fnRefreshSelections(aryDropDownMS[i].toString()). 
    //  In this can can get around it with this function:
    //  First used FaxOrderForm.aspx:

    var aryUserControlIDs = new Array();
    aryUserControlIDs = sUserControlIDs.split(",");
    //alert("2690 fnInitializeDropDownMS2 aryUserControlIDs = " + aryUserControlIDs);

    for (var i = 0; i < aryUserControlIDs.length; i++) {
        var cntSelected = document.getElementById(aryUserControlIDs[i].toString() + "_tbxCntSelected").value;
        //alert("2695 cntSelected = " + cntSelected.toString());
        if (cntSelected == "") { cntSelected = "0"; }
        document.getElementById(aryUserControlIDs[i].toString() + "_tbxDropdownMS").value = " " + cntSelected.toString() + " selected";
    }
    //alert("2700 Utils fnInitializeDropDownMS2");
}


function fnCloseDropDownMS(sUserControlIDs)
{
    //alert("2676 Utils fnCloseDropDownMS sUserControlIDs = " + sUserControlIDs);
    //02/04/2011 If used within an ascx page, requires page id as prefix (ie. CustDelivery1_):
    //Closing instances of sslDropDownMS is initiated by the form tag when it contains onclick="fnCloseDropDownMS();".
    //  However, the form tag onclick fires from anywhere within the form, including when the user control is clicked.
    //  Intention of this function is to close all instances of DropDownMS whose ID is specified in array aryDropDownMS:
    //10/13/10 Added try/catch. In associate.aspx, initially covered by asp:Panel:
    
                
    try {
        var aryDropDownMS = new Array();
        aryUserControlIDs = sUserControlIDs.split(",");
        var sUserControlID="";
        var ifrmID = "";
        var ifrmStyle = "";
        var pos = 0;
        var sumOnClickToggleOpen = 0;
        var i = 0;
        for (i = 0; i < aryUserControlIDs.length; i++) {
            if (document.getElementById(aryUserControlIDs[i].toString() + "_tbxOnClickToggleOpen").value == "1") { sumOnClickToggleOpen += 1; }
        }
        //alert("39 sumOnClickToggleOpen = " + sumOnClickToggleOpen);

        //To close sslDropDownMS instance ddlBakeSequenceMS:
        if (sumOnClickToggleOpen > 0) {
            for (i = 0; i < aryUserControlIDs.length; i++) {
                document.getElementById(aryUserControlIDs[i].toString() + "_tbxOnClickToggleOpen").value = "0";
            }
        }
        else {
            //Parameter is ID of instance of ddlDropDownMS:
            for (i = 0; i < aryUserControlIDs.length; i++) {
                sUserControlID = aryUserControlIDs[i].toString();
                ifrmStyle = document.getElementById(sUserControlID + "_ifrmDropDownMS").style;
                pos = sUserControlID.indexOf("_", 0);
//                if (pos > -1) {
//                    ifrmID = sUserControlID.substr(pos + 1) + "_ifrmDropDownMS";
//                }
                ifrmStyle.display = "none";
            }
        }
    }
    catch (e) { }
}
/////////////////////////////////////////////////////
///// END OPTIONAL FUNCTIONS FOR DropDownMS.asc /////
/////////////////////////////////////////////////////


function fnMyDateDiff(which,sDate1,sDate2)
{
    //10/21/09 Called from BreadItems2. Need to ensure date in the future:
    //which: mm,dd,yy etc. Currently not used;
    //date1, date2 required to be legitimate date objects:
    //date2>=date1:
    //alert("2782 Utils fnMyDateDiff which = " + which + ", sDate1 = " + sDate1 + ", sDate2 = " + sDate2);
    
    //Dates seem to be passed as string event though they were declared dates?!
    var date1=new Date(sDate1);
    var date2=new Date(sDate2);

    var sYear1=date1.getFullYear();
    var dMonth1=date1.getMonth() + 1;
    var sMonth1=dMonth1.toString();
    var sDay1=date1.getDate();
    var myDateFormat1=sYear1.toString() + ((parseInt(sMonth1,10)<10)?"0":"") + sMonth1.toString() + ((parseInt(sDay1,10)<10)?"0":"") + sDay1.toString();

    var sYear2=date2.getFullYear();
    var dMonth2=date2.getMonth() + 1;
    var sMonth2=dMonth2.toString();
    var sDay2=date2.getDate();
    var myDateFormat2=sYear2.toString() + ((parseInt(sMonth2,10)<10)?"0":"") + sMonth2.toString() + ((parseInt(sDay2,10)<10)?"0":"") + sDay2.toString();  

    var iDays=parseInt(myDateFormat2,10) - parseInt(myDateFormat1,10);
    //alert("2799 Utils\n" +  myDateFormat2 + "\n" + myDateFormat1 + "\n" + iDays.toString());
    return iDays;
}
    


function fnValCloseOutTimeOffSet(thisId,val)
{
    //05/17/2011 Added parameter for option to enter unrestricted Offset value.
    //  val: 0 (or undefined) - <24 (hours), 1 unrestricted:
      
    //10/01/09 Copied from BreadItems.ascx:
    //05/16/11 Used in Setup, BreadItems.ascx, Customer, DaysRestricterOrdering.apsx. Moved here:
    //alert("313 fnValCloseOutTimeOffset thisId = " + thisId);
    //var sCloseOutTimeOffSet = document.getElementById("BreadItems1_tbxCloseOutTimeOffSet").value;
    var sCloseOutTimeOffSet = document.getElementById(thisId).value;
    //alert(" 320 sCloseOutTimeOffSet = " + sCloseOutTimeOffSet);
    if (sCloseOutTimeOffSet =="") {return true;}
    
    //Continue:
    sCloseOutTimeOffSet = fnTrimB(sCloseOutTimeOffSet,0);
    //alert("280 sCloseOutTimeOffSet = " + sCloseOutTimeOffSet + ", thisId = " + thisId + ", val = " + val);
           
    var pos = sCloseOutTimeOffSet.indexOf(":",0);
    if (pos==-1) 
    {
        sCloseOutTimeOffSet += ":00";
    }
    else
    {
         if (pos==0) {sCloseOutTimeOffSet="0" + sCloseOutTimeOffSet;}
         if (pos==sCloseOutTimeOffSet.length-1) {sCloseOutTimeOffSet+=":00";}
    }
  

    pos = sCloseOutTimeOffSet.indexOf(":",0);   //Redo:
    var sHours = sCloseOutTimeOffSet.substr(0,pos);
    var sMins = sCloseOutTimeOffSet.substr(pos + 1);
    
    var sTemp=sHours + sMins;
    //alert("380 sTemp = " + sTemp);
    if (isNaN(sTemp)==true)
    {
        alert("Numbers only (" + sCloseOutTimeOffSet + ")");
        return false;
    }

    //05/17/2011:
    if (val != 1) {
        if (parseInt(sHours, 10) > 23) {
            //alert(sCloseOutTimeOffSet + " is invalid");
            alert("Restricted to less than 24 hours (" + sCloseOutTimeOffSet + ")");
            return false;
        }
    }
    
    //alert("50 sHours = " + sHours);
    //12/27/08 Use Math.abs() to correct if -ve value entered:
    sHours=Math.abs(parseInt(sHours,10).toString());
    //alert("52 sHours = " + sHours);
    //if ( (sHours.length==2) & (sHours.substr(0,1)==0) ) { sHours=sHours.substr(1); }
    
    var dMins=parseInt(sMins,10).toString();
    //alert("57 dMins = " + dMins);
    if (dMins>59)
    {
        alert(sCloseOutTimeOffSet + " is invalid");
        return false;
    }
    
    ///////////////////////////////////
    ///////////////////////////////////
    temp=sHours + sMins;  //Refresh:
    //alert("67 sHours = " + sHours + ", sMins = " + sMins);
    if (parseInt(temp,10)==0)
    {
        temp="0:00";
    }
    else
    {
        //fnMyRound2(0,2)=0:00 - No good here:
        temp=sHours + ":" + (sMins+"00").substring(0,2);
        //alert("76 temp = " + temp);
    }
    document.getElementById(thisId).value=temp;
    return true;
} 


function fnGetDayOfWeek(val, blnAbbr)
{
    //alert("2890 utils fnGetDayOfWeek val = " + val + ", blnAbbr = " + blnAbbr);
    //12/15/09 ???? Cusanos.fnCloseOutTimeHasPassed() doesn't exist.
    //11/12/09 new. Used for Cusanos.fnCloseOutTimeHasPassed(). Also, Customer.fnWriteMsgDaysConflict():
    //  NOTE: aryWeekDays in fnWriteMsgDaysConflict() includes "Random". OK to keep it here:
    var aryWeekDays=new Array("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Random");
    var sDayOfWeek = aryWeekDays[val].toString();
    if (blnAbbr==true) {sDayOfWeek=sDayOfWeek.substr(0,3);} 
    //alert("2896 sDayOfWeek = " + sDayOfWeek);
    return sDayOfWeek;   
}


function fnGetClientHeight() {
    //06/05/10 First used for SchedemilingFreq. for dsome reason document.body.clientHeight doesn't return correct value:
    var myWidth = 0, myHeight = 0;
    if (typeof (window.innerWidth) == 'number') {
        //alert("Non-IE");
        myWidth = window.innerWidth;
        myHeight = window.innerHeight;
    } else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
        //alert("IE 6+ in 'standards compliant mode'");
        myWidth = document.documentElement.clientWidth;
        myHeight = document.documentElement.clientHeight;
    } else if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
        //alert("IE 4 compatible");
        myWidth = document.body.clientWidth;
        myHeight = document.body.clientHeight;
    }
    //window.alert('fnGetClientHeight Width = ' + myWidth);
    //window.alert('fnGetClientHeight Height = ' + myHeight);
    return { W: myWidth, H: myHeight };
}


function fnGetRowIndex2(thisId) {
    //10/05/10 Moved here from DragNDrop for use also in PageSecurityLevel:
    
    //04/10/10 Copied from Cusanos.fnGetControlIndex()
    //04/28/08 Change this to also make useable with ddl controls:
    //alert("1112 fnGetControlIndex()");
    var pos = thisId.indexOf("_ctl", 0);
    //var pos2=thisId.indexOf("_t",0); 
    var pos2 = thisId.lastIndexOf("_", thisId.length - 1);
    //alert("695 Cusanos.js fnGetControlIndex pos = " + pos + ", pos2 = " + pos2);
    index = parseInt(thisId.substr((pos + 4), (pos2 - pos - 1)), 10);
    //alert("700 index = " + index);
    if (index < 2) { index = 2; } //Limit top position:
    return index;
}


function stopRKey(evt) {
    //12/12/10 disable enter key. Pointer to stopRKey(evt):
    // REQUIRES CALL (in fnOnload()): document.onkeypress = stopRKey;
    var evt = (evt) ? evt : ((event) ? event : null);
    var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
    if ((evt.keyCode == 13) && (node.type == "text")) { return false; }
}


function fnIsThanksgiving(sDate)
{
    //12/18/10 Replicates OrderEntryCommon.IsThanksgiving().
    //  Need in Customer.js TermsGracePeriodEndDate:
    //alert("fnIsThanksgiving sDate = " + sDate);
    
    /////////////////////////////////////////
    /*
    12/18/10 Smart Pen www.livescribe.com $99
    Records what you write
    Records what it hears
    Tap it and it plays back
    Computer connection
    */
    /////////////////////////////////////////
    
    blnIsThanksGiving = false;
    //sDate = "11/03/2008"       'Test:
    var dDate = new Date(sDate);
    var monthOfDate  = dDate.getMonth();
    var dayOfDate  = dDate.getDate();
    var yearOfDate = dDate.getFullYear();
    //alert( monthOfDate  + "/" + dayOfDate + "/" + yearOfDate);

    var lastDateNovember = new Date("11/30/" + yearOfDate);
    var DOW = lastDateNovember.getDay();
    //alert("lastDateNovember = " + lastDateNovember + ", DOW = " + DOW);

    var dDays = (DOW >= 4) ? (DOW - 4) : (7 + DOW - 4);
    
    ///////////////////////////////////////////////////
    lastDateNovember.setDate(lastDateNovember.getDate() - dDays);   //Decrements lastDateNovember to ThanksgivingDate:
    var thanksgivingDate = new Date(lastDateNovember);
    //alert("thanksgivingDate = " + thanksgivingDate);
    ///////////////////////////////////////////////////

    var monthThanksgiving  = thanksgivingDate.getMonth();
    var dayThanksgiving  = thanksgivingDate.getDate();

    if ((monthOfDate == monthThanksgiving) & (dayOfDate == dayThanksgiving)) { blnIsThanksGiving = true; }
    //alert("blnIsThanksGiving = " + blnIsThanksGiving);
    
    return blnIsThanksGiving;
}


function fnWithinTermsGracePeriod(sWhich, sTermsGracePeriodEndDate) {
    //12/23/10 Called from Customer.js, Master_OrderEntry.js, CallLists_OrderEntry.js:
    //Compare sTermsGracePeriodEndDate with now() to determine whether still within payment grace period:
    //sWhich 0 - Customer.js, 1 - Master_OrderEntry.js, CallLists_OrderEntry.js:
    //  Master_OrderEntry.js, CallLists_OrderEntry.js compare with txtEventdate. Customer.js compares with now():
    //alert("40 fnWithinTermsGracePeriod(sTermsGracePeriodEndDate = " + sTermsGracePeriodEndDate);


    //Compare wth delivery date. Maybe now():
    var today = new Date();
    //Strip off time:
    var dayToday = today.getDate();
    var monthToday = today.getMonth();
    var fullYearToday = today.getFullYear();
    var sDeliveryDate = (monthToday+1).toString() + "/" + dayToday.toString() + "/" + fullYearToday.toString();
    
    if (sWhich == 1) {
        sDeliveryDate = document.getElementById("txtEventDate").value;
    }
    var dDeliveryDate = new Date(sDeliveryDate);
    var dTermsGracePeriodEndDate=new Date(sTermsGracePeriodEndDate);
    var dateDiff = (dTermsGracePeriodEndDate - dDeliveryDate);

    blnReturn = false;
    //04/01/2011 Correction >= was >. Needs to include 0:
    if (dateDiff >= 0) { blnReturn=true; }

    return blnReturn;  
}


function fnBlink(sWhich) {
    //12/16/10 Attempt at blinking.
    //alert("720 fnBlink sWhich = " + sWhich);
    //  First used for CustomerCredit - Credit limit exceeded. Blink red:

    var aryThisIdS = new Array();
    var aryThisIdS2 = new Array();
    var myVal = "";

    var cnt = -1;
    var sColor = "";
    var sCtrls = "";
   
    
    switch (sWhich) {
        case "1":
            {
                //CustomerCredit textboxes:
                sCtrls = "CustomerCredit_tbxCurrentBalance~CustomerCredit_tbxAgingCategory1~CustomerCredit_tbxAgingCategory2~CustomerCredit_tbxAgingCategory3~CustomerCredit_tbxAgingCategory4";
                break;
            }
    }

    aryThisIdS = sCtrls.split("~");
  
    //Loop through aryThisIdS to find the background color of the first value >0:
    for (var i = 0; i < aryThisIdS.length; i++) {
        myVal = document.getElementById(aryThisIdS[i].toString()).value;
        if (myVal != "") {
            //NOTES: CustomerCredit uses parseInt():
            //  This logic was designed for CustomerCredit. It may not be appropriate elsewhere:
            if (parseInt(myVal, 10) > 0) {
                sColor = document.getElementById(aryThisIdS[i].toString()).style.backgroundColor;
          
                ////////////////////////////////
                if (sColor == "red") {
                    cnt += 1;
                    aryThisIdS2[cnt] = aryThisIdS[i].toString(); 
                }
                ////////////////////////////////

            }
        }
    }
    
    
    ////////////////////////////////
    fnTimerBlink(aryThisIdS2,"red");
    ////////////////////////////////
}


function fnTimerBlink(aryThisIdS2, sColor) {
    //Called from fnBlink():
    for (var i = 0; i < aryThisIdS2.length; i++) {
        myVal = document.getElementById(aryThisIdS2[i].toString()).value;
        if (myVal == "") { myVal = "0"; }
        if (parseInt(myVal, 10) > 0) {
            var hgf = document.getElementById(aryThisIdS2[i].toString()).style.backgroundColor;
            if (sColor == "red") {
                document.getElementById(aryThisIdS2[i].toString()).style.backgroundColor = "white";
            }
            else {
                document.getElementById(aryThisIdS2[i].toString()).style.backgroundColor = "red";
            }
        }
    }

    sColor = (sColor == "white")?"red":"white";
    
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    myTimerBlink = setTimeout(fnTimerBlink(aryThisIdS2, sColor), 500);    //500 arbitrary:
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
   
}


function fnCompareEarliestLatest(thisId, allOrNothing, showMsg, blnUse24HourTime) {
    //03/12/2011 Added blnUse24HourTime:
    //02/12/2011 moved from customer.js for use also in googleMap_ctrl.ascx:
    //alert("1759 thisId = " + thisId);
    //allOrNothing - has to be false when called from HTML. But true when called from fnValRoutes():

    if (blnUse24HourTime == true) {
        if (fnValTime24hr(thisId) != true) { return false; }
    }
    else {
        if (fnValTime(thisId) != true) { return false; }
    }

    var myIdEarliest = "";
    var myIdLatest = "";
    if (thisId.indexOf("Earliest", 0) > -1) {
        myIdEarliest = thisId;
        myIdLatest = thisId.replace(/Earliest/g, "Latest");
    }
    else {
        myIdLatest = thisId;
        myIdEarliest = thisId.replace(/Latest/g, "Earliest");
    }
    //alert("1775 myIdEarliest = " + myIdEarliest + ", myIdLatest = " + myIdLatest);

    var sEarliestTime = document.getElementById(myIdEarliest).value;
    var sLatestTime = document.getElementById(myIdLatest).value;
    //alert("1780 sEarliestTime=" + sEarliestTime + ", sLatestTime=" +sLatestTime);
    blnOK = true;
    if ((sEarliestTime != "") & (sLatestTime != "")) {
        earliestTime = fnConvert12To24HrTime(sEarliestTime);
        latestTime = fnConvert12To24HrTime(sLatestTime);
        //alert("1783 earliestTime = " + earliestTime.toString() + ", latestTime = " + latestTime.toString());

        if ( (parseFloat(earliestTime, 10)>=12 ) & (parseFloat(latestTime, 10)<12) ) {
            //i.e. 11:30pm to 2:00am (next day). Allow:
        }
        else {
            if (parseFloat(latestTime, 10) <= parseFloat(earliestTime, 10)) {
                blnOK = false;
            }
        }
    }
    else {
        //alert("1829 allOrNothing = " + allOrNothing);
        //alert("1830 sEarliestTime=" + sEarliestTime + ", sLatestTime=" +sLatestTime);
        if (allOrNothing == true) {
            if (((sEarliestTime != "") & (sLatestTime == "")) || ((sEarliestTime == "") & (sLatestTime != ""))) {
                //alert("1834");
                blnOK = false;
            }
        }
    }

    if (blnOK != true) {
        if (showMsg == true) { alert("Bad Delivery earliest/latest times"); }
    }
    return blnOK;
}


function fnGetBrowserName() {
    //04/06/2011 New. IE, firefox and Safari all screen iframe with different dimensions.
    //  First use for iframeDatePickerGR:
    var sBrowserName = "";
    if (navigator.appName == "Microsoft Internet Explorer") {
        sBrowserName = "IE";
    }
    else {
        var sUserAgent = navigator.userAgent;
        if (sUserAgent.indexOf("Firefox", 0) > -1) {
            sBrowserName = "Firefox";
        }
        else {
            if (sUserAgent.indexOf("Safari", 0) > -1) {
                sBrowserName = "Safari";
            }
            else {
                //No further examination at this time:
                sBrowserName = "unknown";
            }
        }
    }
    return sBrowserName;
}


function fnOpenExcel_Email(val)
{
    //12/21/2011 Moved here from Customer.js for use from AR.aspx:
    
    //alert("300 Utils fnOpenExcel_Email val = " + val);
    //val: where it's coming from. 11 - CustPricing.Excel CustPricing, 12 - Customer/ARExcel Outstanding Invoices, 13 - Customer/AR.Email outstanding invoices: 
    //  22 - AR.Excel, 23 - AR.Email:
    
    var scrWidth=screen.availWidth;
    var scrHeight=screen.availHeight;
    var pageWidth=300; 
    var pageHeight=100;
    var scrLeft=0;  //(scrWidth-pageWidth)/2; 
    var scrTop=(scrHeight-pageHeight);  //Open at the bottom for minimal noticeability! 
    
    var sCustomerID="0"; 
    var sCustomerTypeID="0"; 
    var sCustLast="";
    var sTo ="";
    var sQueryString="";
    var sCompanyName=""

    var sSalesTerritoriesSelectedIDs  = "";
    var sHoldSelectedIDs  = "";
    var sSortOrderMode = "";
    var sStatus  = "";
    blnShowTempCOD  = false;
    var sLoggedUserID  = "";
    var sLoggedUserPosition = "";
    
    switch(val)
    {
        case 11:
        {
            //CustPricing Excel:
            sCustomerID=document.getElementById("CustPricing1_CustIDPricingBox").value; 
            sCustomerTypeID=document.getElementById("CustPricing1_tbxCustomerTypeID").value;
             
            sQueryString=val.toString() + "*" + sCustomerID + "*" + sCustomerTypeID;
            break;
        }
        case 12:
        {
            //Outstanding Invoices Excel:
            sCustomerID=document.getElementById("tbxDriverID").value; 
            
            sQueryString=val.toString() + "*" + sCustomerID; 
            break;
        }
        case 13:
        {
            //Outstanding Invoices Email (Outlook):
            sCustomerID=document.getElementById("tbxDriverID").value; 
            sCustLast=document.getElementById("tbxLastName").value; 
            sTo=document.getElementById("tbxAPEmail").value; 
            var index = document.getElementById("ddlCompanyName").selectedIndex;
            sCompanyName = document.getElementById("ddlCompanyName").options[index].text;
            
            sQueryString=val.toString() + "*" + sCustomerID + "*" + sCustLast+ "*" + sCompanyName + "*" + sTo; 
            break;
        }
        
        case 22:
        {
            
            //AR Excel:
            var sSalesTerritoriesSelectedIDs  = document.getElementById("ddlSalesTerritoriesMS_tbxSelectedIDs").value;
            var sHoldSelectedIDs  = document.getElementById("ddlHoldMS_tbxSelectedIDs").value;
            var sSortOrderMode  = document.getElementById("tbxSortOrderMode").value;
            var sStatus  = document.getElementById("ddlStatus").value;
            blnShowTempCOD  = document.getElementById("cbxShowTempCOD").checked;
            var sLoggedUserID  = document.getElementById("tbxLoggedUserID").value;
            var sLoggedUserPosition = document.getElementById("tbxLoggedUserPosition").value;
            
            sQueryString=val.toString() + "*" + sCustomerID + "*" + sCustLast+ "*" + sCompanyName + "*" + sSalesTerritoriesSelectedIDs + "*" + sHoldSelectedIDs + "*" + sSortOrderMode + "*" + sStatus + "*" + blnShowTempCOD + "*" + sLoggedUserID + "*" + sLoggedUserPosition;
             
            break;
        }
        
        case 23:
        {
            //AR Email (Outlook):
            var sSalesTerritoriesSelectedIDs  = document.getElementById("ddlSalesTerritoriesMS_tbxSelectedIDs").value;
            var sHoldSelectedIDs  = document.getElementById("ddlHoldMS_tbxSelectedIDs").value;
            var sSortOrderMode  = document.getElementById("tbxSortOrderMode").value;
            var sStatus  = document.getElementById("ddlStatus").value;
            blnShowTempCOD  = document.getElementById("cbxShowTempCOD").checked;
            var sLoggedUserID  = document.getElementById("tbxLoggedUserID").value;
            var sLoggedUserPosition = document.getElementById("tbxLoggedUserPosition").value;
            
            sQueryString=val.toString() + "*" + sCustomerID + "*" + sCustLast+ "*" + sCompanyName + "*" + sSalesTerritoriesSelectedIDs + "*" + sHoldSelectedIDs + "*" + sSortOrderMode + "*" + sStatus + "*" + blnShowTempCOD + "*" + sLoggedUserID + "*" + sLoggedUserPosition + "*" + sTo;
             
            break;
        }
        
    }
    //alert("300 Utils sQueryString = " + sQueryString);

    var attribs="left=" + scrLeft + "px, top=" +scrTop + "px, width=" + pageWidth + "px, height=" + pageHeight + "px, "
    attribs +="toolbar=no, location=no, directories=no, menubar=no, resizable=yes, status=no, scrollbars=no";
       
    //Adding new attributes didn't remove address bar etc at the top! 
    //////       1. width=300
    ////// Use this to define the width of the new window. 

    //////2. height=200
    ////// Use this to define the height of the new window. 

    //////3. resizable=yes or no
    ////// Use this to control whether or not you want the user to be able to resize the window.
    ////// 
    //////4. scrollbars=yes or no
    ////// This lets you decide whether or not to have scrollbars on the window. 

    //////5. toolbar=yes or no
    ////// Whether or not the new window should have the browser navigation bar at the top (The back, foward, stop buttons..etc.).
    ////// 
    //////6. location=yes or no
    ////// Whether or not you wish to show the location box with the current url (The place to type http://address).
    ////// 
    //////7. directories=yes or no
    ////// Whether or not the window should show the extra buttons. (what's cool, personal buttons, etc...).
    ////// 
    //////8. status=yes or no
    ////// Whether or not to show the window status bar at the bottom of the window. 

    //////9. menubar=yes or no
    ////// Whether or not to show the menus at the top of the window (File, Edit, etc...).
    ////// 
    //////10. copyhistory=yes or no
    ////// Whether or not to copy the old browser window's history list to the new window.

    //var attribs="dialogLeft:" + scrLeft + "px; dialogTop:" +scrTop + "px; dialogWidth:" + pageWidth + "px; dialogHeight:" + pageHeight + "px;"
    //attribs +=" unadorned:yes; resizable:yes; status:no; scroll:no";
    //alert(attribs);
    
    ////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////
    var myWin=window.open("Excel_Email.aspx?" + sQueryString,"",attribs);
    ////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////
    
    //Close Excel_Email.aspx as quickly as possible:
    //myWin.close();    //This is the fastest option. Don't activate until Thomas gets things going:
}


//////function fnCompareEarliestLatest24hr(thisId, allOrNothing, showMsg) {
//////    //02/12/2011 moved from customer.js for use also in googleMap_ctrl.ascx:
//////    //alert("1759 thisId = " + thisId);
//////    //allOrNothing - has to be false when called from HTML. But true when called from fnValRoutes(): 

//////    if (fnValTime24hr(thisId) != true) { return false; }

//////    var myIdEarliest = "";
//////    var myIdLatest = "";
//////    if (thisId.indexOf("Earliest", 0) > -1) {
//////        myIdEarliest = thisId;
//////        myIdLatest = thisId.replace(/Earliest/g, "Latest");
//////    }
//////    else {
//////        myIdLatest = thisId;
//////        myIdEarliest = thisId.replace(/Latest/g, "Earliest");
//////    }
//////    //alert("1775 myIdEarliest = " + myIdEarliest + ", myIdLatest = " + myIdLatest);

//////    var sEarliestTime = document.getElementById(myIdEarliest).value;
//////    var sLatestTime = document.getElementById(myIdLatest).value;
//////    //alert("1780 sEarliestTime=" + sEarliestTime + ", sLatestTime=" +sLatestTime);
//////    blnOK = true;
//////    if ((sEarliestTime != "") & (sLatestTime != "")) {
//////        earliestTime = fnConvert12To24HrTime(sEarliestTime);
//////        latestTime = fnConvert12To24HrTime(sLatestTime);
//////        //alert("1783 earliestTime = " + earliestTime.toString() + ", latestTime = " + latestTime.toString());

//////        if ((parseFloat(earliestTime, 10) >= 12) & (parseFloat(latestTime, 10) < 12)) {
//////            //i.e. 11:30pm to 2:00am (next day). Allow:
//////        }
//////        else {
//////            if (parseFloat(latestTime, 10) <= parseFloat(earliestTime, 10)) {
//////                blnOK = false;
//////            }
//////        }
//////    }
//////    else {
//////        //alert("1829 allOrNothing = " + allOrNothing);
//////        //alert("1830 sEarliestTime=" + sEarliestTime + ", sLatestTime=" +sLatestTime);
//////        if (allOrNothing == true) {
//////            if (((sEarliestTime != "") & (sLatestTime == "")) || ((sEarliestTime == "") & (sLatestTime != ""))) {
//////                alert("1834");
//////                blnOK = false;
//////            }
//////        }
//////    }

//////    if (blnOK != true) {
//////        if (showMsg == true) { alert("Bad Delivery earliest/latest times"); }
//////    }
//////    return blnOK;
//////}   
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//XXXXXXXXXXXXXXXXXXXX OBSOLETE XXXXXXXXXXXXXXXXXXXXX
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//XXXXXXXXXXXXXXXXXX END OBSOLETE XXXXXXXXXXXXXXXXXXX
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

