//Copyright 2000. Compendium Research Corporation. All Rights Reserved.

var bDebug = 0;        // change to 1 for extra output
var bPrincipal = 1;    // display principal column
var sNl = "\r";
var sOut = "";         // html output string
var nErrorNumber = -1.2345e-12;
var bDisplayAverageInterest = 0;
var sSub = "%s";

var shStart = "<html>" + sNl
      + "<head><title>%s</title></head>" + sNl
      + "<link rel='stylesheet' href='normal.css'>" + sNl
      + "<body>" + sNl
      + "<BASEFONT FACE='Arial, Helvetica, sans-serif'>";

var shHeading1 =  "<TABLE border=0 cellpadding=0 "
                + "cellspacing=0 width=500>" + sNl
      + "<br><TR><TD align=left>" + sNl
      + "<font size=+2><b>%s</b></font></TD></TR></TABLE><br>";

var shHeadGif = "<TABLE width=580 cellpadding=0 cellspacing=0>" + sNl
      + "<TR><TD><img src='%s' " + sNl
      + "width=367 height=20></TD></TR></TABLE>";

var shHeading2 =  "<TABLE border=0 cellpadding=0 "
                + "cellspacing=0 width=500>" + sNl
      + "<br><TR align=center><TD>" + sNl
      + "<font size='+0'><b>%s</b></font></TD></TR></TABLE><br>";

var shBeginTable1 =
      "<TABLE border=0 cellpadding=0 cellspacing=0 width=500>";
var shBeginTable2 =
      "<TABLE border=1 cellpadding=2 cellspacing=0 width=500>";
var shEndTable = "</table>";

var shBeginRow = "<tr>";
var shEndRow = "</tr>";

var shCell1 = "<td class='tdData'>%s</td>";
var shCell2 = "<td class='tdData'>%s</td>";
var shCell3 = "<th class='tdHead'>%s</th>";
var shCell4 = "<td class='tdDataRight'>%s"
                + "</td>";
var shCell5 = "<th class='tdDataRight'>%s"
                + "</th>";
var shEndPage = "<p><a href='javascript: history.go(-1)'>Back</a>"

function EmitRow1(s1, s2)
{
   Emit(shBeginRow);
   Emit1(shCell1, s1);
   Emit1(shCell2, s2);
   Emit(shEndRow);
}

function EmitRow2(s1, s2, sx, s3, s4, s5)
{
   Emit(shBeginRow);
   Emit1(shCell3, s1);
   Emit1(shCell3, s2);
   if (bPrincipal)
      Emit1(shCell3, sx);
   Emit1(shCell3, s3);
   Emit1(shCell3, s4);
   Emit1(shCell3, s5);
   Emit(shEndRow);
}

function EmitRow3(s1, s2, sx, s3, s4, s5)
{
   Emit(shBeginRow);
   Emit1(shCell2, s1);
   Emit1(shCell2, s2);
   if (bPrincipal)
      Emit1(shCell4, Format(sx, 0, 2));
   Emit1(shCell4, Format(s3, 0, 2));
   Emit1(shCell4, Format(s4, 0, 2));
   Emit1(shCell4, Format(s5, 0, 2));
   Emit(shEndRow);
}

function EmitRow4(s1, s2, sx, s3, s4, s5)
{
   Emit(shBeginRow);
   Emit1(shCell1, s1);
   Emit1(shCell1, s2);
   if (bPrincipal)
      Emit1(shCell5, Format(sx, 0, 2));
   Emit1(shCell5, Format(s3, 0, 2));
   Emit1(shCell5, Format(s4, 0, 2));
   Emit1(shCell5, Format(s5, 0, 2));
   Emit(shEndRow);
}

function EmitRow5(s1, s2)
{
   Emit(shBeginRow);
   Emit1(shCell1,s1);
   Emit1(shCell4, s2);
   Emit(shEndRow);
}

function Execute(oForm)
{
   var iError = 0;
   var nPrincipal, nInputInterest, iYears, iStartMonth;
   var iStartYear, bShowFull, iPrepayMethod, nPrepay, iPrepayPeriod;
   var iPrepayUse, iPpy;

   if ( (nPrincipal = Validate(oForm.apph0001.value, 0)) == nErrorNumber)
      iError = 100;
   else if ( (nInputInterest = Validate(oForm.apph0002.value, 0))
				== nErrorNumber)
      iError = 110;
   else if ( (iYears = Validate(oForm.apph0003.value, 1)) == nErrorNumber)
      iError = 120;
   else if ( (iStartMonth = parseInt(oForm.apph0004.options[
   		oForm.apph0004.selectedIndex].value)) == 0)
      iError = 130;
   else if ( (iStartYear = Validate(oForm.apph0005.value, 1))
				== nErrorNumber)
      iError = 140;
   else
   {
      bShowFull = parseInt(oForm.apph0006.options[oForm.apph0006.selectedIndex].value);

      // var iPpy = parseInt(oForm.apph0007.value);
      iPpy = 12;

      iPrepayMethod = parseInt(oForm.apph0008.options[oForm.apph0008.selectedIndex].value);
      if ( (nPrepay = Validate(oForm.apph0009.value, 0)) == nErrorNumber)
	 iError = 150;
      else if ( (iPrepayPeriod = Validate(oForm.apph0010.value, 1))
				== nErrorNumber)
	 iError = 160;

      // var iPrepayUse = parseInt(oForm.apph0011.value);
      var iPrepayUse = 1;
                // 0=reduce payment; 1=pay off early
   }

   var nHalfPrepay = Round(nPrepay/2, 2);

   var iMonths = 12 * iYears;
   var iPeriods = iPpy * iYears;
   var nPercent = nInputInterest / iPpy / 100;

   var nPrepayPv = 0, nPayment = 0;
   var bSemaPrepay = 0, bPeriodPrepay = 0;
   var bAnnPrepay = 0, bSinglePrepay = 0;

   var sSummPayment = "N/A", sSummInterest1 = "N/A",
         sSummInterest2 = "N/A", sSummInterest3 = "N/A",
         sSummYrsReduction = "N/A", sSummAvgInterest1 = "N/A",
         sSummAvgInterest2 = "N/A", sSummAvgInterest3 = "N/A";
   var iReducedMonths = 0;

// alert(iError);

   if (!iError)
   {
      switch (iPrepayMethod)
      {
         case 0:        // None: No Prepayments
         default:
            break;

         case 1:        // Monthly: Pre-pay a set amount each month
         case 2:        // Biweekly: Pre-pay a set amount every two
                                // weeks
            bPeriodPrepay = 1;
            break;

         case 3:        // Semiannually: Prepay a set amount every 6 months
            bSemaPrepay = 1;
            break;

         case 4:        // Annually: Pre-pay a set amount once each year
//          bSemmPrepay = 1;
            bAnnPrepay = 1;
            break;

         case 5:        // One Time: Pre-pay one set amount
                        // after a given # of months
            bSinglePrepay = 1;
            break;
      }

      var sMessage = "";
      if (nPrincipal == 0)
	 sMessage = "Please enter a principal loan balance";
      else if (nPrincipal < 0)
	 sMessage = "Principal loan balance must be positive";
      else if (nPercent == 0)
	 sMessage = "Please enter an interest rate";
      else if (nPercent < 0)
	 sMessage = "Interest rate must be positive";
      else if (iYears == 0)
	 sMessage = "Please enter an amortization length";
      else if (iYears < 0)
	 sMessage = "Amortization length must be positive";
      else if (iYears > 30)
	 sMessage = "Amortization length must be 30 years or less";
      else if (iStartYear == 0)
	 sMessage = "Please enter a starting year";
      else if (iPrepayMethod && !nPrepay)
	 sMessage = "Please enter a pre-payment amount";
      else if (nPrepay && iPrepayMethod == 0)
	 sMessage = "Please select a pre-payment method";
      else if (nPrepay < 0)
	 sMessage = "Pre-payment amount must be positive";
      else if (bSinglePrepay &&
		(iPrepayPeriod <= 0 || iPrepayPeriod > iPeriods))
	 sMessage = "Please enter the appropriate month # for "
			+ "one-time pre-payment";
      else if (!bSinglePrepay && iPrepayPeriod)
	 sMessage = "The one-time pre-payment field should "
		     + "be left blank unless you have selected the "
		     + "one-time pre-payment method above";
      if (sMessage != "")
      {
	 iError = 170;
	 alert(sMessage);
      }
   }

   if (!iError)
   {
      if (bPeriodPrepay)
         nPrepayPv += PresentValue(nPrepay, nPercent, iPeriods);

//    if (bSemmPrepay)
//       nPrepayPv += PresentValue(nHalfPrepay, nPercent/2,
//                          iPeriods*2);
      if (bSemaPrepay)
         nPrepayPv += SparsePresentValue(nPrepay, nPercent,
                        iPpy/2, iPeriods);

      if (bAnnPrepay)
         nPrepayPv += SparsePresentValue(nPrepay, nPercent,
                        iPpy, iPeriods);

      if (bSinglePrepay)
         nPrepayPv += SparsePresentValue(nPrepay, nPercent,
                        iPrepayPeriod, iPrepayPeriod);

      nPayment = (iPrepayUse ? nPrincipal : (nPrincipal - nPrepayPv))
                        / PresentValue(1, nPercent, iPeriods);
      nPayment = Round(nPayment + .005, 2);

      sSummPayment = nPayment;

      if (nPrepayPv > 0)
      {
	 var nFraction = 0;
	 var nBalance = nPrincipal;
	 var nInterest, nReduction;

	 sSummInterest1 = 0;
	 for (var iK = 1; iK <= iMonths; ++iK)
	 {
	    nInterest = nBalance * nPercent + nFraction;
	    nFraction = nInterest - Round(nInterest,2);
	    nInterest = Round(nInterest,2);
	    if (nPayment > nBalance + nInterest)
	       nPayment = nBalance + nInterest;
	    nReduction = nPayment - nInterest;
	    nBalance -= nReduction;
            sSummInterest1 += nInterest;
	 }
	 nPayment = sSummPayment;
	 sSummAvgInterest1 = sSummInterest1 / iMonths;

         iReducedMonths = Math.round(Math.log(
                                1-nPercent*nPrincipal / nPayment)
                        / Math.log(1 - nPercent));
         sSummYrsReduction = (iMonths - iReducedMonths) / 12;
         sSummInterest2 = iReducedMonths * nPayment - nPrincipal
                                       + nPrepayPv;
         sSummInterest3 = sSummInterest1 - sSummInterest2;
         // sSummAvgInterest2 = sSummInterest3 / iReducedMonths;
         sSummAvgInterest2 = sSummInterest2 / iMonths;
         sSummAvgInterest3 = sSummAvgInterest1 - sSummAvgInterest2;
         sSummYrsReduction = Format(sSummYrsReduction, 0, 1);

         sSummInterest2 = DollarFormat(sSummInterest2, 0, 2);
         sSummInterest3 = DollarFormat(sSummInterest3, 0, 2);
         sSummAvgInterest2 = DollarFormat(sSummAvgInterest2, 0, 2);
         sSummAvgInterest3 = DollarFormat(sSummAvgInterest3, 0, 2);
      }

   }

   if (!iError)
   {
      Emit1(shStart, "<center><span class='tableHead'>Amortization Schedule Results<\/span><\/center>");

      Emit1(shHeading1, "<center><span class='tableHead'>Amortization Schedule Results<\/span><\/center>");
      Emit1(shHeading2, "<span class='tableHead'>MORTGAGE INFORMATION<\/span>");

      Emit(shBeginTable2);
      EmitRow1("Principal", DollarFormat(nPrincipal, 0, 2));
      EmitRow1("Interest Rate", nInputInterest + " %");
      EmitRow1("Amortization Period", iYears + " Years");
      EmitRow1("Starting month", Mmm(iStartMonth));
      EmitRow1("Starting year", iStartYear);
      EmitRow1("Monthly Mortgage Payment", DollarFormat(nPayment, 0, 2));
      if (nPrepayPv)
         EmitRow1(
            bPeriodPrepay ? "Monthly Pre-Payment Amount"
               : bSemaPrepay ? "Semiannual Pre-Payment Amount"
               : bAnnPrepay ? "Annual Pre-Payment Amount"
               : "One-Time Pre-Payment",
            DollarFormat(nPrepay, 0, 2)
                  + (bSinglePrepay ? (" paid after month "
                                + iPrepayPeriod) : ""));



//    if (bDebug)
//       EmitRow1("nPrepayPv*", DollarFormat(nPrepayPv, 0, 2));

      Emit(shEndTable);
   }

   if (!iError && bDebug)
   {
      Emit1(shHeading2, "ESTIMATED SUMMARY");

      Emit(shBeginTable2);
      EmitRow1("Reduced Months", iReducedMonths);
      EmitRow1("Prepayment Present Value",
                                DollarFormat(nPrepayPv, 0, 2));
      EmitRow1("Monthly Payment", DollarFormat(sSummPayment, 0, 2));
      EmitRow1("Total Interest Without Pre-Payment",
                                DollarFormat(sSummInterest1, 0, 2));
      EmitRow1("Total Interest With Pre-Payment (if applicable)",
                                sSummInterest2);
      EmitRow1("Total Interest Saved", sSummInterest3);
      EmitRow1("Total Reduction in Years of Loan Length "
                        + "(if applicable)", sSummYrsReduction);
      if (bDisplayAverageInterest)
      {
	 EmitRow1("Average Interest Per Month Without Pre-Payment",
                                DollarFormat(sSummAvgInterest1, 0, 2));
	 EmitRow1("Average Interest Per Month With Pre-Payment (if applicable)",
                                sSummAvgInterest2);
	 EmitRow1("Average Interest Per Month Saved (if applicable)",
                                sSummAvgInterest3);
      }
      Emit(shEndTable);
   }

   if (!iError)
   {
      if (nPrepayPv == 0)
      {
         sSummInterest1 = 0;
         sSummAvgInterest1 = 0;
      }
      else
      {
         sSummInterest2 = sSummInterest3 = 0;
         sSummAvgInterest2 = sSummAvgInterest3 = 0;
      }



      Emit1(shHeading2, "<span class='tableHead'>AMORTIZATION SCHEDULE<\/span>");
      Emit(sNl + sNl + sNl);
      Emit(shBeginTable2);

      EmitRow2("Year", "Month", "Payment", "Principal",
                                "Interest", "Balance");

      var iK, iN;
      var nBalance = nPrincipal;
      var iMonth = iStartMonth, iYear = iStartYear;

      var nInterest, nReduction;
      var nPrepayment;
      var nSavedInterest = 0;

//    if (bSemmPrepay)
//       nSavedInterest = PresentValue(nHalfPrepay, -nPercent/2, 1);

      var bDetail = true, bEoy = 0, iStopK = 0;
      var sCol1, sCol2;
      var nYrPayment = 0, nYrPrincipal = 0, nYrInterest = 0;
      var nFraction = 0;

      for (var iK = 1; !iError && !iStopK && iK <= iPeriods; ++iK)
      {
         if (iPpy != 26)
         {
            sCol1 = iYear;
            sCol2 = Mmm(iMonth);
         }
         else
         {
            sCol1 = "Year # " + Math.floor((iK + iPpy - 1)/iPpy);
            sCol2 = "Pmt # " + ((iK-1) % iPpy + 1);
         }

         nInterest = nBalance * nPercent - nSavedInterest + nFraction;
	 nFraction = nInterest - Round(nInterest,2);
	 nInterest = Round(nInterest,2);
         nYrInterest += nInterest;

         nPrepayment = 0;
         if (bPeriodPrepay || (bAnnPrepay && iK % iPpy == 0)
                        || (bSinglePrepay && iK == iPrepayPeriod))
            nPrepayment += nPrepay;

//       if (bSemmPrepay)
//          nPrepayment += nHalfPrepay;

         if (bSemaPrepay && (iK % (iPpy/2) == 0))
            nPrepayment += nPrepay;

         if (nPayment > nBalance + nInterest - nPrepayment)
         {
            nPrepayment = nBalance + nInterest - nPayment;
            if (nPrepayment < 0)
            {
               nPayment += nPrepayment;
               nPrepayment = 0;
            }
         }

         nYrPayment += nPayment + nPrepayment;

         nReduction = nPayment - nInterest;
         nBalance -= nReduction;
         if (bDetail)
            EmitRow3(sCol1, sCol2,
                     nPayment, nReduction, nInterest, nBalance);

         if (nPrepayPv == 0)
            sSummInterest1 += nInterest;
         else
            sSummInterest2 += nInterest;

         nBalance -= nPrepayment;

         if (bDetail && nPrepayment != 0)
               EmitRow3(sCol1, sCol2 + " Prepay",
                        nPrepayment, nPrepayment, 0, nBalance);
         if (Round(nBalance,2) == 0)
         {
            nBalance = 0;
            iStopK = iK;
         }

         if (bEoy || iK == iPeriods || iStopK)
         {
            nYrPrincipal = nYrPayment - nYrInterest;
            EmitRow4(sCol1, "TOTALS",
                     nYrPayment, nYrPrincipal, nYrInterest, nBalance);
            nYrPrincipal = nYrPayment = nYrInterest = 0;
         }

         if (iPpy == 26)
         {
            bEoy = (iK % 26 == 25);
            bDetail = (iK > 26);
         }
         else
         {
            bEoy = 0;
            if (iPpy == 12 || iK % 2 == 0)
               if (++iMonth > 12)
               {
                  iMonth = 1;
                  ++iYear;
                  bDetail = (iK < 3 * iPpy / 4);
                  // alert(iK + "/" + (3 * iPpy / 4) + "/" + bDetail);
               }
            if (iMonth == 12 && (iPpy == 12 || iK % 2 == 1))
               bEoy = 1;
         }
         if (bShowFull || bDebug)
            bDetail = true;
      }
      Emit(shEndTable);
   }

// alert(sOut);

   if (!iError)
   {
      if (nPrepayPv == 0)
      {
         sSummAvgInterest1 = sSummInterest1 / iMonths;
//       sSummAvgInterest1 = DollarFormat(sSummInterest1 / iMonths,
//                               0, 2);
//       sSummInterest1 = DollarFormat(sSummInterest1, 0, 2);
      }
      else
      {
         sSummYrsReduction = Format((iMonths - iStopK)/12, 0, 1);
         sSummAvgInterest2 = sSummInterest2 / iMonths;
         sSummInterest3 = sSummInterest1 - sSummInterest2;
         sSummAvgInterest3 = sSummAvgInterest1 - sSummAvgInterest2;
         sSummInterest2  = DollarFormat(sSummInterest2  , 0, 2);
         sSummAvgInterest2 = DollarFormat(sSummAvgInterest2, 0, 2);
         sSummInterest3  = DollarFormat(sSummInterest3  , 0, 2);
         sSummAvgInterest3 = DollarFormat(sSummAvgInterest3, 0, 2);
      }

      sSummPayment = DollarFormat(sSummPayment, 0, 2);
      sSummInterest1 = DollarFormat(sSummInterest1, 0, 2);
      sSummAvgInterest1 = DollarFormat(sSummAvgInterest1, 0, 2);


      Emit1(shHeading2, "<span class='tableHead'>SUMMARY<\/span>");

      Emit(shBeginTable2);
      EmitRow5("Monthly Payment", sSummPayment);
      EmitRow5("Total Interest Without Pre-Payment",
                                sSummInterest1);
      EmitRow5("Total Interest With Pre-Payment (if applicable)",
                                sSummInterest2);
      EmitRow5("Total Interest Saved (if applicable)", sSummInterest3);
      EmitRow5("Total Reduction in Years of Loan Length "
                        + "(if applicable)", sSummYrsReduction);
      if (bDisplayAverageInterest)
      {
	 EmitRow5("Average Interest Per Month Without Pre-Payment",
                                sSummAvgInterest1);
	 EmitRow5("Average Interest Per Month With "
      				+ "Pre-Payment (if applicable)",
                                sSummAvgInterest2);
	 EmitRow5("Average Interest Per Month Saved (if applicable)",
                                sSummAvgInterest3);
      }
      Emit(shEndTable);
      Emit(shEndPage);
   }

   if (!iError)
   {
//    document.open();
      document.write(sOut);
      document.close();

   }

}

function Emit(sStr) { sOut +=  sStr + sNl ; }

function Emit1(sStr, s1)
{
   var iK;
   if ( (iK = sStr.indexOf(sSub)) >= 0)
      sStr = sStr.substring(0, iK) + s1
                + sStr.substring(iK + sSub.length);
   Emit(sStr);
}

function Emitx(sStr, sSubs)
{
   var iK, iL, iPos1 = 0, iPos2 = 0;
   while ( (iK = sStr.indexOf(iPos1, sSub)) >= 0)
   {
      if ( (iL = eSubs.indexOf(iPos2, sSep)) < 0)
         iL = eSubs.length();
      sStr = sStr.substring(0, iK)
                        + sSubs.substring(iPos1, iL)
                        + sStr.substring(iK + sSub.length());
      iPos1 = iK + sSub.length();
      if ( (iPos2 = iL + eSep.length()) > sSubs.length())
         iPos2 = sSubs.length();
   }
   Emit(sStr);
}


var sBlanks = "                                             "
                + "                                          ";
var sDashes = "---------------------------------------------"
                + "-----------------------------------------";

var nPowers = [
        0.0000000000000001,
        0.000000000000001,
        0.00000000000001,
        0.0000000000001,
        0.000000000001,
        0.00000000001,
        0.0000000001,
        0.000000001,
        0.00000001,
        0.0000001,
        0.000001,
        0.00001,
        0.0001,
        0.001,
        0.01,
        0.1,
        1.,
        10.,
        100.,
        1000.,
        10000.,
        100000.,
        1000000.,
        10000000.,
        100000000.,
        1000000000.,
        10000000000.];

function Power(n)
{
    return nPowers[n+16];
}

var sMonths = ["January", "February", "March", "April", "May",
        "June", "July", "August", "September", "October",
        "November", "December"];

function Mmm(iK) { if (!(iK >= 1 && iK <= 12)) return "MMM" + iK;
            return sMonths[iK-1].substring(0, 3); }

function ExpN(nX, iN)   // compute x ** n, where n is integral
{
   var nResult = 1;
   var bSign = 0;
   if (iN < 0)
   {
      bSign = 1;
      iN = -iN;
   }
   while (iN > 0)
   {
      if (iN & 1)
         nResult *= nX;
      nX *= nX;
      iN >>= 1;
   }
   if (bSign)
      nResult = 1 / nResult;
   return nResult;
}

function PresentValue(nPayment, nPercent, iNumPeriods)
{
   var nAmount = (Math.abs(nPercent) > 1e-20)
         ? nPayment * (1 - ExpN(1 + nPercent, -iNumPeriods))
                                        / nPercent
         : nPayment * iNumPeriods;
   return nAmount;
}

function SparsePresentValue(nPayment, nPercent, iInterval, iNumPeriods)
{
   var nAmount = (Math.abs(nPercent) > 1e-20)
         ? nPayment * (1 - ExpN(1 + nPercent, -iNumPeriods))
                        / (ExpN(1 + nPercent, iInterval) - 1)
         : nPayment * iNumPeriods;
   return nAmount;
}

function Round(nVal, iD)
{
   var iSign = 1;
   if (nVal < 0)
   {
      nVal = - nVal;
      iSign = -1;
   }
   var iInt = Math.round(nVal);
   if (iD > 0)
      iInt = Math.floor(nVal);
   var nFp = nVal - iInt;
// alert ('iInt, nFp = ' + iInt + ", " + nFp);
   if (iD > 0)
      nFp = Math.round(nFp * Power(iD)) / Power(iD);
   nVal = iSign * (iInt + nFp);
   return nVal;
}

function Validate(sVal, bInt)
{
   var sMessage = "";
   var bDot = 0, bE = 0, iState = 0;
   var sCh, iK;
   var bInvalid = 0;
   var nValue = bInt ? parseInt(sVal) : parseFloat(sVal);

   for (iK = 0; sMessage == "" && iK < sVal.length; ++iK)
   {
      sCh = sVal.charAt(iK);
      if (sCh == " ")
      {
         if (iState > 0)
            iState = 9;
      }
      else
      {
         if (iState == 9)
            sMessage = "Number '" + sVal + "' has an embedded blank";
         else if (sCh == '.' && !bInt)
         {
            if (bDot || bE)
	       bInvalid = 1;
            else
               bDot = 1;
         }
         else if ((sCh == 'e' || sCh == 'E') && !bInt)
         {
            if (bE)
	       bInvalid = 1;
            else
            {
               bE = 1;
               iState = 6;
            }
         }
         else if (sCh == '+' || sCh == '-')
         {
            if (iState == 0 || iState == 6)
               ++iState;
            else
               sMessage = "Number '" + sVal + "' contains a sign "
                                + "in an illegal position"
         }
         else if (sCh >= '0' && sCh <= '9')
         {
            if (iState == 1 || iState == 7)
               ++iState;
            else if (iState == 0 || iState == 6)
               iState += 2;
         }
         else
	    bInvalid = 1;
      }
      if (bInvalid)
	 sMessage = "Number '" + sVal + "' contains"
                        + " invalid non-numeric character(s)";
   }

   if (sMessage == "")
      if (iState == 1 || iState == 6 || iState == 7)
         sMessage = "Illegal number: " + sVal;
      else if (iState == 0)
	 nValue = 0;
   if (sMessage != "")
   {
      alert(sMessage);
      nValue = nErrorNumber;
   }
   return nValue
}


// DollarFormat -- could be jazzed up to produce "CR" or "DB"
function DollarFormat(nVal, iW, iD)
{
   return GenFmt(nVal, iW, iD, 1);
}

function Format(nVal, iW, iD)
{
   return GenFmt(nVal, iW, iD, 0);
}

function GenFmt(nVal, iW, iD, bDollar) // format val into w chars,
                        // d digs after decimal point
{
   var sOut = "";
   var iSign = 0;
   nVal = Round(nVal, iD);
   if (nVal < 0)
   {
      nVal = - nVal;
      iSign = 1;
   }
   var iInt = Math.round(nVal);
   if (iD > 0)
      iInt = Math.floor(nVal);
   var nFp = nVal - iInt;
   var iDigs = 1;
   if (iInt > 9)
      iDigs = Math.floor(Math.log(iInt+.1)/Math.log(10)) + 1;
   var iLeft = iW - iSign - (bDollar ? 1 : 0);
   if (iD > 0)
      iLeft -= iD + 1;
   if (iLeft > iDigs)
      sOut += sBlanks.substring(0, iLeft - iDigs);
   if (iSign)
      sOut += '-';
   if (bDollar)
      sOut += '$';
   sOut += iInt;
   if (iD > 0)
   {
      nFp = Math.round((1 + nFp) * Power(iD));
      sOut += '.' + String(nFp).substring(1);
   }
   return sOut;
}

function PrepadString(sStr, iW)
{
   if (sStr.length < iW)
      sStr = sBlanks.substring(0, iW - sStr.length) + sStr;
   return sStr;
}

function CenterString(sStr, iW)
{
   var iBlanks = iW - sStr.length;
   if (iBlanks > 0)
      sStr = sBlanks.substring(0, Math.floor(iBlanks/2)) + sStr
                + sBlanks.substring(0, iBlanks - Math.floor(iBlanks/2));
   return sStr;
}