// This code is based on the following, which appear to be based on one
// another:
// http://www.webreference.com/programming/javascript/gr/
// http://www.codeproject.com/useritems/datepicker.asp

function DatePicker(name, earliest, latest, textForSelectNone)
{
    this.name = name;
    this.dt = new Date();
    if (earliest)
        this.earliest = new Date(earliest);
    else // default to Jan 1 of this year
        this.earliest = new Date(this.dt.getFullYear(), 1, 1);
    
    if (latest)
        this.latest = new Date(latest);
    else // default to Dec 31 of this year
        this.latest = new Date(this.dt.getFullYear(), 11, 31);
    
    this.dt = this.ensureDTinValidRange(this.dt);
    this.shown_dt = this.dt;

    this.textForSelectNone = textForSelectNone;

    var body = document.getElementsByTagName('body')[0];
    
    this.oFrame = document.createElement('iframe');
    this.oFrame.id = name + '_frame';
    this.oFrame.src = 'about:blank';
    this.oFrame.style.zIndex = 0;
    this.oFrame.style.position = 'absolute';
    this.oFrame.style.visibility = 'hidden';
    this.oFrame.style.display = 'block';
    this.oFrame.setAttribute('class', 'DatePickerBackingFrame');
    body.appendChild(this.oFrame);

    this.oSpan = document.createElement('span');
    this.oSpan.id = name;
    this.oSpan.style.zIndex = 0;
    this.oSpan.style.position = 'absolute';
    this.oSpan.style.visibility = 'hidden';
    this.oSpan.setAttribute('class', 'DatePicker');
    body.appendChild(this.oSpan);

    this.rendered = false;
}

DatePicker.prototype.show =
function(dt, x, y, callback)
{
    if ( dt ) {
        this.shown_dt = this.dt = dt;
    }
    this.callback = callback;
 
    // if not rendered yet, do so
    if ( !this.rendered ) this.render();
 
    // set coordinates
    this.oSpan.style.left = this.oFrame.style.left = x + 'px';
    this.oSpan.style.top = this.oFrame.style.top = y + 'px';

    this.fill();
 
    this.oSpan.style.visibility = this.oFrame.style.visibility = "visible";
    this.oFrame.style.width = this.oSpan.offsetWidth + 'px';
    this.oFrame.style.height = this.oSpan.offsetHeight + 'px';

    // this line will cause an error in Firefox if show() is called by an
    // onfocus event of an <input type="text"> tag, if the tag does not include
    // autocomplete="off".  So include it, if onfocus is what triggers show().
    this.oMonth.focus(); 
}
 
DatePicker.prototype.hide =
function()
{
    if ( this.oSpan ) {
        this.oSpan.style.visibility = this.oFrame.style.visibility = 'hidden';
    }
}

DatePicker.prototype.render =
function()
{
    var oT1, oTR1, oTD1, oTH1;
    var oT2, oTR2, oTD2;
    var oDatePicker = this; // 
    
    this.oSpan.appendChild(oT1 = document.createElement("table"));
    oT1.className = 'DatePickerFrame'
 
    oTR1 = oT1.insertRow(oT1.rows.length);
    oTD1 = oTR1.insertCell(oTR1.cells.length);
    oTD1.colSpan = 7;
    oTD1.className = 'DatePickerControlRow';

    
    oT2 = document.createElement("table");
    oTD1.appendChild(oT2);
    oT2.className = 'DatePickerControls';
 
    oTR2 = oT2.insertRow(oT2.rows.length);
    
    oTD2 = oTR2.insertCell(oTR2.cells.length);
    oTD2.title = this.texts.prevMonth;
    oTD2.onclick =
        function() { this.oDatePicker.onPrev(); };
    oTD2.oDatePicker = this;
    oTD2.innerHTML = "&lt;&lt;";
    oTD2.className = 'DatePickerCtrlBtn';
 
    // Month combo.
    oTD2 = oTR2.insertCell(oTR2.cells.length);
    this.oMonth = document.createElement("select");
    oTD2.appendChild(this.oMonth);
    this.oMonth.oDatePicker = this;
    this.oMonth.onchange = this.oMonth.onkeyup =
        function() { this.oDatePicker.onMonth(); };
    this.oMonth.className = 'DatePickerCtrlBtn';
     
    // Year combo.
    oTD2 = oTR2.insertCell(oTR2.cells.length);
    oTD2.title = this.texts.yearTitle;
    this.oYear = document.createElement("select");
    oTD2.appendChild(this.oYear);
    this.oYear.oDatePicker = this;
    this.oYear.onchange = this.oYear.onkeyup =
        function() { this.oDatePicker.onYear(); };
    for(i = this.earliest.getFullYear(); i <= this.latest.getFullYear(); i++)
    {
        this.oYear.add(new Option(i, i),undefined);
    }
    this.oYear.className = 'DatePickerCtrlBtn';
    
    oTD2 = oTR2.insertCell(oTR2.cells.length);
    oTD2.title = this.texts.nextMonth;
    oTD2.onclick =
        function() { this.oDatePicker.onNext(); };
    oTD2.oDatePicker = this;
    oTD2.innerHTML = "&gt;&gt;";
    oTD2.className = 'DatePickerCtrlBtn';

    // Close button.
    oTD2 = oTR2.insertCell(oTR2.cells.length);
    oTD2.title = this.texts.close;
    oTD2.onclick =
        function() { this.oDatePicker.hide(); };
    oTD2.oDatePicker = this;
    oTD2.innerHTML = "[x]";
    oTD2.className = 'DatePickerCtrlBtn';
    oTD2.title = "Close";
    
   
    oTR1 = oT1.insertRow(oT1.rows.length);
    for ( i = 0; i < 7; i++ )
    {
        oTH1 = document.createElement("th");
        oTR1.appendChild(oTH1);
        oTH1.innerHTML = this.texts.days[i];
        oTH1.className = 'DatePickerDayHeader';
    }
 
    this.aCells = new Array;
    this.aRows = new Array;
    for ( var j = 0; j < 6; j++ )
    {
        this.aCells.push(new Array);
        oTR1 = oT1.insertRow(oT1.rows.length);
        this.aRows[j] = oTR1;
        for ( i = 0; i < 7; i++ )
        {
            this.aCells[j][i] = oTR1.insertCell(oTR1.cells.length);
            this.aCells[j][i].oDatePicker = this;
            this.aCells[j][i].onclick =
                function() { oDatePicker.onDay(this); };
        }
    }

    if (this.textForSelectNone)
    {
        oTR1 = oT1.insertRow(oT1.rows.length);
        oTD1 = oTR1.insertCell(oTR1.cells.length);
        oTD1.colSpan = 7;
        oTD1.className = 'DatePickerSelectNoneRow';
        oTD1.innerHTML = this.textForSelectNone;
        oTD1.onclick =
            function() { oDatePicker.hide(); oDatePicker.callback(null); };
    }

    this.repopMonths();

    this.rendered = true;
}
 
DatePicker.prototype.fill =
function()
{
    // first clear all
    this.clear();

    // place the dates in the calendar
    var nRow = 0;
    var d = new Date(this.shown_dt.getFullYear(), this.shown_dt.getMonth(), 1);
    var today = new Date();
    today = new Date(today.getFullYear(), today.getMonth(), today.getDate());

    for ( ; d.getMonth() == this.shown_dt.getMonth(); d.setDate(d.getDate() + 1) )
    {
        this.aRows[nRow].style.display = '';
        
        var nCol = d.getDay();
        this.aCells[nRow][nCol].innerHTML = d.getDate();

        switch ( d.getDay() ) {
        case 0:
        case 6:
            this.aCells[nRow][nCol].className += ' DatePickerWeekend';
            break;
        default:
            this.aCells[nRow][nCol].className += ' DatePickerWeekday';
            break;
        }

        
        if ( d.getTime() == this.dt.getTime() )
        {
            this.aCells[nRow][nCol].className += ' DatePickerSelectedDay';
        }
        
        if ( d.getTime() == today.getTime() )
        {
            this.aCells[nRow][nCol].className += ' DatePickerToday';
        }
        
        if ( nCol == 6 ) nRow++;
    }

    // set the month combo
    this.oMonth.value = this.shown_dt.getMonth();
 
    // set the year combo
    this.oYear.value = this.shown_dt.getFullYear();
}
 
DatePicker.prototype.clear =
function()
{
    for ( var j = 0; j < 6; j++ )
    {
        this.aRows[j].style.display = 'none';
        for ( var i = 0; i < 7; i++ )
        {
            this.aCells[j][i].innerHTML = "";
            this.aCells[j][i].className = 'DatePickerBtn';
        }
    }
}

function getLastDayOfMonth(d) {
    var d_ldom = new Date(d.getFullYear(), d.getMonth()+1, 1);
    d_ldom.setDate(0);
    return d_ldom;
}

function getFirstDayOfMonth(d) {
    return new Date(d.getFullYear(), d.getMonth(), 1);
}


DatePicker.prototype.repopMonths =
function()
{
    this.oMonth.length = 0;
    
    var d = new Date(this.dt.getFullYear(), 0, 1);
    var earliest_fdom = getFirstDayOfMonth(this.earliest);
    var latest_ldom = getLastDayOfMonth(this.latest);
    for ( var i = 0; i < 12; i++ )
    {
        d.setMonth(i);
        if(d >= earliest_fdom && d <= latest_ldom )
        {
            this.oMonth.add(new Option(this.texts.months[i], i),undefined);
        }
    }
}

DatePicker.prototype.setDate =
function (new_dt)
{
    var old_dt = this.dt;
    this.dt = new_dt = this.ensureDTinValidRange(new_dt);

    showDate(this.dt);
}

DatePicker.prototype.showDate =
function (new_dt)
{
    var old_dt = this.shown_dt;
    this.shown_dt = new_dt = this.ensureDTinValidRange(new_dt);

    if ( old_dt.getTime() == new_dt.getTime() )
        return;
    
    if ( new_dt.getFullYear() != old_dt.getFullYear() ) {
        this.oYear.value = new_dt.getFullYear();
        this.repopMonths();
    }
    
    if ( new_dt.getMonth() != old_dt.getMonth() ) {
        this.oMonth.value = new_dt.getMonth();
    }
    
    this.fill();
}

DatePicker.prototype.onPrev =
function()
{
    var new_dt = new Date(this.shown_dt);
    new_dt.setMonth( this.shown_dt.getMonth() - 1 );
    this.showDate(new_dt);
}
 
DatePicker.prototype.onNext =
function()
{
    var new_dt = new Date(this.shown_dt);
    new_dt.setMonth( this.shown_dt.getMonth() + 1 );
    this.showDate(new_dt);
}

DatePicker.prototype.onMonth =
function()
{
    var new_dt = new Date(this.shown_dt);
    new_dt.setMonth( this.oMonth.value );
    this.showDate(new_dt);
}

DatePicker.prototype.onYear =
function()
{
    var new_dt = new Date(this.shown_dt);
    new_dt.setFullYear(this.oYear.value);
    this.showDate(new_dt);
}

DatePicker.prototype.onDay =
function(oCell)
{
    var d = parseInt(oCell.innerHTML);
    if ( d > 0 )
    {
        this.dt = new Date(this.shown_dt);
        this.dt.setDate(d);
        this.hide();
        this.callback(this.dt);
    }
}


DatePicker.prototype.ensureDTinValidRange =
function(dt)
{
    if(dt < this.earliest)
        return new Date(this.earliest);
    
    if(dt > this.latest)
        return new Date(this.latest);

    return dt;
}


DatePicker.prototype.texts = {
    months: [
        "January", "February", "March",
        "April", "May", "June",
        "July", "August", "September",
        "October", "November", "December"
        ],
    days: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
    prevMonth: "Previous Month",
    nextMonth: "Next Month",
    yearTitle: "Year. Click to modify."
}

