/* 
 *  Pop Up Calendar 
 *  By Paul Geerts
 *  11 Oct 2002
 *
 *  Designed as a replacement for current Tapestry DatePicker control
 *  to work on both IE5+ and Mozilla based browsers
*/

// Reference Data arrays
var months = new Array("Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic");
var days = new Array("D", "L", "M", "M", "G", "V", "S");
var monthLengths =new Array(31,28,31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
var monthLongNames = new Array('Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre');
var dayNames = new Array('Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì','Venerdì', 'Sabato');


// is the calendar created?
var calDiv = null;

// is the calendar showing?
var showing = false;

var pickArea = null;

// function to create the calendar if necessary and pop it up
// the button is the "V" button that triggers the popup event
function goCalendar(button) {
    if (showing) {   // if we're currently showing, stop it
        closeCalendar();
    }

    format = button.previousSibling;      // get the hidden text field containng the desired date format 
    val = format.previousSibling;         // get the date as milliseconds since 1970 
    disp = val.previousSibling;           // get the display field

    currentDate = new Date();             // create a new date to hold the
    if (val.value != "") {
        currentDate.setTime(val.value);   // start value 
        oldDate = currentDate;            // Rememeber the old date so we can show it as blue
    } else {
        oldDate = null;
    }

    var point = getPoint(disp);

    if (!calDiv) {                        // if we haven't create the calendar, go to
        calDiv = document.createElement("DIV");
        calDiv.style.backgroundColor = "#cccccc";
        calDiv.style.border = "2px outset";
        calDiv.style.position = "absolute";
        calDiv.style.top = point.y + disp.offsetHeight + 1;
        calDiv.style.left = point.x;
        calDiv.style.fontFamily = "sans-serif";
        calDiv.appendChild(createCalendar(currentDate));
        document.body.appendChild(calDiv);
    } else {                             // otherwise, just move and unhide it
        calDiv.style.display = "block";
        calDiv.style.top = point.y + disp.offsetHeight + 1;
        calDiv.style.left = point.x;
        updatePickArea();               // update the selection area
    }

    showing = true;  // we are now showing

    // this needs to be done outsite a click event for some reason
    setTimeout(addBodyClick, 1);

}

function addBodyClick() {   // this adds the handler which hides the 
                            // picker when something else is clicked
    addEvent("click", document.body, closeCalendar);
}

function createCalendar(date) {   // create calendar using W3C DOM methods

    var table = document.createElement("TABLE");
    var tbody = document.createElement("TBODY");
    table.appendChild(tbody);
    // create head row with dropdowns
    var headtr = document.createElement("TR");
    tbody.appendChild(headtr);
    headtr.appendChild(getHeadTD(currentDate));

    // create body TR with picker
    var bodytr = document.createElement("TR");
    tbody.appendChild(bodytr);
    pickArea = createPickArea();
    updatePickArea();
    bodytr.appendChild(pickArea);

    // create footer TR with "Today" link
    var foottr = document.createElement("TR");
    foottd = document.createElement("TD");
    foottr.appendChild(foottd);
    foottd.align = "middle";
    var today = new Date();
    foottd.innerHTML = "Oggi : " + months[today.getMonth()] + " " + today.getDate()  + ", " + today.getFullYear();
    foottd.date = new Date();
    foottd.align="center";
    foottd.style.fontSize="8pt";
    setCursor(foottd);
    foottd.style.backgroundColor="#CFE6D7";

    // add events using the approprate method for each browser
    addEvent("mouseover", foottd, tdMouseOver);
    addEvent("mouseout", foottd, tdMouseOut);
    addEvent("click", foottd, tdClick);
    tbody.appendChild(foottr);


    return table;

}

function getHeadTD(currentDate) {  // create dropdown TD 
    var td = document.createElement("TD");
    td.align = "center";
    td.valign = "top";

    monthSelect = getMonthSelect(currentDate);  // remember these so we can update them later
    yearSelect = getYearSelect(currentDate);
    td.appendChild(monthSelect);
    td.appendChild(yearSelect);

    
    return td;
}

function getMonthSelect(currentDate) {  // return a select with a list of months

    var sel = document.createElement("SELECT");
    for (var i = 0 ; i < months.length ; i++) {
        var opt = document.createElement("OPTION");
        opt.innerHTML = monthLongNames[i];
        opt.value = i;
        if (i == currentDate.getMonth()) {
            opt.selected = true;
        }
        sel.appendChild(opt);
    }
    
    // add events
    addEvent("change", sel, changeMonth);   

    // the doNuffin event is to cancel the bubble
    // without it, the calendar would disappear
    // when you clicked on the dropdpwn
    addEvent("click", sel, doNuffin);  
                                       
    return sel;
}

function getYearSelect(currentDate) {   // generate a list
                                        // of years
    var sel = document.createElement("SELECT");
    var year = currentDate.getFullYear();
    for (var i = 1940; i < 2121; i++) {
        var opt = document.createElement("OPTION");
        opt.innerHTML = i;
        opt.value = i;
        if (i == year) {
            opt.selected = true;
        }
        sel.appendChild(opt);
    }

    addEvent("change", sel, changeYear);
    addEvent("click", sel, doNuffin);
    return sel;
}

function createPickArea() {    // create the area where the calendar actually sits
    var bigtd = document.createElement("TD");
    var table = document.createElement("TABLE");
    var tbody = document.createElement("TBODY");
    bigtd.appendChild(table);
    table.appendChild(tbody);
    table.style.width="100%";
    table.cellSpacing = 0;
    table.style.backgroundColor="#ffffff";
    var trhead = document.createElement("TR");
    
    // create the header labels
    for (var i = 0 ; i < 7 ; i ++) {
        var td = document.createElement("TD");
        td.innerHTML = days[i];
        td.align="center";
        td.style.fontSize="8pt";
        td.style.color = "#ffffff";
        td.style.backgroundColor = "#777777";   
        trhead.appendChild(td);
    }
    tbody.appendChild(trhead);
    // create the grid for the 
    // date picking area
    for (var i = 0 ; i < 6 ; i ++) {
        var tr = document.createElement("TR");
        for (var j = 0 ; j < 7 ; j ++) {
            var td = document.createElement("TD");
            td.style.fontSize="8pt";
            setCursor(td);
            td.innerHTML = i*7+j;
            td.align="center";
            // add events for highlighting and clicking
            addEvent("mouseover", td, tdMouseOver);
            addEvent("mouseout", td, tdMouseOut);
            addEvent("click", td, tdClick);
            tr.appendChild(td);
        }
        tbody.appendChild(tr);
    }

    bigtd.tbody = tbody;
    return bigtd;
}


function updatePickArea() {  // this changes the pick area to reflect the current date

    monthSelect.value = currentDate.getMonth();
    yearSelect.value = currentDate.getFullYear();
    var first = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1);
    var dayNumber = new Array();
    var monthOffset = new Array();
    var currdn = 0;

    // work out how what days from the previous month should be displayed
    for (var i = 0 ; i < first.getDay() ; i++) {
        var back = new Date();
        var daysback = first.getDay() - i;
        back.setTime(first - daysback * 1000 * 60 * 60 * 24);
        dayNumber[currdn] = back.getDate();
        monthOffset[currdn] = -1;
        currdn++;
    }

    // work out how what days from the current month should be displayed
    for (var i = 1 ; i <= monthLengths[first.getMonth()] ; i++) {
        dayNumber[currdn] = i;
        monthOffset[currdn] = 0;
        currdn++;
    }

    // work out how what days from the next month should be displayed
    var nm = 1;
    while (currdn < 42) {
        dayNumber[currdn] = nm++;
        monthOffset[currdn] = 1;
        currdn++;
    }
    
    // update the cells to the correct values
    var c = 0;
    for (var i = 1 ; i <= 6 ; i++) {
        row = pickArea.tbody.childNodes[i];
        for (var j = 0 ; j < 7 ; j++) {
            col = row.childNodes[j];
            col.innerHTML = dayNumber[c];
            // store the date for this cell as an ad-hoc property of the cell
            // using the month-offset like this could lead to having negative
            // months (months start at zero for some reason) 
            // but the JS Date object works everything out fine
            col.date = new Date(currentDate.getFullYear(),
                                            currentDate.getMonth() + monthOffset[c],
                                            dayNumber[c]);

            // format the cell colour
            col.style.backgroundColor = "#ffffff";
            if (monthOffset[c]!=0) {
                col.style.color = "#aaaaaa";
            } else {
                col.style.color = "#000000";
            }

            // blue if current date
            if (oldDate != null && 
                col.date.getFullYear() == oldDate.getFullYear() &&
                col.date.getMonth() == oldDate.getMonth() && 
                col.date.getDate() == oldDate.getDate()) {
                col.style.color = "#0000FF";
            }            
            c++;
                                
        }
    }    
}


function changeMonth(e) {    // event handler for changing month
    var sel = getEventObject(e);
    currentDate = new Date(currentDate.getFullYear(), sel.value, currentDate.getDate());
    updatePickArea();

}

function changeYear(e) {  // event handler for changing year
    var sel = getEventObject(e);
    currentDate = new Date(sel.value,currentDate.getMonth(), currentDate.getDate());
    updatePickArea();

}

function doNuffin(e) {  // cancels bubble, see comment on line 140
    e.cancelBubble = true;
}

// highlite the cell
function tdMouseOver(e) {
    var td = getEventObject(e);
    td.oldBackground = td.style.backgroundColor;
    td.style.backgroundColor = "yellow";
}

// put it back the way it was
function tdMouseOut(e) {
    var td = getEventObject(e);
    td.style.backgroundColor = td.oldBackground;
}

// select a date by clicking
function tdClick(e) {
    tdMouseOut(e);   // ensure bg colour is reset, because we are reusing the date picker
    var td = getEventObject(e);
    setDate(td.date);
}

function setDate(date) {   // update the date display and hidden value
    var dstring = formatDate(date, format.value);
    
    disp.value = dstring;
    val.value = date.getTime();
    closeCalendar();
}

function closeCalendar() {   // put the calendar away
    deleteEvent("click", document.body, closeCalendar);  // remove the event
    calDiv.style.display = "none";
    showing = false;
}

function getEventObject(e) {  // utility function to retrieve object from event
    if (navigator.appName == "Microsoft Internet Explorer") {
        return e.srcElement;
    } else {  // is mozilla/netscape
        // need to crawl up the tree to get the first "real" element
        // i.e. a tag, not raw text
        var o = e.target;
        while (!o.tagName) {
            o = o.parentNode;
        }
        return o;
    }
}

function pad(number,X) {   // utility function to pad a number to a given width
	X = (!X ? 2 : X);
	number = ""+number;
	while (number.length < X) {
	    number = "0" + number;
	}
	return number;
}

function formatDate(date, format) {  // formats the date based on the date format specified
    var bits = new Array();
    // work out what each bit should be
    bits['d'] = date.getDate();
    bits['dd'] = pad(date.getDate(),2);
    bits['dddd'] = dayNames[date.getDay()];

    bits['M'] = date.getMonth()+1;
    bits['MM'] = pad(date.getMonth()+1,2);
    bits['MMM'] = months[date.getMonth()];
    bits['MMMM'] = monthLongNames[date.getMonth()];
    
    var yearStr = "" + date.getFullYear();
    yearStr = (yearStr.length == 2) ? '19' + yearStr: yearStr;
    bits['yyyy'] = yearStr;
    bits['yy'] = bits['yyyy'].toString().substr(2,2);

    // do some funky regexs to replace the format string
    // with the real values
    var frm = new String(format);
    var sect;
    for (sect in bits) {
      frm = eval("frm.replace(/\\b" + sect + "\\b/,'" + bits[sect] + "');");
    }

    return frm;
}


function addEvent(name, obj, funct) { // utility function to add event handlers

    if (navigator.appName == "Microsoft Internet Explorer") {
        obj.attachEvent("on"+name, funct);
    } else {  // is mozilla/netscape
        obj.addEventListener(name, funct, false);
    }
}


function deleteEvent(name, obj, funct) { // utility function to delete event handlers

    if (navigator.appName == "Microsoft Internet Explorer") {
        obj.detachEvent("on"+name, funct);
    } else {  // is mozilla/netscape
        obj.removeEventListener(name, funct, false);
    }
}

function setCursor(obj) {
   if (navigator.appName == "Microsoft Internet Explorer") {
        obj.style.cursor = "hand";
    } else {  // is mozilla/netscape
        obj.style.cursor = "pointer";
    }
}

// Point x, y class
function Point(iX, iY)
{
   this.x = iX;
   this.y = iY;
}

// Get the Point of the given tag
function getPoint(aTag)
{
   var oTmp = aTag;  
   var point = new Point(0,0);
  
   do 
   {
      point.x += oTmp.offsetLeft;
      point.y += oTmp.offsetTop;
      oTmp = oTmp.offsetParent;
   } 
   while (oTmp.tagName != "BODY");

   return point;
}
