/*****************************************************************************/
//
// Mappings:
// CalendarMonth_ = CM_
// AnchorPosition_ = SP_
// LeadDayCalculator_ = LDC_
// CalendarPopup_ = CP_
// PopupWindow_ = PW_

// ===================================================================
// 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.
// ===================================================================

/*
AnchorPosition.js
Author: Matt Kruse
Last modified: 10/11/02

DESCRIPTION: These functions find the position of an <A> tag in a document,
so other elements can be positioned relative to it.

COMPATABILITY: Netscape 4.x,6.x,Mozilla, IE 5.x,6.x on Windows. Some small
positioning errors - usually with Window positioning - occur on the
Macintosh platform.

FUNCTIONS:
getAnchorPosition(anchorname)
  Returns an Object() having .x and .y properties of the pixel coordinates
  of the upper-left corner of the anchor. Position is relative to the PAGE.

getAnchorWindowPosition(anchorname)
  Returns an Object() having .x and .y properties of the pixel coordinates
  of the upper-left corner of the anchor, relative to the WHOLE SCREEN.

NOTES:

1) For popping up separate browser windows, use getAnchorWindowPosition.
   Otherwise, use getAnchorPosition

2) Your anchor tag MUST contain both NAME and ID attributes which are the
   same. For example:
   <A NAME="test" ID="test"> </A>

3) There must be at least a space between <A> </A> for IE5.5 to see the
   anchor tag correctly. Do not do <A></A> with no space.
*/

// getAnchorPosition(anchorname)
//   This function returns an object having .x and .y properties which are the coordinates
//   of the named anchor, relative to the page.
function getAnchorPosition(anchorname) {
  // This function will return an Object with x and y properties
  var useWindow=false;
  var coordinates=new Object();
  var x=0,y=0;
  // Browser capability sniffing
  var use_gebi=false, use_css=false, use_layers=false;
  if (document.getElementById) { use_gebi=true; }
  else if (document.all) { use_css=true; }
  else if (document.layers) { use_layers=true; }
  // Logic to find position
  if (use_gebi && document.all) {
    x=AP_getPageOffsetLeft(document.all[anchorname]);
    y=AP_getPageOffsetTop(document.all[anchorname]);
    }
  else if (use_gebi) {
    var o=document.getElementById(anchorname);
    x=AP_getPageOffsetLeft(o);
    y=AP_getPageOffsetTop(o);
    }
  else if (use_css) {
    x=AP_getPageOffsetLeft(document.all[anchorname]);
    y=AP_getPageOffsetTop(document.all[anchorname]);
    }
  else if (use_layers) {
    var found=0;
    for (var i=0; i<document.anchors.length; i++) {
      if (document.anchors[i].name==anchorname) { found=1; break; }
      }
    if (found==0) {
      coordinates.x=0; coordinates.y=0; return coordinates;
      }
    x=document.anchors[i].x;
    y=document.anchors[i].y;
    }
  else {
    coordinates.x=0; coordinates.y=0; return coordinates;
    }
  coordinates.x=x;
  coordinates.y=y;
  return coordinates;
  }

// getAnchorWindowPosition(anchorname)
//   This function returns an object having .x and .y properties which are the coordinates
//   of the named anchor, relative to the window
function getAnchorWindowPosition(anchorname) {
  var coordinates=getAnchorPosition(anchorname);
  var x=0;
  var y=0;
  if (document.getElementById) {
    if (isNaN(window.screenX)) {
      x=coordinates.x-document.body.scrollLeft+window.screenLeft;
      y=coordinates.y-document.body.scrollTop+window.screenTop;
      }
    else {
      x=coordinates.x+window.screenX+(window.outerWidth-window.innerWidth)-window.pageXOffset;
      y=coordinates.y+window.screenY+(window.outerHeight-24-window.innerHeight)-window.pageYOffset;
      }
    }
  else if (document.all) {
    x=coordinates.x-document.body.scrollLeft+window.screenLeft;
    y=coordinates.y-document.body.scrollTop+window.screenTop;
    }
  else if (document.layers) {
    x=coordinates.x+window.screenX+(window.outerWidth-window.innerWidth)-window.pageXOffset;
    y=coordinates.y+window.screenY+(window.outerHeight-24-window.innerHeight)-window.pageYOffset;
    }
  coordinates.x=x;
  coordinates.y=y;
  return coordinates;
  }

// Functions for IE to get position of an object
function AP_getPageOffsetLeft (el) {
  var ol=el.offsetLeft;
  while ((el=el.offsetParent) != null) { ol += el.offsetLeft; }
  return ol;
  }
function AP_getWindowOffsetLeft (el) {
  return AP_getPageOffsetLeft(el)-document.body.scrollLeft;
  }
function AP_getPageOffsetTop (el) {
  var ot=el.offsetTop;
  while((el=el.offsetParent) != null) { ot += el.offsetTop; }
  return ot;
  }
function AP_getWindowOffsetTop (el) {
  return AP_getPageOffsetTop(el)-document.body.scrollTop;
  }

/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/

// ===================================================================
// 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. Instead,
// please just point to my URL to ensure the most up-to-date versions
// of the files. Thanks.
// ===================================================================


/*
CalendarPopup.js

DESCRIPTION: This object implements a popup calendar to allow the user to
select a date, month, quarter, or year.

COMPATABILITY: Works with Netscape 4.x, 6.x, IE 5.x on Windows. Some small
positioning errors - usually with Window positioning - occur on the
Macintosh platform.
The calendar can be modified to work for any location in the world by
changing which weekday is displayed as the first column, changing the month
names, and changing the column headers for each day.

NOTES:
1) Requires the functions in AnchorPosition.js and PopupWindow.js

2) Your anchor tag MUST contain both NAME and ID attributes which are the
   same. For example:
   <A NAME="test" ID="test"> </A>

3) There must be at least a space between <A> </A> for IE5.5 to see the
   anchor tag correctly. Do not do <A></A> with no space.

4) When a CalendarPopup object is created, a handler for 'onmouseup' is
   attached to any event handler you may have already defined. Do NOT define
   an event handler for 'onmouseup' after you define a CalendarPopup object
   or the autoHide() will not work correctly.

5) The calendar display uses "graypixel.gif" which is a 1x1 gray pixel of
   color #C0C0C0. If this file is not present, the calendar display should
   still be fine but will not show the gray lines.

6) The calendar popup display uses style sheets to make it look nice.

*/

// CONSTRUCTOR for the CalendarPopup Object

function CalendarPopup() {
        var c;
        if (arguments.length>0 && !document.layers) {
        //if (arguments.length>0){
                c = new PopupWindow(arguments[0]);
                c.calendarDiv = arguments[0];
                }
        else {
                c = new PopupWindow();
                c.setSize(170,300);
                }
        c.offsetX = -152;
        c.offsetY = 25;
        c.setHideListener( CP_hideListener);
        c.autoHide();
        // Calendar-specific properties
        c.monthNames = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
        c.dayHeaders = new Array("S","M","T","W","T","F","S");
        c.weekStartDay = 0;
        c.displayType = "date";
        c.disabledWeekDays = new Object();
        c.currentDate = null;
        c.graypixelImage = "images/graypixel.gif";
        c.legendImage = "images/calegend.gif";
        c.legendLongDesc = "";
        c.minDate = null;
        c.maxDate = null;
        c.holidays = null;
        c.leadDays = -1;
        window.CP_targetInput = null;
        window.CP_deliverByDiv = null;
        window.CP_dateFormat = "mm/dd/yyyy";
        window.CP_parseDateFormat = "m/d/yyyy";
        window.CP_formElements = null;
        // Method mappings
        c.setMonthNames = CP_setMonthNames;
        c.setDayHeaders = CP_setDayHeaders;
        c.setWeekStartDay = CP_setWeekStartDay;
        c.setDisabledWeekDays = CP_setDisabledWeekDays;
        c.setFormElements = CP_setFormElements;
        c.showCalendar = CP_showCalendar;
        c.hideCalendar = CP_hideCalendar;
        c.getStyles = CP_getStyles;
        c.getHiddenLayer = CP_getHiddenLayer;
        c.refreshCalendar = CP_refreshCalendar;
        c.getCalendar = CP_getCalendar;
        c.select = CP_select;
        c.fillDeliverBy = CP_fillDeliverBy;
        c.fillSendOn = CP_fillSendOn;
        c.setGraypixelImage = CP_setGraypixelImage;
        c.setLegendImage = CP_setLegendImage;
        c.setLegendLongDesc = CP_setLegendLongDesc;
        c.setDateRange = CP_setDateRange;
        c.getHeader = CP_getHeader;
        c.setHolidays = CP_setHolidays;
        c.setDateFormat = CP_setDateFormat;
        c.setParseDateFormat = CP_setParseDateFormat;
        c.renderMonth = CP_renderMonth;
        c.getMonthNavigation = CP_getMonthNavigation;
        c.getElementById= CP_getElementById;
        c.formatDateId = CP_formatDateId;
        // Return the object
        return c;
        }

//====================================================================
// Globals functions for hide/display events
//
//

// Temporary default functions to be called when items clicked, so no error is thrown
function CP_returnSendOnDate(y,m,d_in){
        if (window.CP_targetInput) {
                var d = new Date(y,m-1,d_in,0,0,0);
                window.CP_targetInput.value = formatDate(d,window.CP_dateFormat);
        }
}

// Temporary default functions to be called when items clicked, so no error is thrown
function CP_returnDeliverByDate(y,m,d_in){
        var d = new Date(y,m-1,d_in,0,0,0);
        var formattedDate = formatDate(d,window.CP_dateFormat);
        CP_setDeliverByContent(formattedDate);
}

function CP_setDeliverByContent(text){
        if (!document.layers && window.CP_deliverByDiv) {
                var divObj = document.getElementById(window.CP_deliverByDiv);
                if(divObj) {
                divObj.innerHTML = text;
                }
        }
        if (window.CP_deliverByHiddenField) {
                window.CP_deliverByHiddenField.value = text;
        }
}

function CP_hideListener(){
         CP_setFormVisibility(true);
}

function CP_setFormVisibility(show){
   if(document.layers)   {
      return;
   }
   var i,j;
   for(i=0; i<document.forms.length; i++)   {
      var form = document.forms[i];
      for(j=0; j<form.length; j++) {
         var obj = form[j];
         if(obj.type && obj.type == "select-one")  {
           if( show ) {
           obj.style.visibility = 'visible';
           }
           else {
           obj.style.visibility = 'hidden';
           }
         }
      }
   }
}

//====================================================================
// Setup stuff
//
//

// Set which holidays should not be clickable
function CP_setHolidays(){
        this.holidays = new Array(arguments.length);
        for (var i = 0; i < arguments.length; i++) {
          this.holidays[i] = arguments[i];
        }
}

//set pixel image
function CP_setGraypixelImage(imageURL) { this.graypixelImage = imageURL; }

//set legend image
function CP_setLegendImage(imageURL) { this.legendImage = imageURL; }

//set legend long desc
function CP_setLegendLongDesc(longDescURL) { this.legendLongDesc = longDescURL; }

// Over-ride the built-in month names
function CP_setMonthNames() {
        for (var i=0; i<arguments.length; i++) { this.monthNames[i] = arguments[i]; }
        }

// Over-ride the built-in column headers for each day
function CP_setDayHeaders() {
        for (var i=0; i<arguments.length; i++) { this.dayHeaders[i] = arguments[i]; }
        }

// Set the day of the week (0-7) that the calendar display starts on
// This is for countries other than the US whose calendar displays start on Monday(1), for example
function CP_setWeekStartDay(day) { this.weekStartDay = day; }

// Set which weekdays should not be clickable
function CP_setDisabledWeekDays() {
        this.disabledWeekDays = new Object();
        for (var i=0; i<arguments.length; i++) { this.disabledWeekDays[arguments[i]] = true; }
        }

// Simple method to interface popup calendar with a text-entry box
function CP_setDateRange(minDate, maxDate){
  this.asapDate = minDate;
  this.minDate = new SimpleDate(minDate);
  this.maxDate = new SimpleDate(maxDate);
}

// set form elements to hide (Array of dropdown/text names)
function CP_setFormElements(formElements){
     window.CP_formElements = formElements;
}

// set date format
function CP_setDateFormat(dateFormat){
        window.CP_dateFormat = dateFormat;
}

// set date format
function CP_setParseDateFormat(dateFormat){
        window.CP_parseDateFormat = dateFormat;
}

//====================================================================
// Calendar Display functions
//
//

// Hide a calendar object
function CP_hideCalendar() {
        if (arguments.length > 0) {
          window.popupWindowObjects[arguments[0]].hidePopup();
          window.CP_targetInput.focus();
        }
        else { alert('noarg'); }
        }

// Refresh the contents of the calendar display
function CP_refreshCalendar(index) {
        var calObject = window.popupWindowObjects[index];
        if (arguments.length>1) {
                calObject.populate(calObject.getCalendar(arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]));
                }
        else {
                calObject.populate(calObject.getCalendar());
                }
        calObject.refresh();
        if(document.forms.calendarForm) {
        document.forms.calendarForm.closeButton.focus();
        }
}

// Populate the calendar and display it
function CP_showCalendar(anchorname) {
        this.populate(this.getCalendar());
        this.showPopup(anchorname);
        if(document.forms.calendarForm) {
         document.forms.calendarForm.closeButton.focus();
        }
}

/*
   Method fillSendOn
   ARGUMENTS:
   amountField: DOM object of amount field
   dateField:  DOM object of text field to fill w/ date
   deliverByDiv: DIV/SPAN to fill deliverBy Date
   leadDays: Number of leadDays to display.  Pass -1 or 0 for no lead day display
   yearsAhead: Number of years ahead of sendOn date to display
*/

function CP_fillSendOn(amountField, dateField, deliverByHiddenField, deliverByDiv, leadDays, blockNonBusinessDays, yearsAhead) {
        if (amountField.value=="") {
            return;
        }

        var formattedDate = formatDate(this.asapDate,window.CP_dateFormat);
        //fill date field w/ formatted asapDate
        if(dateField.value=="") {
           dateField.value = formattedDate;
           this.fillDeliverBy(dateField, deliverByHiddenField, deliverByDiv, leadDays, blockNonBusinessDays, yearsAhead);
        }
}


/*
   Method fill DeliverByField
   ARGUMENTS:
   dateField:  DOM object of text field to fill w/ date
   deliverByDiv: DIV/SPAN to fill deliverBy Date
   leadDays: Number of leadDays to use for calculation.
   yearsAhead: Number of years ahead of sendOn date to display
*/
function CP_fillDeliverBy(dateField, deliverByHiddenField, deliverByDiv, leadDays, blockNonBusinessDays, yearsAhead) {
        window.CP_targetInput = dateField;
        window.CP_deliverByHiddenField = deliverByHiddenField;
        window.CP_deliverByDiv = deliverByDiv;
        window.CP_blockNonBusiness = blockNonBusinessDays;

        if(leadDays <0) {
            CP_setDeliverByContent("");
            return;
        }

        //if # of years to display ahead is overridden by select function, set maxDate
        if(yearsAhead && yearsAhead>0) {
          var today = new Date();
          this.maxDate= new SimpleDate(new Date(today.getTime() + yearsAhead*365*24*60*60*1000));
        }
        var sendon;
        if (dateField.value!="") {
             var time = getDateFromFormat(dateField.value,window.CP_parseDateFormat);
             if (time==0) {
                CP_setDeliverByContent("");
                return;
             }
             else {
               sendon=new Date(time);
             }
        }
        else {
            CP_setDeliverByContent("");
            return;
        }

        // NOTE: Careful of conversions between SimpleDate and Date
        //       SimpleDate has months from 1-12 where Date is 0-11!!!
        var year = sendon.getFullYear();
        var month = sendon.getMonth() + 1;
        var date= sendon.getDate();
        var day = sendon.getDay();

        // Check to see that user entered date is within range
        if( CM_isDateBefore(year, month, date, this.minDate.year, this.minDate.month, this.minDate.date)) {
            CP_setDeliverByContent("");
            return;
        }

        if( CM_isDateAfter(year, month, date, this.maxDate.year, this.maxDate.month, this.maxDate.date)) {
            CP_setDeliverByContent("");
            return;
        }

        // If blocking business day, check if weekend/holiday
        if( blockNonBusinessDays && CM_isHoliday(year, month, date, this.holidays)) {
            CP_setDeliverByContent("");
            return;
        }

        if( blockNonBusinessDays && this.disabledWeekDays[day]) {
            CP_setDeliverByContent("");
            return;
        }

        var firstMonth = new CalendarMonth(month, year, this.holidays, this.disabledWeekDays,this.minDate, this.maxDate );
        var secondMonth = firstMonth.createNextMonth();

        var monthArray = new Array();
        monthArray[0]=firstMonth;
        monthArray[1]=secondMonth;

        var leadDayCalculator = new LeadDayCalculator(monthArray);
        var deliverDate = leadDayCalculator.calculateDeliverBy(year, month, date,day, leadDays);

        CP_returnDeliverByDate(deliverDate.year, deliverDate.month, deliverDate.date);
}

/*
   Method to show calendar popup
   ARGUMENTS:
   dateField:  DOM object of text field to fill w/ date
   deliverByDiv: DIV/SPAN to fill deliverBy Date
   linkName: HREF anchor name where calendar should appear
   leadDays: Number of leadDays to display.
             -1:  don't highlight send on or deliverby, don't display legend
             -2:  highlight just send on date, display legend
   blockNonBusiness: Block users from picking holidays/weekends
   yearsAhead: Number of years ahead of sendOn date to display
*/
function CP_select(dateField, deliverByHiddenField, deliverByDiv, linkname, leadDays, blockNonBusiness, yearsAhead, hideDropDownListBox) {
        if (!window.getDateFromFormat) {
                alert("calendar.select: To use this method you must also include 'date.js' for date formatting");
                return;
        }
        if (dateField.type!="text" && dateField.type!="hidden" && dateField.type!="textarea") {
                alert("calendar.select: Input object passed is not a valid form input object");
                window.CP_targetInput=null;
                return;
        }
        if (dateField.disabled == true) {
            //alert("dateField has been disabled");
            return;
        }

        window.CP_targetInput = dateField;
        window.CP_deliverByHiddenField = deliverByHiddenField;
        window.CP_deliverByDiv = deliverByDiv;
        window.CP_blockNonBusiness = blockNonBusiness;
        //if # of years to display ahead is overridden by select function, set maxDate
        if(yearsAhead && yearsAhead>0) {
          var today = new Date();
          //this.maxDate= new SimpleDate(new Date(today.getTime() + yearsAhead*365*24*60*60*1000));
          this.maxDate= new SimpleDate(new Date(today.getTime() + 0)); //Set this to make the cal stop at the current day
        }

        if (dateField.value!="") {
                var time = getDateFromFormat(dateField.value,window.CP_parseDateFormat);
                if (time==0) { this.currentDate=null; }
                else { this.currentDate=new Date(time); }
                }
        else { this.currentDate=null; }

        if(leadDays!=0 && !leadDays) {
          this.leadDays = -1;
        }
        else {
          this.leadDays = leadDays;
        }
        if ((hideDropDownListBox != null) && (hideDropDownListBox == true))
            CP_setFormVisibility(false); // hides the drop down list boxes. They overlap over the calendar.
        this.showCalendar(linkname);
}

// Return a string containing all the calendar code to be displayed
function CP_getCalendar() {
        this.calculating=true;
        //set starting date to asapDate if not already set
        if (this.currentDate==null) {
          this.currentDate = new Date(); //asapDate; //Set this to "new Date()" to make the cal don't go all the way back to "asapDate" if the input value is null
        }

        // Check to see that user entered date is within range
        // NOTE: Careful of conversions between SimpleDate and Date
        //       SimpleDate has months from 1-12 where Date is 0-11!!!
        if( CM_isDateBefore(this.currentDate.getFullYear(), this.currentDate.getMonth()+1, this.currentDate.getDate(),
                                       this.minDate.year, this.minDate.month, this.minDate.date)) {
            this.currentDate = new Date(this.minDate.year, this.minDate.month-1,this.minDate.date);
        }

        if( CM_isDateAfter(this.currentDate.getFullYear(), this.currentDate.getMonth()+1, this.currentDate.getDate(),
                                       this.maxDate.year, this.maxDate.month, this.maxDate.date)) {
            this.currentDate = new Date(this.maxDate.year, this.maxDate.month-1,this.maxDate.date);
        }

        var result = "";
        var month;
        var year;
        if (arguments.length > 0) {
           this.month = arguments[0];
        }
        else {
           this.month = this.currentDate.getMonth()+1;
        }
        if (arguments.length > 1) {
           this.year = arguments[1];
        }
        else {
           this.year = this.currentDate.getFullYear();
        }

        // Reference to window
        if (this.type == "WINDOW") { this.windowref = "window.opener."; }
        else { this.windowref = ""; }

        result+= this.getHeader();

        /*   SETUP */
        this.nextMonth = this.month+1;
        this.nextMonthYear = this.year;
        if (this.nextMonth > 12) { this.nextMonth=1; this.nextMonthYear++; }

        this.lastMonth = this.month-1;
        this.lastMonthYear = this.year;
        if (this.lastMonth < 1) { this.lastMonth=12; this.lastMonthYear--; }

        if (this.type=="WINDOW") {
                result+= '<TABLE WIDTH="100%" BORDER="0" BORDERWIDTH="0" CELLSPACING="0" CELLPADDING="0"  bgcolor="#ffffff" summary="This table is used for layout.">';
        }
        else {
                result += '<TABLE WIDTH="144" BORDER="0" BORDERWIDTH="0" CELLSPACING="0" CELLPADDING="0" summary="This table is used for layout.">';
                }

        this.firstMonth = new CalendarMonth(this.month, this.year, this.holidays, this.disabledWeekDays,this.minDate, this.maxDate );
        this.secondMonth = this.firstMonth.createNextMonth();

        this.monthArray = new Array();
        this.monthArray[0]=this.firstMonth;
        this.monthArray[1]=this.secondMonth;
        this.monthArray[2]=this.secondMonth.createNextMonth();

        /*   RENDERING */
        result += '<TR><TD ALIGN="CENTER">';
        result += '<input class="form-button" type="submit" ID="closeButton" NAME="closeButton" VALUE="Close">';
        if(!document.layers) {
           result+='<BR />&nbsp;';
        }
        result +='</TD></TR>';
        result += '<TR><TD VALIGN="TOP">';
        result += '<TABLE WIDTH="90%" BORDER="0" CELLSPACING="0" CELLPADDING="0" ALIGN="CENTER" summary="This table represents a calendar month.">';
        result += this.getMonthNavigation(this.month, this.year, true, true);
        result += '<TR><TD COLSPAN="3" HALIGN="CENTER">';
        result += '<TABLE BORDER="0" CELLSPACING="1" CELLPADDING="0" ALIGN="CENTER" summary="This table is used for layout.">';
        result += this.renderMonth(this.firstMonth, true);
        result += '</TABLE></TD></TR>';
        result += '</TABLE>';
        result += '</TD></TR>';
        if(this.leadDays >= 0 && !document.layers) {
          result += '<TR><TD><CENTER><IMG SRC="'+this.legendImage+'" longdesc="'+this.legendLongDesc+'" alt="Visual cues for selecting a send on date."/><BR /><BR /></CENTER></TD></TR>';
        }
        if(document.layers) {
          result += '<TR><TD>&nbsp;</TD></TR>';
        }

        result += '<TR><TD>';
        result += '<TABLE WIDTH="90%" BORDER="0" CELLSPACING="0" CELLPADDING="0" ALIGN="CENTER" summary="This table represents a calendar month.">';
        result += this.getMonthNavigation(this.nextMonth, this.nextMonthYear, true, true);
        result += '<TR><TD COLSPAN="3" HALIGN="CENTER">';
        result += '<TABLE BORDER="0" CELLSPACING="1" CELLPADDING="0" ALIGN="CENTER" summary="This table is used for layout.">';
        result += this.renderMonth(this.secondMonth, false);
        result += '</TABLE></TD></TR>';
        result += '</TABLE>';
        result += '</TD></TR>';
        if(!document.layers) {
          result += '<TR><TD ALIGN="CENTER"><span id="deliverbyrep" class="deliverbytext" style="position:relative;visibility:hidden;">DeliverBy:</span></TD></TR>';
        }
        result += '</TABLE></CENTER>';


        // Common
        if (this.type == "WINDOW") {
                result += '</FORM>';
                result += "</BODY></HTML>";
        }
        else {
                result += "</TD></TR></TABLE>";
                result += '</FORM>';
        }
        this.calculating=false;
        this.gotClicked=true;

        return result;
}

function CP_getHeader() {
        var result ="";
        // If POPUP, write entire HTML document
        if (this.type == "WINDOW") {
                result += '<HTML><HEAD><TITLE>Calendar</TITLE>'+this.getStyles()+'</HEAD><BODY MARGINWIDTH="0" MARGINHEIGHT="0" TOPMARGIN="0" RIGHTMARGIN="0" LEFTMARGIN="0">';
                result += '<form name="calendarForm" method="post" action="javascript:'+this.windowref+'CP_hideCalendar(\''+this.index+'\');">';
                result += '<CENTER>';

        }
        else {
                result += '<form name="calendarForm" method="post" action="javascript:'+this.windowref+'CP_hideCalendar(\''+this.index+'\');">';
                result += '<TABLE WIDTH="144" BORDER="1" BORDERWIDTH="1" BORDERCOLOR="#808080" CELLSPACING="0" CELLPADDING="1"  bgcolor="#ffffff" summary="This table is used for layout.">';
                result += '<TR><TD ALIGN="CENTER">';
                result += '<CENTER>';
        }
        return result;
}

function CP_getMonthNavigation(month, year, renderPrevious, renderNext) {
        var result = "";
        result += '<TR BGCOLOR="#1A568A">';

        var maxDisplayMonth = this.maxDate.month-1;
        var maxDisplayYear = this.maxDate.year;
        if (maxDisplayMonth < 1) { this.maxDisplayMonth=12; this.maxDisplayYear--; }

        var refresh = 'javascript:'+this.windowref+'CP_refreshCalendar';

        if (!renderPrevious || this.lastMonthYear < this.minDate.year || (this.lastMonthYear == this.minDate.year && this.lastMonth < this.minDate.month)) {
          result += '<TD BGCOLOR="#1A568A" CLASS="cal-white" WIDTH="22" ALIGN="CENTER" VALIGN="MIDDLE">&nbsp;</TD>';
        } else {
          result += '<TD BGCOLOR="#1A568A" CLASS="cal-white" WIDTH="22" ALIGN="CENTER" VALIGN="MIDDLE"><B><A title="Previous Month" CLASS="cal-white" HREF="'+refresh+'('+this.index+','+this.lastMonth+','+this.lastMonthYear+');">&lt;&lt;</A></B></TD>';
        }
        result += '<TD BGCOLOR="#1A568A" CLASS="cal-white" WIDTH="100" ALIGN="CENTER">'+this.monthNames[month-1]+' '+year+'</TD>';
        if (!renderNext || this.nextMonthYear > maxDisplayYear || (this.nextMonthYear == maxDisplayYear && this.nextMonth > maxDisplayMonth)) {
          result += '<TD BGCOLOR="#1A568A" CLASS="cal-white" WIDTH="22" ALIGN="CENTER" VALIGN="MIDDLE">&nbsp;</TD>';
        } else {
          result += '<TD BGCOLOR="#1A568A" CLASS="cal-white" WIDTH="22" ALIGN="CENTER" VALIGN="MIDDLE"><B><A title="Next Month" CLASS="cal-white" HREF="'+refresh+'('+this.index+','+this.nextMonth+','+this.nextMonthYear+');">&gt;&gt;</A></B></TD>';
        }
        result += '</TR>';
        return result;
}

// Get style block needed to display the calendar correctly
function CP_getStyles() {
        var result = "";
        result +='<STYLE type="text/css">';

        result += "TD.cal { font-family:arial; font-size:8pt; }";
        result += "TD.cal-white { font-family:arial; font-size:8pt; color:#FFFFFF;}";
        result += "TD.cal-black { font-family:arial; font-size:8pt; color:#000000;}";
        result += "TD.calmonth { font-family:arial; font-size:8pt; text-align: right;}";
        result += "TD.caltoday { font-family:arial; font-size:8pt; text-align: right; color: white; background-color:#C0C0C0; border-width:1; border-type:solid; border-color:#800000; }";
        result += "A.textlink { font-family:arial; font-size:8pt; height: 20px; color: black; }";
        result += ".disabledtextlink { font-family:arial; font-size:8pt; height: 20px; color: #808080; }";
        result += "A.cal { text-decoration:none; color:#000000; }";
        result += "A.cal:visited {text-decoration:underline}";
        result += "A.cal-white { text-decoration:none; color:#FFFFFF; }";
        result += "A.cal-white:visited {text-decoration:none; color:#FFFFFF;}";
        result += "A.cal-black { text-decoration:none; color:#000000; }";
        result += "A.cal-black:visited {text-decoration:none; color:#000000;}";
        result += "A.calthismonth { text-decoration:none; color:#000000; }";
        result += "A.calothermonth { text-decoration:none; color:#808080; }";
        result += ".calnotclickable { color:#808080; }";
        result += ".sendon { background-color:#1A568A;color:#FFFFFF; padding:1px 2px 1px; }";
        result += ".deliverby { background-color:#FFC418;color:#000000; padding:1px 2px 1px; }";
        result += ".deliverbytext { font-family:arial; font-size:8pt; height: 20px; color: black; }";
        result += ".form-button {color:#003366;background-color:#FFFFFF;font-family:Arial, Helvetica, sans-serif;font-size: 0.7em;border:1px solid #FFFFFF;text-decoration:underline;}";
        result += ".adacal {color:#ffffff;font-size:1px;}";
        if(!document.layers) {
          result += "#sendtip TD {PADDING-RIGHT: 1px; PADDING-LEFT: 2px; FONT-SIZE: 11px; PADDING-BOTTOM: 0px;COLOR:#FFFFFF; PADDING-TOP: 0px; FONT-FAMILY: Arial, sans-serif;BACKGROUND-COLOR: #1A568A }";
          result += "#delivertip TD {PADDING-RIGHT: 1px; PADDING-LEFT: 2px; FONT-SIZE: 11px; PADDING-BOTTOM: 0px;COLOR:black; PADDING-TOP: 0px; FONT-FAMILY: Arial, sans-serif;BACKGROUND-COLOR: #FFC418 }";
          result += "#deliverbyrep {PADDING-RIGHT: 1px; PADDING-LEFT: 2px; FONT-SIZE: 11px; PADDING-BOTTOM: 0px;COLOR:black; PADDING-TOP: 0px; FONT-FAMILY: Arial, sans-serif;BACKGROUND-COLOR: #FFC418 }";
        }
        result += "</STYLE>";
        return result;
}

function CP_getHiddenLayer() {
        var result="";

        if(!document.layers) {
          result +='<span id="sendtip" style="z-index:10; position:absolute; visibility:hidden;">';
          result +='<table summary="This table is used for layout."><tbody><tr><td nowrap="nowrap">Send On</td></tr></tbody></table></span>';

          result +='<span id="delivertip" style="z-index:10; position:absolute; visibility:hidden;">';
          result +='<table summary="This table is used for layout."><tbody><tr><td nowrap="nowrap">Deliver By</td></tr></tbody></table></span>';
        }

        return result;
}

/* Render Month - pass in month Model object */
function CP_renderMonth(month, isFirst) {
     var result = "";
     result += '<TR>';
     var td = '<TD CLASS="cal-black" ALIGN="RIGHT" WIDTH="14%">';
     for (var j0=0; j0<7; j0++) {
             result += td+this.dayHeaders[(this.weekStartDay+j0)%7]+'</TD>';
     }
     if(!document.layers){
      result += '<TD><span class="adacal">The following are dates in '+this.monthNames[month.month-1]+'</span></TD>';
     }
     result += '</TR>';
     result += '<TR BGCOLOR="#C0C0C0"><TD COLSPAN="7" ALIGN="CENTER"><IMG SRC="'+ this.graypixelImage + '" WIDTH="120" HEIGHT="1" alt="" /></TD></TR>';

     var leadDayCalculator = null;
     if(this.leadDays > -1)  {
        leadDayCalculator = new LeadDayCalculator(this.monthArray);
     }

     for(var i=0; i< month.weeks.length; i++){
         result+= "<TR>";
         for(var j=0; j< 7; j++){
            var weekDay;
            if( month.weeks[i]){
               weekDay = month.weeks[i].days[j];
            } else {
               weekDay = null;
            }

            var dateClass, tdClass, sendOnClass, deliverByClass;
            if(weekDay){
               if(!weekDay.isEnabled){
                  dateClass="calnotclickable";
                  tdClass="calmonth";
                  result += '<TD CLASS="'+tdClass+'"><SPAN CLASS="'+dateClass+'">'+weekDay.date+'</SPAN></TD>';
               }
               else{
                  dateClass = "calthismonth";
                  sendOnClass = "sendon";
                  tdClass="calmonth";
                  deliverByClass = "deliverby";

                  var dateId= this.formatDateId(month.year, month.month,weekDay.date);
                  var dateAnchorId= "a"+dateId;
                  var deliverDate;

                  if(this.leadDays > -1) {
                     deliverDate = leadDayCalculator.calculateDeliverBy(month.year, month.month, weekDay.date,j, this.leadDays);
                  }
                  else {
                     deliverDate = null;
                  }

                  var deliverById;
                  if(!deliverDate) {
                      deliverById="";
                  }
                  else {
                      deliverById= this.formatDateId(deliverDate.year,deliverDate.month,deliverDate.date);
                  }
                  deliverByAnchorId = "a"+ deliverById;
                  var spanStyle="";

                  result += '<TD CLASS="'+tdClass+'">';

                  result += '<A class="cal-black" HREF="javascript:'+this.windowref+'CP_returnSendOnDate('+month.year+','+month.month+','+weekDay.date+');';
                  if(this.leadDays > -1 && deliverDate) {
                    result +=this.windowref+'CP_returnDeliverByDate('+deliverDate.year+','+deliverDate.month+','+deliverDate.date+');';
                  }
                  result +=this.windowref+'CP_hideCalendar(\''+this.index+'\');"';
                  result +=' name="'+dateAnchorId+'" id="'+dateAnchorId+'"';

                  //everything else besides netscape 4
                  if(!document.layers && this.leadDays != -1) {
                    result+= ' onMouseOver="javascript:';
                    result+= this.windowref+'CP_changeClass(\''+dateId+'\',\''+sendOnClass + '\');';
                    result+= this.windowref+'CP_showTooltip(\'sendtip\',\''+dateAnchorId + '\',-15,-15);';

                    //highlighting only send on, don't do deliverby
                    if( this.leadDays != -2) {
                       result+=   this.windowref+'CP_changeClass(\''+deliverById+'\',\''+deliverByClass + '\');';
                       result+=   this.windowref+'CP_showTooltip(\'delivertip\',\''+deliverByAnchorId + '\',-18,13);';
                    }
                    result += '"';

                    result+= ' onMouseOut="javascript:';
                    result+= this.windowref+'CP_changeClass(\''+dateId+'\',\''+dateClass + '\');';
                    result+= this.windowref+'CP_hideTooltip(\'sendtip\');';

                    //highlighting only send on, don't do deliverby
                    if( this.leadDays != -2) {
                       result+= this.windowref+'CP_changeClass(\''+deliverById+'\',\''+dateClass + '\');';
                       result+= this.windowref+'CP_hideTooltip(\'delivertip\');';
                    }
                    result+='"';
                  }

                  result += ' CLASS="'+dateClass+'">';

                  if(!document.layers) {
                    result += '<span id="'+ dateId+'" style="position:relative;">';
                  }

                  result += weekDay.date;
                  if(!document.layers) {
                    result += '</span>';
                  }
                  result += '</A></TD>';
               }
            }
            else{
               dateClass="calnotclickable";
               tdClass="calmonth";
               result += '<TD CLASS="'+tdClass+'"><SPAN CLASS="'+dateClass+'">&nbsp;</SPAN></TD>';
           }
         }
         result+="</TR>";
     }
     if( month.weeks.length<6){
       result+= '<TR><TD CLASS="'+tdClass+'"><SPAN CLASS="'+dateClass+'">&nbsp;</SPAN></TD></TR>';
     }
     return result;
}

function CP_formatDateId(year, month, date) {
        if(month < 10){
           month = "0"+month;
        }

        if(date < 10){
           date = "0"+ date;
        }
        return "d"+year.toString() + month.toString()+ date.toString();
}

//================================================================
// Calendar/LeadDayCalculator "Objects"
//

/*
   Class representing a Month
   month: month to display (1-12 representing month)
   year: year to display
   sendOnDate: Anything before sendOn date is disabled
   Calculates active/inactive days depending on holidays, disabledWeekdays, and date range
 */
function CalendarMonth(month, year, holidays, disabledWeekDays, minDate, maxDate){
        this.weekStartDay = 0;
        this.month= month;
        this.year= year;

        this.minDate = minDate;
        this.maxDate = maxDate;
        this.holidays= holidays;
        this.disabledWeekDays=disabledWeekDays;

        //methods
        this.isHoliday = CM_isHoliday;
        this.withinDateRange = CM_withinDateRange;
        this.isDateBefore = CM_isDateBefore;
        this.isDateAfter = CM_isDateAfter;
        this.createNextMonth = CM_createNextMonth;

        this.daysInMonthList = new Array(0,31,28,31,30,31,30,31,31,30,31,30,31);
        if ( ( (year%4 == 0)&&(year%100 != 0) ) || (year%400 == 0) ) {
                this.daysInMonthList[2] = 29;
        }

        this.daysInMonth = this.daysInMonthList[month];    //number of days in month
        this.currentMonthDate = new Date(year,month-1,1);
        this.startDay= this.currentMonthDate.getDay();     //first day of the month

        var dateIndex = 1;
        var weekIndex = 0;

        //populate weeks and days
        this.weeks = new Array();
        while(dateIndex <= this.daysInMonth){
           var currentWeek = new CalendarWeek(weekIndex);
           this.weeks[ weekIndex ] = currentWeek;
           for(var dayIndex=0; dayIndex<7 && dateIndex <= this.daysInMonth; dayIndex++){
             if( weekIndex > 0 || dayIndex >= this.startDay ){
                 var holiday = this.isHoliday(this.year, this.month, dateIndex, this.holidays);
                 var disabledWeekDay = this.disabledWeekDays[dayIndex];
                 var withinRange = this.withinDateRange(this.year, this.month, dateIndex);
                 var isBusinessDay = true;
                 var isEnabled = true;
                 //separate out business days and enabled days
                 //(DeliverBy has to be business day but not necessarily in range)
                 if( window.CP_blockNonBusiness && (holiday || disabledWeekDay)) {
                   isBusinessDay = false;
                 }
                 if( !isBusinessDay || !withinRange){
                    isEnabled = false;
                 }
                 currentWeek.days[dayIndex] = new CalendarDay(dateIndex, isEnabled, isBusinessDay);
                 dateIndex++;
             }
           }
           weekIndex++;
        }
}

//return whether day is a holiday
function CM_isHoliday(year, month, day, holidays){
        if(!holidays) {
          return false;
        }
        //use String comparison instead of Date for speed
        if(month < 10){
           month = "0"+month;
        }

        if(day < 10){
           day = "0"+ day;
        }
        var displayDate = year.toString() + month.toString()+ day.toString();

        for (var i = 0; i < holidays.length; i++) {
                if (holidays[i] > displayDate) {
                        return false;
                }
                if (holidays[i] == displayDate) {
                        return true;
                }
        }
        return false;
}

//return whether day is within min max range
function CM_withinDateRange(year, month, day) {
        if( !this.isDateBefore( year, month, day, this.minDate.year, this.minDate.month, this.minDate.date) && !this.isDateAfter( year, month, day, this.maxDate.year, this.maxDate.month, this.maxDate.date)) {
            return true;
        }
        else  {
            return false;
        }
}

//return true if first date is before than second
function CM_isDateBefore(year, month, day,minYear, minMonth, minDay) {
        if (year < minYear || (year == minYear && month < minMonth) || (year == minYear && month == minMonth && day < minDay)) {
                return true;
        }
        else {
                return false;
        }
}

//return true if first date is after than first
function CM_isDateAfter(year, month, day,maxYear, maxMonth, maxDay) {
        if (year > maxYear || (year == maxYear && month > maxMonth) || (year == maxYear && month == maxMonth && day > maxDay)) {
                return true;
        }
        else  {
                return false;
        }
}


// return populated month for next month
function CM_createNextMonth() {
    var nextMonth = this.month+1;
    var nextMonthYear = this.year;
    if (nextMonth > 12) { nextMonth=1; nextMonthYear++; }
    return new CalendarMonth(nextMonth, nextMonthYear,
                             this.holidays, this.disabledWeekDays,this.minDate, this.maxDate );
}

/*
     LeadDayCalculator
       used to calculate deliver by date for a certain send on date
       MonthArray - array of month objects to use for calculation
 */
function LeadDayCalculator(monthArray) {
    //methods
    this.nextDay=LDC_nextDay;
    this.nextActiveDay=LDC_nextActiveDay;
    this.calculateDeliverBy=LDC_calculateDeliverBy;

    //not sure how to initialize this both internal/external
    this.months = monthArray;
}

function LDC_calculateDeliverBy(year, month, date, day, leadDays) {
    var foundMonth = false;

    for( var i =0; i<this.months.length; i++) {
        this.currentMonth = this.months[i];
        if( this.currentMonth.month ==  month && this.currentMonth.year == year ) {
            foundMonth=true;
            this.monthIndex = i;
            break;
        }
    }
    if( !foundMonth) {
      return null;
    }

    this.dateIndex = date;
    this.dayIndex  = day;
    this.weekIndex = Math.floor( (this.currentMonth.startDay + this.dateIndex - 1)/7 );
    this.year = year;
    for(var i1=0; i1<leadDays; i1++) {
        var withinRange =this.nextActiveDay();
        if(!withinRange) {
          return null;
        }
    }
    return new SimpleDateFull(this.year,this.currentMonth.month, this.dateIndex);
}

function LDC_nextActiveDay() {
     var dayActive = false;
     var withinMonth = true;
     while( withinMonth ) {
          withinMonth = this.nextDay();
          if(!withinMonth){
            return false;
          }
          if(!this.currentMonth.weeks) {
            return false;
          }
          var day = this.currentMonth.weeks[this.weekIndex].days[this.dayIndex];

          if(!day) {
             return false;
          }

          if(day.isBusinessDay) {
            return true;
          }
     }
}

/* Increments one day
   Returns false if nextDay out of month range
 */
function LDC_nextDay(){
     this.dateIndex++;

     if(this.dateIndex <= this.currentMonth.daysInMonth) {
         this.dayIndex++;
         if(this.dayIndex > 6) {   this.dayIndex=0;
             this.weekIndex++;
         }
     }
     else  {
         this.monthIndex++;
         if( this.monthIndex >= this.months.length) {
             return false;
         }
         this.currentMonth= this.months[this.monthIndex];
         this.dateIndex = 1;
         this.dayIndex = this.currentMonth.startDay;
         this.weekIndex=0;
         this.year = this.currentMonth.year;
     }
     return true;
}

function CalendarWeek(weekNumber) {
     this.weekNumber = weekNumber;
     this.days = new Array(7);
}

/* Day Object
   Date: Date
   isEnabled: If user can select date as SendOn
   isBusinessDay: If date is businessDay (can still be out of valid date range)
 */

function CalendarDay(date, isEnabled, isBusinessDay) {
     this.date = date;
     this.isEnabled= isEnabled;
     this.isBusinessDay= isBusinessDay;
}

/* Date Object
   NOTE: month in range 1-12
 */
function SimpleDate(dateObj) {
     this.year = dateObj.getFullYear();
     //Need to add 1 b/c Date object represents months from 0-11
     this.month = dateObj.getMonth()+1;
     this.date = dateObj.getDate();
}

/*
   Date Object
   NOTE: month in range 1-12
 */
function SimpleDateFull(year, month, date) {
     this.year = year;
     this.month = month;
     this.date = date;
}

//================================================================
// Dynamic HTML functions (tooltips/highlighting)
//

/*
  Returns Javascript code to get Element by Id
  Supports Netscape Layers, IE API, DOM compliant API
  Can have 1 or more argument
  For 1 argument it is the ID of the element to get
  For Netscape4 can have more than 1:elementId parentId parentId ...
*/
function CP_getElementById(elementId){
       var result = "";
       if(document.getElementById) {
         result = "document.getElementById('"+elementId+"')";
       }
       else if(document.all) {
         result ="document.all['"+elementId+"']";
       }
       else if(document.layers) {
           result ="";

           result ="document.layers['"+elementId+"']";
           for(var i=1; i<arguments.length; i++) {
             var arg = arguments[i];
             if( arg && arg != null ) {
               result = "document.layers['"+arguments[i]+"']."+result;
             }
           }
       }
       return result;
}

function CP_changeClass(layerId, className) {
     var layerObj = eval(CP_getElementById( layerId));
     if(!layerObj) {
       return;
     }
     layerObj.className = className;
}

function CP_showTooltip(tooltipId, dateId, xOffset,yOffset) {
     var layerObj = eval(CP_getElementById( tooltipId));
     var positionObj  = eval( CP_getElementById( dateId));
     if(!layerObj || !positionObj) {
         if(tooltipId == 'delivertip') {
             CP_showDeliverByText( dateId);
         }
         return;
     }

     layerObj.style.visibility = 'visible';
     var coordinates = getAnchorPosition(dateId);

     //standards compliant browsers need "px" after coordinates
     if(document.getElementById) {
       layerObj.style.left = coordinates.x + xOffset +"px";
       layerObj.style.top = coordinates.y + yOffset  +"px";
     }
     else {
       layerObj.style.left = coordinates.x + xOffset;
       layerObj.style.top = coordinates.y + yOffset;
     }

}

function CP_hideTooltip(tooltipId) {
     CP_hideDeliverByText();
     var layerObj = eval(CP_getElementById( tooltipId));

     if(!layerObj) {
       return;
     }
     layerObj.style.visibility = 'hidden';
}

function CP_showDeliverByText(dateId) {
     var layerObj = eval(CP_getElementById( 'deliverbyrep'));
     if(!layerObj) {
      return;
     }
     layerObj.innerHTML = CP_formatDeliverById(dateId);
     layerObj.style.visibility = 'visible';
}

function CP_formatDeliverById(dateId) {
      if(dateId.length != 10 ) {
        return;
      }
      var year = dateId.slice(2,6);
      var month =dateId.slice(6,8);
      var day =dateId.slice(8,10);
      var d = new Date(year,month-1,day,0,0,0);
      return "Deliver By:"+formatDate(d,window.CP_dateFormat);
}

function CP_hideDeliverByText() {
     var layerObj = eval(CP_getElementById( 'deliverbyrep'));
     if(!layerObj) {
       return;
     }
     layerObj.style.visibility = 'hidden';
}

/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/

// ===================================================================
// 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. Instead,
// please just point to my URL to ensure the most up-to-date versions
// of the files. Thanks.
// ===================================================================


// ------------------------------------------------------------------
// These functions use the same 'format' strings as the
// java.text.SimpleDateFormat class, with minor exceptions.
// The format string consists of the following abbreviations:
//
// Field        | Full Form          | Short Form
// -------------+--------------------+-----------------------
// Year         | yyyy (4 digits)    | yy (2 digits), y (2 or 4 digits)
// Month        | MMM (name or abbr.)| MM (2 digits), M (1 or 2 digits)
// Day of Month | dd (2 digits)      | d (1 or 2 digits)
// Hour (1-12)  | hh (2 digits)      | h (1 or 2 digits)
// Hour (0-23)  | HH (2 digits)      | H (1 or 2 digits)
// Hour (0-11)  | KK (2 digits)      | K (1 or 2 digits)
// Hour (1-24)  | kk (2 digits)      | k (1 or 2 digits)
// Minute       | mm (2 digits)      | m (1 or 2 digits)
// Second       | ss (2 digits)      | s (1 or 2 digits)
// AM/PM        | a                  |
//
// NOTE THE DIFFERENCE BETWEEN MM and mm! Month=MM, not mm!
// Examples:
//  "MMM d, y" matches: January 01, 2000
//                      Dec 1, 1900
//                      Nov 20, 00
//  "M/d/yy"   matches: 01/20/00
//                      9/2/00
//  "MMM dd, yyyy hh:mm:ssa" matches: "January 01, 2000 12:30:45AM"
// ------------------------------------------------------------------

var MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
function LZ(x) {return(x<0||x>9?"":"0")+x;}

// ------------------------------------------------------------------
// isDate ( date_string, format_string )
// Returns true if date string matches format of format string and
// is a valid date. Else returns false.
// It is recommended that you trim whitespace around the value before
// passing it to this function, as whitespace is NOT ignored!
// ------------------------------------------------------------------
function isDate(val,format) {
  var date=getDateFromFormat(val,format);
  if (date==0) { return false; }
  return true;
  }

// -------------------------------------------------------------------
// compareDates(date1,date1format,date2,date2format)
//   Compare two date strings to see which is greater.
//   Returns:
//   1 if date1 is greater than date2
//   0 if date2 is greater than date1 of if they are the same
//  -1 if either of the dates is in an invalid format
// -------------------------------------------------------------------
function compareDates(date1,dateformat1,date2,dateformat2) {
  var d1=getDateFromFormat(date1,dateformat1);
  var d2=getDateFromFormat(date2,dateformat2);
  if (d1==0 || d2==0) {
    return -1;
    }
  else if (d1 > d2) {
    return 1;
    }
  return 0;
  }

// ------------------------------------------------------------------
// formatDate (date_object, format)
// Returns a date in the output format specified.
// The format string uses the same abbreviations as in getDateFromFormat()
// ------------------------------------------------------------------
function formatDate(date,format) {
  format=format+"";
  var result="";
  var i_format=0;
  var c="";
  var token="";
  var y=date.getYear()+"";
  var M=date.getMonth()+1;
  var d=date.getDate();
  var H=date.getHours();
  var m=date.getMinutes();
  var s=date.getSeconds();
  var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,KK,K,kk,k;
  // Convert real date parts into formatted versions
  var value=new Object();
  if (y.length < 4) {y=""+(y-0+1900);}
  value["y"]=""+y;
  value["yyyy"]=y;
  value["yy"]=y.substring(2,4);
  value["M"]=M;
  value["MM"]=LZ(M);
  value["MMM"]=MONTH_NAMES[M-1];
  value["d"]=d;
  value["dd"]=LZ(d);
  value["H"]=H;
  value["HH"]=LZ(H);
  if (H==0){value["h"]=12;}
  else if (H>12){value["h"]=H-12;}
  else {value["h"]=H;}
  value["hh"]=LZ(value["h"]);
  if (H>11){value["K"]=H-12;} else {value["K"]=H;}
  value["k"]=H+1;
  value["KK"]=LZ(value["K"]);
  value["kk"]=LZ(value["k"]);
  if (H > 11) { value["a"]="PM"; }
  else { value["a"]="AM"; }
  value["m"]=m;
  value["mm"]=LZ(m);
  value["s"]=s;
  value["ss"]=LZ(s);
  while (i_format < format.length) {
    c=format.charAt(i_format);
    token="";
    while ((format.charAt(i_format)==c) && (i_format < format.length)) {
      token += format.charAt(i_format++);
      }
    if (value[token] != null) { result=result + value[token]; }
    else { result=result + token; }
    }
  return result;
  }

// ------------------------------------------------------------------
// Utility functions for parsing in getDateFromFormat()
// ------------------------------------------------------------------
function _isInteger(val) {
  var digits="1234567890";
  for (var i=0; i < val.length; i++) {
    if (digits.indexOf(val.charAt(i))==-1) { return false; }
    }
  return true;
  }
function _getInt(str,i,minlength,maxlength) {
  for (var x=maxlength; x>=minlength; x--) {
    var token=str.substring(i,i+x);
    if (token.length < minlength) { return null; }
    if (_isInteger(token)) { return token; }
    }
  return null;
  }

// ------------------------------------------------------------------
// getDateFromFormat( date_string , format_string )
//
// This function takes a date string and a format string. It matches
// If the date string matches the format string, it returns the
// getTime() of the date. If it does not match, it returns 0.
// ------------------------------------------------------------------
function getDateFromFormat(val,format) {
  val=val+"";
  format=format+"";
  var i_val=0;
  var i_format=0;
  var c="";
  var token="";
  var token2="";
  var x,y;
  var now=new Date();
  var year=now.getYear();
  var month=now.getMonth()+1;
  var date=now.getDate();
  var hh=now.getHours();
  var mm=now.getMinutes();
  var ss=now.getSeconds();
  var ampm="";

  while (i_format < format.length) {
    // Get next token from format string
    c=format.charAt(i_format);
    token="";
    while ((format.charAt(i_format)==c) && (i_format < format.length)) {
      token += format.charAt(i_format++);
      }
    // Extract contents of value based on format token
    if (token=="yyyy" || token=="yy" || token=="y") {
      if (token=="yyyy") { x=4;y=4; }
      if (token=="yy")   { x=2;y=2; }
      if (token=="y")    { x=2;y=4; }
      year=_getInt(val,i_val,x,y);
      if (year==null) { return 0; }
      i_val += year.length;
      if (year.length==2) {
        if (year > 70) { year=1900+(year-0); }
        else { year=2000+(year-0); }
        }
      }
    else if (token=="MMM"){
      month=0;
      for (var i=0; i<MONTH_NAMES.length; i++) {
        var month_name=MONTH_NAMES[i];
        if (val.substring(i_val,i_val+month_name.length).toLowerCase()==month_name.toLowerCase()) {
          month=i+1;
          if (month>12) { month -= 12; }
          i_val += month_name.length;
          break;
          }
        }
      if ((month < 1)||(month>12)){return 0;}
      }
    else if (token=="MM"||token=="M") {
      month=_getInt(val,i_val,token.length,2);
      if(month==null||(month<1)||(month>12)){return 0;}
      i_val+=month.length;}
    else if (token=="dd"||token=="d") {
      date=_getInt(val,i_val,token.length,2);
      if(date==null||(date<1)||(date>31)){return 0;}
      i_val+=date.length;}
    else if (token=="hh"||token=="h") {
      hh=_getInt(val,i_val,token.length,2);
      if(hh==null||(hh<1)||(hh>12)){return 0;}
      i_val+=hh.length;}
    else if (token=="HH"||token=="H") {
      hh=_getInt(val,i_val,token.length,2);
      if(hh==null||(hh<0)||(hh>23)){return 0;}
      i_val+=hh.length;}
    else if (token=="KK"||token=="K") {
      hh=_getInt(val,i_val,token.length,2);
      if(hh==null||(hh<0)||(hh>11)){return 0;}
      i_val+=hh.length;}
    else if (token=="kk"||token=="k") {
      hh=_getInt(val,i_val,token.length,2);
      if(hh==null||(hh<1)||(hh>24)){return 0;}
      i_val+=hh.length;hh--;}
    else if (token=="mm"||token=="m") {
      mm=_getInt(val,i_val,token.length,2);
      if(mm==null||(mm<0)||(mm>59)){return 0;}
      i_val+=mm.length;}
    else if (token=="ss"||token=="s") {
      ss=_getInt(val,i_val,token.length,2);
      if(ss==null||(ss<0)||(ss>59)){return 0;}
      i_val+=ss.length;}
    else if (token=="a") {
      if (val.substring(i_val,i_val+2).toLowerCase()=="am") {ampm="AM";}
      else if (val.substring(i_val,i_val+2).toLowerCase()=="pm") {ampm="PM";}
      else {return 0;}
      i_val+=2;}
    else {
      if (val.substring(i_val,i_val+token.length)!=token) {return 0;}
      else {i_val+=token.length;}
      }
    }
  // If there are any trailing characters left in the value, it doesn't match
  if (i_val != val.length) { return 0; }
  // Is date valid for month?
  if (month==2) {
    // Check for leap year
    if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year
      if (date > 29){ return false; }
      }
    else { if (date > 28) { return false; } }
    }
  if ((month==4)||(month==6)||(month==9)||(month==11)) {
    if (date > 30) { return false; }
    }
  // Correct hours value
  if (hh<12 && ampm=="PM") { hh+=12; }
  else if (hh>11 && ampm=="AM") { hh-=12; }
  var newdate=new Date(year,month-1,date,hh,mm,ss);
  return newdate.getTime();
  }

/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/

// ===================================================================
// 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.
// ===================================================================

/*
PopupWindow.js
Author: Matt Kruse
Last modified: 3/21/02

DESCRIPTION: This object allows you to easily and quickly popup a window
in a certain place. The window can either be a DIV or a separate browser
window.

COMPATABILITY: Works with Netscape 4.x, 6.x, IE 5.x on Windows. Some small
positioning errors - usually with Window positioning - occur on the
Macintosh platform. Due to bugs in Netscape 4.x, populating the popup
window with <STYLE> tags may cause errors.

USAGE:
// Create an object for a WINDOW popup
var win = new PopupWindow();

// Create an object for a DIV window using the DIV named 'mydiv'
var win = new PopupWindow('mydiv');

// Set the window to automatically hide itself when the user clicks
// anywhere else on the page except the popup
win.autoHide();

// Show the window relative to the anchor name passed in
win.showPopup(anchorname);

// Hide the popup
win.hidePopup();

// Set the size of the popup window (only applies to WINDOW popups
win.setSize(width,height);

// Populate the contents of the popup window that will be shown. If you
// change the contents while it is displayed, you will need to refresh()
win.populate(string);

// Refresh the contents of the popup
win.refresh();

// Specify how many pixels to the right of the anchor the popup will appear
win.offsetX = 50;

// Specify how many pixels below the anchor the popup will appear
win.offsetY = 100;

NOTES:
1) Requires the functions in AnchorPosition.js

2) Your anchor tag MUST contain both NAME and ID attributes which are the
   same. For example:
   <A NAME="test" ID="test"> </A>

3) There must be at least a space between <A> </A> for IE5.5 to see the
   anchor tag correctly. Do not do <A></A> with no space.

4) When a PopupWindow object is created, a handler for 'onmouseup' is
   attached to any event handler you may have already defined. Do NOT define
   an event handler for 'onmouseup' after you define a PopupWindow object or
   the autoHide() will not work correctly.
*/

// Set the position of the popup window based on the anchor
function PW_getXYPosition(anchorname) {
        var coordinates;
        if (this.type == "WINDOW") {
                coordinates = getAnchorWindowPosition(anchorname);
                }
        else {
                coordinates = getAnchorPosition(anchorname);
                }
        this.x = coordinates.x;
        this.y = coordinates.y;
        }
// Set width/height of DIV/popup window
function PW_setSize(width,height) {
        this.width = width;
        this.height = height;
        }
// Fill the window with contents
function PW_populate(contents) {
        this.contents = contents;
        this.populated = false;
        }
// Refresh the displayed contents of the popup
function PW_refresh() {
        if (this.divName != null) {
                // refresh the DIV object
                if (this.use_gebi) {
                        document.getElementById(this.divName).innerHTML = this.contents;
                        }
                else if (this.use_css) {
                        document.all[this.divName].innerHTML = this.contents;
                        }
                else if (this.use_layers) {
                        var d = document.layers[this.divName];
                        d.document.open();
                        d.document.writeln(this.contents);
                        d.document.close();
                        }
                }
        else {
                if (this.popupWindow != null && !this.popupWindow.closed) {
                        this.popupWindow.document.open();
                        this.popupWindow.document.writeln(this.contents);
                        this.popupWindow.document.close();
                        this.popupWindow.focus();
                        }
                }
        }
// Position and show the popup, relative to an anchor object
function PW_showPopup(anchorname) {
        this.getXYPosition(anchorname);
        this.x += this.offsetX;
        this.y += this.offsetY;
        if (!this.populated && (this.contents != "")) {
                this.populated = true;
                this.refresh();
                }
        if (this.divName != null) {
    //added: reposition DIV object to fit within screen
    this.repositionDiv();
                // Show the DIV object
                if (this.use_gebi) {
                        document.getElementById(this.divName).style.left = this.x+"px";
                        document.getElementById(this.divName).style.top = this.y +"px";
                        document.getElementById(this.divName).style.visibility = "visible";
                        }
                else if (this.use_css) {
                        document.all[this.divName].style.left = this.x;
                        document.all[this.divName].style.top = this.y;
                        document.all[this.divName].style.visibility = "visible";
                        }
                else if (this.use_layers) {
                        document.layers[this.divName].left = this.x;
                        document.layers[this.divName].top = this.y;
                        document.layers[this.divName].visibility = "visible";
                        }
                }
        else {
                if (this.popupWindow == null || this.popupWindow.closed) {
                        // If the popup window will go off-screen, move it so it doesn't
                        if (screen && screen.availHeight) {
                                if ((this.y + this.height) > screen.availHeight) {
                                        this.y = screen.availHeight - this.height;
                                        }
                                }
                        if (screen && screen.availWidth) {
                                if ((this.x + this.width) > screen.availWidth) {
                                        this.x = screen.availWidth - this.width;
                                        }
                                }
                        this.popupWindow = window.open("","window_"+anchorname,"toolbar=no,location=no,status=no,menubar=no,scrollbars=auto,resizable,alwaysRaised,dependent,titlebar=no,width="+this.width+",height="+this.height+",screenX="+this.x+",left="+this.x+",screenY="+this.y+",top="+this.y+"");
                        }
                this.refresh();
                }
    if(this.timeout && this.timeout>0){
    this.timeOutId=setInterval("PW_checkTimeout("+this.index+")", this.timeout);
    }
    this.visible=true;
        }

//added: reposition div to fit within viewable area
function PW_repositionDiv(){
    var calendar = null;
    var resultY = this.y;
    var calendarHeight;
    var screenHeight;

    if(document.layers){
       calendar = document.layers[this.divName];
       screenHeight = window.pageYOffset + window.innerHeight;
       calendarHeight = calendar.clip.height;
    }
    else if( document.all){
       calendar = document.all[this.divName];
       screenHeight = document.body.clientHeight + document.body.scrollTop;
       calendarHeight = calendar.clientHeight;
    }
    else if(document.getElementById) {
        calendar = document.getElementById(this.divName);
        screenHeight = document.body.clientHeight + document.body.scrollTop;
        calendarHeight = calendar.offsetHeight;
    }

    var bottomY = calendarHeight + resultY;
    if( bottomY > screenHeight ) {
          resultY = screenHeight - calendarHeight;
    }
    if(resultY < 0) {
       resultY = 0;
    }
    this.y = resultY;
}

// Hide the popup
function PW_hidePopup() {
  if(this.timeOutId) {
  clearInterval(this.timeOutId);
  }
        if (this.divName != null) {
                if (this.use_gebi) {
                        document.getElementById(this.divName).style.visibility = "hidden";
                        }
                else if (this.use_css) {
                        document.all[this.divName].style.visibility = "hidden";
                        }
                else if (this.use_layers) {
                        document.layers[this.divName].visibility = "hidden";
                        }
                }
        else {
                if (this.popupWindow && !this.popupWindow.closed) {
                        this.popupWindow.close();
                        this.popupWindow = null;
                        }
                }

                //call hide listener
    if(this.hideListener) {  this.hideListener(); }
    this.visible=false;
        }

//added: timeout functionality
function PW_checkTimeout(index){
    popup = popupWindowObjects[index];
    if( popup && !popup.gotClicked && !popup.calculating) {
       popup.hidePopup();
    }
    popup.gotClicked = false;
}

// Pass an event and return whether or not it was the popup DIV that was clicked
function PW_isClicked(e) {
        if (this.divName != null) {
                var t;
                if (this.use_layers) {
                        var clickX = e.pageX;
                        var clickY = e.pageY;
                        t = document.layers[this.divName];
                        if ((clickX > t.left) && (clickX < t.left+t.clip.width) && (clickY > t.top) && (clickY < t.top+t.clip.height)) {
                                return true;
                                }
                        else { return false; }
                        }
                else if (document.all) { // Need to hard-code this to trap IE for error-handling
                        t = window.event.srcElement;
                        while (t.parentElement != null) {
                                if (t.id==this.divName) {
                                        return true;
                                        }
                                t = t.parentElement;
                                }
                        return false;
                        }
                else if (this.use_gebi) {
                        t = e.originalTarget;
                        while (t.parentNode != null) {
                                if (t.id==this.divName) {
                                        return true;
                                        }
                                t = t.parentNode;
                                }
                        return false;
                        }
                return false;
                }
        return false;
        }

// Check an onMouseDown event to see if we should hide
function PW_hideIfNotClicked(e) {
        var isClicked = this.isClicked(e);
        if(isClicked) { this.gotClicked=true;}
  if (this.autoHideEnabled && !isClicked && this.visible) {
                this.hidePopup();
                }
        }
// Call this to make the DIV disable automatically when mouse is clicked outside it
function PW_autoHide() {
        this.autoHideEnabled = true;
        }
// This global function checks all PopupWindow objects onmouseup to see if they should be hidden
function PW_hidePopupWindows(e) {
        for (var i=0; i<popupWindowObjects.length; i++) {
                if (popupWindowObjects[i] != null) {
                        var p = popupWindowObjects[i];
                        p.hideIfNotClicked(e);
                        }
                }
        }
// Run this immediately to attach the event listener
function PW_attachListener() {
        if (document.layers) {
                document.captureEvents(Event.MOUSEUP);
                }
        window.popupWindowOldEventListener = document.onmouseup;
        if (window.popupWindowOldEventListener != null) {
                document.onmouseup = new Function("window.popupWindowOldEventListener(); PW_hidePopupWindows();");
                }
        else {
                document.onmouseup = PW_hidePopupWindows;
                }
        }

function PW_setHideListener(hideListener) {
  this.hideListener = hideListener;
}

function PW_setTimeout(timeout) {
  this.timeout=timeout;
}

// CONSTRUCTOR for the PopupWindow object
// Pass it a DIV name to use a DHTML popup, otherwise will default to window popup
function PopupWindow() {
        if (!window.popupWindowIndex) { window.popupWindowIndex = 0; }
        if (!window.popupWindowObjects) { window.popupWindowObjects = new Array(); }
        if (!window.listenerAttached) {
                window.listenerAttached = true;
                PW_attachListener();
                }
        this.index = popupWindowIndex++;
        popupWindowObjects[this.index] = this;
        this.divName = null;
        this.popupWindow = null;
        this.width=0;
        this.height=0;
        this.populated = false;
        this.visible = false;
        this.autoHideEnabled = false;

        this.contents = "";
        if (arguments.length>0) {
                this.type="DIV";
                this.divName = arguments[0];
                }
        else {
                this.type="WINDOW";
                }
        this.use_gebi = false;
        this.use_css = false;
        this.use_layers = false;
        if (document.getElementById) { this.use_gebi = true; }
        else if (document.all) { this.use_css = true; }
        else if (document.layers) { this.use_layers = true; }
        else { this.type = "WINDOW"; }
        this.offsetX = 0;
        this.offsetY = 0;
  this.hideListener = null;
  this.timeout = 60000;
  this.gotClicked=false;
  this.calculating=false;

        // Method mappings
        this.getXYPosition = PW_getXYPosition;
        this.populate = PW_populate;
        this.refresh = PW_refresh;
        this.showPopup = PW_showPopup;
        this.hidePopup = PW_hidePopup;
        this.setSize = PW_setSize;
        this.isClicked = PW_isClicked;
        this.autoHide = PW_autoHide;
        this.hideIfNotClicked = PW_hideIfNotClicked;
        this.repositionDiv =PW_repositionDiv;
        this.setHideListener = PW_setHideListener;
        this.setTimeout = PW_setTimeout;
        }


