// Constants
// ---------
var DEFAULT_DATE_SEPARATOR = "/";        
var DEFAULT_DATE_FORMAT = "DMY";
var DEFAULT_OFFSET_X = 5;
var DEFAULT_OFFSET_Y = 5;
var DEFAULT_DIV_NAME = "dpDIV";
var DEFAULT_FRAME_NAME = "dpFRAME";
var DEFAULT_MIN_YEAR = 1900;
var DEFAULT_MIN_MONTH = 0;
var DEFAULT_MAX_YEAR = 2100;
var DEFAULT_MAX_MONTH = 11;
var DEFAULT_FUTURE_ONLY = false;


var DAYS_SHORT = new Array('Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su');
var DAYS_MEDIUM = new Array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun');
var DAYS_LONG = new Array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday');
var MONTHS_SHORT = new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
var MONTHS_MEDIUM = new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec');
var MONTHS_LONG = new Array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');

var HTML_CRLF = "\r\n";
var HTML_DIV_MONTH_YEAR = "<div class='dpMonthYear'>";
var HTML_DIV_DAY_SELECTED = "<div class='dpDaySelected'>";
var HTML_xDIV = "</div>";
var HTML_TABLE = "<table class='dpCalendar'>" + HTML_CRLF;
var HTML_xTABLE = "</table>" + HTML_CRLF;
var HTML_TR_HEADER = "<tr>";
var HTML_TR_DAYS = "<tr>";
var HTML_TR_DATES = "<tr>";
var HTML_TR_FOOTER = "<tr>";
var HTML_xTR = "</tr>" + HTML_CRLF;
var HTML_TD_HEADER = "<td colspan=5 valign=bottom>";
var HTML_TD_MOVE = "<td>";
var HTML_TD_DAYS = "<td class='dpDaysOfWeek'>";
var HTML_TD_DATES= "<td class='dpDates' onMouseOut='this.className=\"dpDates\";' onMouseOver=' this.className=\"dpDatesHoverCell\";' ";
var HTML_TD_DATES_GONE= "<td class='dpDatesGone'>";
var HTML_TD_DATES_NULL= "<td>";
var HTML_TD_FOOTER = "<td colspan=7>";
var HTML_TD_SELECTED = "<td class='dpDaySelectedCell' onMouseOut='this.className=\"dpDaySelectedCell\";' onMouseOver='this.className=\"dpDatesHoverCell\";' ";
var HTML_xTD = "</td>" + HTML_CRLF;


// Globals
// -------
var gDivID = DEFAULT_DIV_NAME;
var gIFrameID = DEFAULT_FRAME_NAME;
var gDateSeparator = DEFAULT_DATE_SEPARATOR;
var gDateFormat = DEFAULT_DATE_FORMAT;
var gRange_MinYear = DEFAULT_MIN_YEAR;
var gRange_MinMonth = DEFAULT_MIN_MONTH;
var gRange_MaxYear = DEFAULT_MAX_YEAR;
var gRange_MaxMonth = DEFAULT_MAX_MONTH;
var gFutureOnly = DEFAULT_FUTURE_ONLY;

// ---------------------------------------------------------
// StringToDate:
// Convert a date string to a JavaScript Date object
// ---------------------------------------------------------
function StringToDate(pDateString)
{
  var dateVal;
  var dArray;
  var d, m, y;
 
  try {
    dArray = SplitDateString(pDateString);
   
    if (dArray) {
      switch (gDateFormat) {
        case "DMY" :
          d = parseInt(dArray[0], 10);
          m = parseInt(dArray[1], 10) - 1;
          y = parseInt(dArray[2], 10);
          break;
        case "YMD" :
          d = parseInt(dArray[2], 10);
          m = parseInt(dArray[1], 10) - 1;
          y = parseInt(dArray[0], 10);
          break;
        case "MDY" :
        default :
          d = parseInt(dArray[1], 10);
          m = parseInt(dArray[0], 10) - 1;
          y = parseInt(dArray[2], 10);
          break;
      }
      dateVal = new Date(y, m, d);
    } else if (pDateString) {
      dateVal = new Date(pDateString);
    } else {
      dateVal = new Date();
    }
  } catch(e) {
    dateVal = new Date();
  }
 
  return dateVal;
}


// ---------------------------------------------------------
// SplitDateString:
// Split a date string into an array of elements
// ---------------------------------------------------------
function SplitDateString(pDateString)
{
  var dArray;
  if (pDateString.indexOf("/") >= 0)
    dArray = pDateString.split("/");
  else if (pDateString.indexOf(".") >= 0)
    dArray = pDateString.split(".");
  else if (pDateString.indexOf("-") >= 0)
    dArray = pDateString.split("-");
  else if (pDateString.indexOf("\\") >= 0)
    dArray = pDateString.split("\\");
  else
    dArray = false;
 
  return dArray;
}


// ----------------------------------------------------------------------------
// ApplyIFrameShim:
// Use iFrame "shim" to prevent underlying controls showing through calendar
//
// Notes:
// Windowed controls in IE will always cover DHTML layers. If a floating <DIV> 
// intersects with a windowed control (eg. <SELECT>), the windowed control will 
// obscure the <DIV>. Placing an <IFRAME> under the <DIV> will block out the 
// windowed control.
// ----------------------------------------------------------------------------
function ApplyIFrameShim(pDiv, pIFrame)
{
  // technique does not work on Opera browser
  // ----------------------------------------
  var is_opera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
  if (is_opera)
    return;
  
  try {
    if (!document.getElementById(gIFrameID)) {
      var newNode = document.createElement("iFrame");
      newNode.setAttribute("id", gIFrameID);
      newNode.setAttribute("src", "javascript:false;");
      newNode.setAttribute("scrolling", "no");
      newNode.setAttribute ("frameborder", "0");
      document.body.appendChild(newNode);
    }
    
    if (!pDiv)
      pDiv = document.getElementById(gDivID);

    if (!pIFrame)
      pIFrame = document.getElementById(gIFrameID);
    
    try {
      pIFrame.style.position = "absolute";
      pIFrame.style.width = pDiv.offsetWidth;
      pIFrame.style.height = pDiv.offsetHeight ;
      pIFrame.style.top = pDiv.style.top;
      pIFrame.style.left = pDiv.style.left;
      pIFrame.style.zIndex = pDiv.style.zIndex - 1;
      pIFrame.style.visibility = pDiv.style.visibility ;
      pIFrame.style.display = pDiv.style.display;
    } catch(e) {
    }
 
  } catch (ee) {
  }
}


// ---------------------------------------------------------
// UpdateTarget:
// Update the target field and hide the datepicker
// ---------------------------------------------------------
function UpdateTarget(pTargetField, pDateString)
{
  var TargetField = document.getElementsByName (pTargetField).item(0);
  if (pDateString)
    TargetField.value = pDateString;
 
  var pickerDiv = document.getElementById(gDivID);
  pickerDiv.style.visibility = "hidden";
  pickerDiv.style.display = "none";
 
  ApplyIFrameShim();
  TargetField.focus();
}


// ---------------------------------------------------------
// DateToString:
// Convert a JavaScript Date object to a numeric date string
// ---------------------------------------------------------
function DateToString(pDate)
{
  var dayString = "00" + pDate.getDate();
  var monthString = "00" + (pDate.getMonth()+1);
  dayString = dayString.substring(dayString.length - 2);
  monthString = monthString.substring(monthString.length - 2);

  // String format determined by gDateFormat and gDateSeparator
  // ----------------------------------------------------------
  switch (gDateFormat) {
  case "DMY" :
    return dayString + gDateSeparator + monthString + gDateSeparator + pDate.getFullYear();
  case "YMD" :
    return pDate.getFullYear() + gDateSeparator + monthString + gDateSeparator + dayString;
  case "MDY" :
     default :
    return monthString + gDateSeparator + dayString + gDateSeparator + pDate.getFullYear();
  }
}


// ------------------------------------------------
// GenerateNextPrev:
// Generates code for the next and previous buttons
// ------------------------------------------------
function GenerateNextPrev(pTargetField, pCurrentDate, pAdjust, pUnit, pButtonLabel)
{
  // Set tooltip text
  // ----------------
  var Tooltip
  if (pUnit == "Y") {
     if (pAdjust < 0) {
        Tooltip = "Previous year";
     } else {
        Tooltip = "Next year";
     };   
  } else {
     if (pAdjust < 0) {
        Tooltip = "Previous month";
     } else {
        Tooltip = "Next month";
     };   
  };


  // Adjust month and year for direction 
  // -----------------------------------
  var newMonth
  var newYear
  if (pUnit == "Y") {
     newMonth = pCurrentDate.getMonth();
     newYear = pCurrentDate.getFullYear() + pAdjust;
  } else {  
     newMonth = (pCurrentDate.getMonth () + pAdjust) % 12;
     newYear = pCurrentDate.getFullYear() + parseInt((pCurrentDate.getMonth() + pAdjust) / 12);
     if (newMonth < 0) {
        newMonth += 12;
        newYear += -1;
     }
  };


  // Generate html for the button. If adjusted month and
  // year are outside current range no button is created 
  // ---------------------------------------------------
  if (pAdjust < 0) {
     if ( (newYear < gRange_MinYear) || ((newYear == gRange_MinYear) && (newMonth < gRange_MinMonth)) ) {
        return "&nbsp;"
     };
  }
  else {
     if ( (newYear > gRange_MaxYear) || ((newYear == gRange_MaxYear) && (newMonth > gRange_MaxMonth)) ) {
        return "&nbsp;"
     };
  }

  return "<button class='dpButton' title='" + Tooltip + "' onClick='RefreshPicker(\"" + pTargetField + "\", " + newYear + ", " + newMonth + ");'>" + pButtonLabel + "</button>";
}


// ------------------------------------------------------------
// ShiftDay:
// The javascript getDay method returns 0=Sun, 1=Mon .. 6=Sat,
// convert so that Sunday is end of week 0=Mon, 1=Tue ... 6=Sun
// ------------------------------------------------------------
function ShiftDay(pDay)
{
  return (pDay == 0)?6:pDay - 1;
}


// --------------------------------------------------------
// RefreshPicker:
// Generates the html that makes up the DatePicker calendar
// --------------------------------------------------------
function RefreshPicker(pTargetField, pYear, pMonth, pDay)
{
  var TempDate, LoopDate, LoopDay, LoopMonth, LoopYear;
  var Todays_Date, SelectedDay, Html, OnClick;

  // LoopDate is set to the 1st of the user selected Year/Month or
  // the 1st of the current Year/Month if no date has been selected
  //
  // Todays_Date is the current date
  //
  // SelectedDay is either the user selected day or the current
  // day if no date has been selected.
  //
  // Note:
  // LoopDate and Todays_Date are created with a zeroised time
  // element so comaprisons are based on the date only.
  // --------------------------------------------------------------
  TempDate = new Date();
  LoopDate = new Date(TempDate.getFullYear(), TempDate.getMonth(), TempDate.getDate());
  Todays_Date = new Date(TempDate.getFullYear(), TempDate.getMonth(), TempDate.getDate());
  if ((pMonth >= 0) && (pYear > 0)) {
    SelectedDay = pDay;
    LoopDate = new Date(pYear, pMonth, 1);
  } else {
    SelectedDay = LoopDate.getDate();
    LoopDate.setDate(1);
  }
 
 
  // Generate html for the calendar table
  // ------------------------------------
  Html = HTML_TABLE;
 
  // Header row, includes prev and next buttons, if the range of years
  // is large then there are buttons to increment years AND months
  // -----------------------------------------------------------------
  Html += HTML_TR_HEADER;
  if (gRange_MaxYear - gRange_MinYear <= 2) {
     Html += HTML_TD_MOVE + GenerateNextPrev(pTargetField, LoopDate, -1, "M", "&lt;") + HTML_xTD;
  } else {
     Html += HTML_TD_MOVE + GenerateNextPrev(pTargetField, LoopDate, -1, "M", "&lt;") + "<BR>" + GenerateNextPrev(pTargetField, LoopDate, +1, "M", "&gt;") + HTML_xTD;
  };
  Html += HTML_TD_HEADER + HTML_DIV_MONTH_YEAR + MONTHS_LONG[ LoopDate.getMonth()] + " " + LoopDate.getFullYear() + HTML_xDIV + HTML_xTD;
  if (gRange_MaxYear - gRange_MinYear <= 2) {
     Html += HTML_TD_MOVE + GenerateNextPrev(pTargetField, LoopDate, +1, "M", "&gt;") + HTML_xTD;
  } else {
     Html += HTML_TD_MOVE + GenerateNextPrev(pTargetField, LoopDate, -1, "Y", "&lt;") + "<BR>" + GenerateNextPrev(pTargetField, LoopDate, +1, "Y", "&gt;") + HTML_xTD;
  };
  Html += HTML_xTR;

 
  // Days of the week row
  // --------------------
  Html += HTML_TR_DAYS;
  for(i = 0; i < DAYS_SHORT.length; i++)
    Html += HTML_TD_DAYS + DAYS_SHORT[i] + HTML_xTD;
  Html += HTML_xTR;
 
  // Start rows for the dates
  // ------------------------
  Html += HTML_TR_DATES;
 
  // Leading blank cells, where month does not begin on a Monday
  // -----------------------------------------------------------
  for (i = 0; i < ShiftDay(LoopDate.getDay()); i++)
    Html += HTML_TD_DATES_NULL + "&nbsp;" + HTML_xTD;
 
  // The date cells
  // --------------
  do {
    LoopDay = LoopDate.getDate();
    LoopMonth = LoopDate.getMonth();
    LoopYear = LoopDate.getFullYear();
    OnClick = " onclick=\"UpdateTarget('" + pTargetField + "', '" + DateToString(LoopDate) + "');\">";

    if ( (gFutureOnly) && (LoopDate < Todays_Date) ) {
       Html += HTML_TD_DATES_GONE + "X" + HTML_xTD;
    }
    else {
      if (LoopDay == SelectedDay)
         Html += HTML_TD_SELECTED + OnClick + HTML_DIV_DAY_SELECTED + LoopDay + HTML_xDIV + HTML_xTD;
      else
         Html += HTML_TD_DATES + OnClick + LoopDay + HTML_xTD;
    };
    
    
    if (ShiftDay(LoopDate.getDay()) == 6)
      Html += HTML_xTR + HTML_TR_DATES;
    
    LoopDate.setDate(LoopDate.getDate() + 1);

  } while (LoopDate.getDate() > 1);
 
  // Trailing blank cells, where month does not finish on a Sunday
  // -------------------------------------------------------------
  if (ShiftDay(LoopDate.getDay()) > 0) {
    for (i = 6; i > ShiftDay(LoopDate.getDay()); i--)
      Html += HTML_TD_DATES_NULL+ "&nbsp;" + HTML_xTD;
  }
  Html += HTML_xTR;
 

  // Footer row with buttons
  // -----------------------
  Html += HTML_TR_FOOTER + HTML_TD_FOOTER;
  Html += "<button class='dpButton' title='Jump to today' onClick='RefreshPicker(\"" + pTargetField + "\");'>today</button>&nbsp; ";
  Html += "<button class='dpButton' title='Close calendar' onClick='UpdateTarget(\"" + pTargetField + "\");'>close</button>";
  Html += HTML_xTD + HTML_xTR;
 
  // End of <table>
  // --------------
  Html += HTML_xTABLE;
 

  // Add Html to the <div> and then add an iFrame "shim"
  // to prevent underlying elements showing through
  // ---------------------------------------------------
  document.getElementById(gDivID).innerHTML = Html;
  ApplyIFrameShim();
}


// ----------------------------------------------------------
// DrawPicker:
// The DatePicker object is a <table> within a <div> which is
// positioned at the X,Y coordinates specified
// ----------------------------------------------------------
function DrawPicker(pTargetField, pX, pY)
{
  // Create <div> if not already exist
  // ---------------------------------
  if (!document.getElementById(gDivID)) {
    var newNode = document.createElement("div");
    newNode.setAttribute("id", gDivID);
    newNode.setAttribute("style", "visibility: hidden;");
    document.body.appendChild(newNode);
  }
 
  // Move <div> to x,y coordinate and toggle visiblity
  // -------------------------------------------------
  var pickerDiv = document.getElementById(gDivID);
  pickerDiv.style.position = "absolute";
  pickerDiv.style.left = pX + "px";
  pickerDiv.style.top = pY + "px";
  pickerDiv.style.visibility = (pickerDiv.style.visibility == "visible" ? "hidden" : "visible");
  pickerDiv.style.display = (pickerDiv.style.display == "block" ? "none" : "block");
  pickerDiv.style.zIndex = 10000;
 
  // Extract current value and draw calendar
  // ---------------------------------------
  var dt = StringToDate(pTargetField.value);
  RefreshPicker(pTargetField.name, dt.getFullYear(), dt.getMonth(), dt.getDate());
}


// --------------------------------------------------------------------------------------
// DatePicker:
// The main function that is called from the html
//
// Arguments:
//  pTarget       : Name of the html text box that will hold the picked date
//  pOffsetX      : Horizontal offset from text box BLHC where calendar will be displayed
//  pOffsetY      : Vertical offset from text box BLHC where calendar will be displayed
//  pFormat       : Date format, "DMY" or "MDY" or "YMD"
//  pSeparator    : Date separator
//  pFutureOnly   : Only show current and future dates
//  pMinYearMonth : Earliest year + month to show
//  pMaxYearMonth : Latest year + month to show 
// --------------------------------------------------------------------------------------
function DatePicker(pTarget, pOffsetX, pOffsetY, pFormat, pSeparator, pFutureOnly, pMinYearMonth, pMaxYearMonth)
{
  // Required arguments
  // ------------------
  var TargetField = document.getElementsByName (pTarget).item(0);

  // Optional arguments
  // ------------------
  if (!pOffsetX) pOffsetX = DEFAULT_OFFSET_X;

  if (!pOffsetY) pOffsetY = DEFAULT_OFFSET_Y;

  if (pSeparator) {
     gDateSeparator = pSeparator;
  } else {
     gDateSeparator = DEFAULT_DATE_SEPARATOR;
  };
 
  if (pFormat) {
     gDateFormat = pFormat;
  } else {
     gDateFormat = DEFAULT_DATE_FORMAT;
  };

  if (pMinYearMonth) {
     gRange_MinYear = parseInt(pMinYearMonth.substr(0,4), 10);
     gRange_MinMonth = parseInt(pMinYearMonth.substr(4,2), 10) - 1;
  } else {
     gRange_MinYear = DEFAULT_MIN_YEAR;
     gRange_MinMonth = DEFAULT_MIN_MONTH;
  };

  if (pMaxYearMonth) {
     gRange_MaxYear = parseInt(pMaxYearMonth.substr(0,4), 10);
     gRange_MaxMonth = parseInt(pMaxYearMonth.substr(4,2), 10) - 1;
  } else {
     gRange_MaxYear = DEFAULT_MAX_YEAR;
     gRange_MaxMonth = DEFAULT_MAX_MONTH;
  };

  if (pFutureOnly) {
     gFutureOnly=true;
     var ThisDate = new Date();
     gRange_MinYear = ThisDate.getFullYear();
     gRange_MinMonth = ThisDate.getMonth();
  } else {
     gFutureOnly = DEFAULT_FUTURE_ONLY;
  };


  // Calculate position of calendar
  // ------------------------------
  var x = TargetField.offsetLeft + pOffsetX;
  var y = TargetField.offsetTop + pOffsetY + TargetField.offsetHeight;
 
  // Handle elements inside tables ..etc 
  // -----------------------------------
  var parent = TargetField;
  while (parent.offsetParent) {
    parent = parent.offsetParent;
    x += parent.offsetLeft;
    y += parent.offsetTop ;
  }
 
  DrawPicker(TargetField, x, y);
}


