/**
 * Calendar Class
 * - Generates a table calendar
 * - outputs as table w/ and w/o links
 *
 * @author Derek Mebus <mebusd@treetopsolutions.com>
 * @version 1.0 10/25/2002
 * @since 0.0
 * @package calendar
 * @copyright Tree Top Solutions
 *
 */

//weekend days of the week
var weekend = new Array(0,6);
//width of calendar table
var calendarWidth = 220;
//color of border
var borderColor = "";
//flag as to whether or not to display a border
var borderSW = false;
//whether to display calendar links
var calendarNav= true;

////instance of date for current date (sets as today for default)
var gCur = new Date();
var ggWinCal;

//months of the year calendar object varibale
Calendar.Months = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];

// Non-Leap year Month days calendar object varibale
Calendar.DOMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
// Leap year Month days calendar object varibale
Calendar.lDOMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

/**
 * Calendar class constructor
 * - initialize object variables
 *
 * @return string
 * @access public
 * @param string p_item target form object
 * @param string p_WinCal target 
 * @param string p_month target date month
 * @param string p_year target date year
 * @param string p_format date formate
 * @refer gWinCal
 * @refer gMonthName
 * @refer gMonth
 * @refer gYearly
 * @refer gYear
 * @refer gDate
 * @refer gFormat
 * @refer gReturnItem
 * @refer xCoord
 * @refer yCoord
 * @see get_month()
 * @see cal_data()
 */
function Calendar(p_item, p_WinCal, p_month, p_year, p_format) {
	if ((p_month == null) && (p_year == null))	return;

	if (p_WinCal == null)
		this.gWinCal = ggWinCal;
	else
		this.gWinCal = p_WinCal;
	
	if (p_month == null) {
		this.gMonthName = null;
		this.gMonth = null;
		this.gYearly = true;
	} else {
		this.gMonthName = Calendar.get_month(p_month);
		this.gMonth = new Number(p_month);
		this.gYearly = false;
	}

	this.gYear = p_year;
	this.gFormat = p_format;
	this.gReturnItem = p_item;
}

//sets functions as calendar object functions
Calendar.get_month = Calendar_get_month;
Calendar.get_daysofmonth = Calendar_get_daysofmonth;
Calendar.calc_month_year = Calendar_calc_month_year;
Calendar.print = Calendar_print;

/**
 * Returns month name
 *
 * @return string
 * @access public
 * @param integer monthNo month pointer to Months array
 * @refer Months
 */ 
function Calendar_get_month(monthNo) {
	return Calendar.Months[monthNo];
}

/**
 * Returns number of days in month
 * - checks for leap years
 *
 * @return string
 * @access public
 * @param integer monthNo month pointer to Months array
 * @param integer p_year year
 * @refer DOMonth
 * @refer DOMonth
 */ 
function Calendar_get_daysofmonth(monthNo, p_year) {
	/* 
	Check for leap year ..
	1.Years evenly divisible by four are normally leap years, except for... 
	2.Years also evenly divisible by 100 are not leap years, except for... 
	3.Years also evenly divisible by 400 are leap years. 
	*/
	if ((p_year % 4) == 0) {
		if ((p_year % 100) == 0 && (p_year % 400) != 0)
			return Calendar.DOMonth[monthNo];
	
		return Calendar.lDOMonth[monthNo];
	} else
		return Calendar.DOMonth[monthNo];
}

/**
 * Returns month and year after an increment or decrement of one month 
 *
 * @return array [0]=>month, [1]=>year
 * @access public
 * @param integer p_Month date month
 * @param integer p_Year date year
 * @param integer incr increment/decrement value (1/-1)
 */
function Calendar_calc_month_year(p_Month, p_Year, incr) {
	/* 
	Will return an 1-D array with 1st element being the calculated month 
	and second being the calculated year 
	after applying the month increment/decrement as specified by 'incr' parameter.
	'incr' will normally have 1/-1 to navigate thru the months.
	*/
	var ret_arr = new Array();
	
	if (incr == -1) {
		// B A C K W A R D
		if (p_Month == 0) {
			ret_arr[0] = 11;
			ret_arr[1] = parseInt(p_Year) - 1;
		}
		else {
			ret_arr[0] = parseInt(p_Month) - 1;
			ret_arr[1] = parseInt(p_Year);
		}
	} else if (incr == 1) {
		// F O R W A R D
		if (p_Month == 11) {
			ret_arr[0] = 0;
			ret_arr[1] = parseInt(p_Year) + 1;
		}
		else {
			ret_arr[0] = parseInt(p_Month) + 1;
			ret_arr[1] = parseInt(p_Year);
		}
	}
	
	return ret_arr;
}

/**
 * Prints calendar
 *
 * @return Boolean
 * @access public
 * @refer ggWinCal
 */
function Calendar_print() {
	ggWinCal.print();
}


// This is for compatibility with Navigator 3, we have to create and discard one object before the prototype object exists.
new Calendar();

/**
 * Returns calendar days in month layout
 * - triggers cal_header() for days of the week headers
 * - triggers cal_data() for days
 *
 * @return string
 * @access public
 * @param Boolean attributes[0] switcher for making days links
 * @see cal_header()
 * @see cal_data()
 */
Calendar.prototype.getMonthlyCalendarCode = function() {
	var vCode = "";
	var vHeader_Code = "";
	var vData_Code = "";
	
	// Begin Table Drawing code here..
	vHeader_Code = this.cal_header();
	vData_Code = (arguments[0]==1) ? this.cal_data(1) : this.cal_data();
	vCode = vCode + vHeader_Code + vData_Code;
	
	return vCode;
}

/**
 * Formats date if today
 *
 * @return string
 * @access public
 * @param string vday date
 * @refer todayColor
 * @refer gMonth
 * @refer gYear
 */
Calendar.prototype.format_day = function(vday) {
    var now = new Date();
	var vNowDay = now.getDate();
	var vNowMonth = now.getMonth();
	var vNowYear = now.getFullYear();

	if (vday == vNowDay && this.gMonth == vNowMonth && this.gYear == vNowYear)
	{
		return ("<span class=\"calendarToday\"><b><i>"+vday+"</i></b></span>");
	}else
		return (vday);
}

/**
 * Formats date if weekend or current selection
 *
 * @return string
 * @access public
 * @param string vday date
 * @refer weekend
 * @refer gCur
 * @refer gDepth
 * @refer gMonth
 * @refer gYear
 */
Calendar.prototype.write_cell_style = function(p_date,day) {
	var i;
	var cur_week = false;
    
	//checks if cell is in current week if depth is week
	if (this.gDepth=="week") {
	    //if current date is in first week of its month
		if ( (gCur.getDate() < 8) && (gCur.getDate()-gCur.getDay() < 2) ) {
		    //if current month is displayed month
			if ( ( this.gMonth==gCur.getMonth() ) && ( this.gYear==gCur.getFullYear() ) ) {
			    //date of first saturday in month
			    var saturday = gCur.getDate() + ( 6 - gCur.getDay() );
			    //if cell is from first week in displayed month
				if (p_date <= saturday && p_date != 0) cur_week = true;
			//if current month is month after displayed month
			} else {
			    //previous month array from current month
			    var prev_month = Calendar_calc_month_year(gCur.getMonth(), gCur.getYear(), -1);
				//if displayed month is previous month from current month
				if ( (this.gMonth==prev_month[0]) && (this.gYear==prev_month[1]) ) {
				    //date of last sunday in displayed month
				    var sunday = Calendar_get_daysofmonth(this.gMonth,this.gYear) - ( gCur.getDay() - gCur.getDate() ) - 1;
				    //if cell is from next month or from same week as current date
				    if ( p_date == 0 || p_date > sunday ) cur_week = true;
				}
			}
		//if current date is in last week of its month
		} else if ( ( Calendar_get_daysofmonth( gCur.getMonth(), gCur.getFullYear() ) - gCur.getDate() ) < 7 ) {
		    //if current month is displayed month
			if ( this.gMonth == gCur.getMonth() && this.gYear == gCur.getFullYear() ) {
			    //date of last sunday in month
				var sunday = gCur.getDate() - gCur.getDay();
			    //if cell is from last week in displayed month
				if ( p_date >= sunday || p_date == 0 ) cur_week = true
			//if displayed month is month after current month
			} else {
			    //next month array from current month
			    var next_month = Calendar_calc_month_year(gCur.getMonth(), gCur.getYear(), 1);
				//if displayed month is next month after current month
				if ( this.gMonth == next_month[0] && this.gYear == next_month[1] ) {
				    //date of first saturday in displayed month
					var saturday = ( 6 - gCur.getDay() ) - ( Calendar_get_daysofmonth( gCur.getMonth(), gCur.getFullYear() ) - gCur.getDate() );
				    //if cell is from last month or from 1st week
				    if ( p_date <= saturday && p_date != 0 ) cur_week = true;
				}
			}
		//if current date is not in first or last week of its month
		} else {
		    //if cell is from displayed month and current month and year are displayed
		    if ( (p_date > 0) && ( this.gMonth==gCur.getMonth() ) && ( this.gYear==gCur.getFullYear() ) ) {
			    var in_week = p_date - ( gCur.getDate() - gCur.getDay() );
		        if ( (in_week >= 0) && (in_week < 7) ) cur_week = true;
			}
		}
	}
	
	//Return special formatting for current selection
	if ( ( p_date==gCur.getDate() && ( this.gMonth==gCur.getMonth() ) && ( this.gYear==gCur.getFullYear() ) ) || cur_week ) 
	    return (" class=\"calendarCurrentSelection\"");
		
	// Return special formatting for the weekend day.
	for (i=0; i<weekend.length; i++) {
		if (day == weekend[i])
			return (" class=\"calendarWeekend\"");
	}
	
	return "";
}

/**
 * Formats date according to format
 *
 * @return string
 * @access public
 * @param string p_day date
 * @refer gMonth
 * @refer gYear
 * @refer gFormat
 */
Calendar.prototype.format_data = function(p_day) {
	var vData;
	var vMonth = 1 + this.gMonth;
	vMonth = (vMonth.toString().length < 2) ? "0" + vMonth : vMonth;
	var vMon = Calendar.get_month(this.gMonth).substr(0,3).toUpperCase();
	var vFMon = Calendar.get_month(this.gMonth).toUpperCase();
	var vY4 = new String(this.gYear);
	var vY2 = new String(this.gYear.substr(2,2));
	var vDD = (p_day.toString().length < 2) ? "0" + p_day : p_day;

	switch (this.gFormat) {
		case "MM\/DD\/YYYY" :
			vData = vMonth + "\/" + vDD + "\/" + vY4;
			break;
		case "MM\/DD\/YY" :
			vData = vMonth + "\/" + vDD + "\/" + vY2;
			break;
		case "MM-DD-YYYY" :
			vData = vMonth + "-" + vDD + "-" + vY4;
			break;
		case "MM-DD-YY" :
			vData = vMonth + "-" + vDD + "-" + vY2;
			break;

		case "DD\/MON\/YYYY" :
			vData = vDD + "\/" + vMon + "\/" + vY4;
			break;
		case "DD\/MON\/YY" :
			vData = vDD + "\/" + vMon + "\/" + vY2;
			break;
		case "DD-MON-YYYY" :
			vData = vDD + "-" + vMon + "-" + vY4;
			break;
		case "DD-MON-YY" :
			vData = vDD + "-" + vMon + "-" + vY2;
			break;

		case "DD\/MONTH\/YYYY" :
			vData = vDD + "\/" + vFMon + "\/" + vY4;
			break;
		case "DD\/MONTH\/YY" :
			vData = vDD + "\/" + vFMon + "\/" + vY2;
			break;
		case "DD-MONTH-YYYY" :
			vData = vDD + "-" + vFMon + "-" + vY4;
			break;
		case "DD-MONTH-YY" :
			vData = vDD + "-" + vFMon + "-" + vY2;
			break;

		case "DD\/MM\/YYYY" :
			vData = vDD + "\/" + vMonth + "\/" + vY4;
			break;
		case "DD\/MM\/YY" :
			vData = vDD + "\/" + vMonth + "\/" + vY2;
			break;
		case "DD-MM-YYYY" :
			vData = vDD + "-" + vMonth + "-" + vY4;
			break;
		case "DD-MM-YY" :
			vData = vDD + "-" + vMonth + "-" + vY2;
			break;

		default :
			vData = vMonth + "\/" + vDD + "\/" + vY4;
	}

	return vData;
}

/**
 * Returns a table row of headers for days of the week => Sun - Sat
 *
 * @return string
 * @access public
 */
Calendar.prototype.cal_header = function() {
	var vCode = "";
		
	vCode = vCode + "<TR>";
	vCode = vCode + "<TD class=\"calendarDayHeader\">Sun</TD>";
	vCode = vCode + "<TD class=\"calendarDayHeader\">Mon</TD>";
	vCode = vCode + "<TD class=\"calendarDayHeader\">Tue</TD>";
	vCode = vCode + "<TD class=\"calendarDayHeader\">Wed</TD>";
	vCode = vCode + "<TD class=\"calendarDayHeader\">Thu</TD>";
	vCode = vCode + "<TD class=\"calendarDayHeader\">Fri</TD>";
	vCode = vCode + "<TD class=\"calendarDayHeader\">Sat</TD>";
	vCode = vCode + "</TR>";
	
	if (!borderSW) vCode = vCode + "<TR><TD CLASS=\"calendarHR\" COLSPAN=\"7\" CLASS=\"calendarSpacer\" height=\"1\"><img src=\"images/spacer.gif\" width=\"2\" height=\"1\" /></TD></TR>";
	    
	return vCode;
}

/**
 * Generates Calender inside a table
 * - switches for whether days are links
 *
 * @return string
 * @access public
 * @param Boolean attributes[0] switcher for making days links
 * @refer gMonthName
 * @refer gMonth
 * @refer gYear
 * @refer gFormat
 * @refer gReturnItem
 * @refer calendarWidth
 * @refer borderSW
 * @refer borderColor
 * @see getMonthlyCalendarCode()
 */
Calendar.prototype.show = function() {
	var vCode = ""; var output = "";

	// Show navigation buttons
	var prevMMYYYY = Calendar.calc_month_year(this.gMonth, this.gYear, -1);
	var prevMM = prevMMYYYY[0];
	var prevYYYY = prevMMYYYY[1];

	var nextMMYYYY = Calendar.calc_month_year(this.gMonth, this.gYear, 1);
	var nextMM = nextMMYYYY[0];
	var nextYYYY = nextMMYYYY[1];
	
	//if links
	var links = (arguments[0] == 1) ? 1 : 0;
	//border setting
	var border = (borderSW) ? 1 : 0;
	//border color setting
    var bgcolor = ( (border == 1) && (borderColor != "") ) ? " BORDERCOLOR='"+borderColor+"'" : "";

	output = "<TABLE BORDER="+border+bgcolor+" CLASS='calendarTable' CELLSPACING=0 CELLPADDING=0 WIDTH='"+calendarWidth+"'>";
	
	if ( (navigator.appName.search(/Netscape/i)>-1) && (navigator.appVersion.search(/^4\./)>-1)) { }
	else if (calendarNav) {
	    output += "<TR><TD ALIGN=\"center\" CLASS=\"calendarNavCell\" COLSPAN=7 NOWRAP>";
	
	    action = "changeMonth(";
		action += "'" + this.gReturnItem + "', '" + this.gMonth + "', '" + (parseInt(this.gYear)-1) + "', '" + this.gFormat + "', " + links;
		action += ")";
	    output += "&nbsp;<A HREF=\"javascript:"+action+"\" class=\"calendarNav\"";
	    output += "   onMouseOver=\"mouser('Previous Year');return true;\" onMouseOut=\"mouseout()\">";
	    output += "   &lt;&lt;-Year<\/A>&nbsp;";
	    
		action = "changeMonth(";
		action += "'" + this.gReturnItem + "', '" + prevMM + "', '" + prevYYYY + "', '" + this.gFormat + "', " + links;
		action += ")";
	    output += "&nbsp;<A HREF=\"javascript:"+action+"\" class=\"calendarNav\"";
	    output += "   onMouseOver=\"mouser('Previous Month');return true;\" onMouseOut=\"mouseout()\">";
	    output += "   &lt;-Month<\/A>&nbsp;";
	    
		action = "changeMonth(";
		action += "'" + this.gReturnItem + "', 0, 0, '" + this.gFormat + "', " + links;
		action += ")";
    	output += "&nbsp;<A HREF=\"javascript:"+action+"\" class=\"calendarNav\"";
	    output += "   onMouseOver=\"mouser('Today');return true;\" onMouseOut=\"mouseout()\">";
	    output += "   Today<\/A>&nbsp;";
	    
		action = "changeMonth(";
		action += "'" + this.gReturnItem + "', '" + nextMM + "', '" + nextYYYY + "', '" + this.gFormat + "', " + links;
		action += ")";
	    output += "&nbsp;<A HREF=\"javascript:"+action+"\" class=\"calendarNav\"";
	    output += "   onMouseOver=\"mouser('Next Month');return true;\" onMouseOut=\"mouseout()\">";
        output += "   Month-&gt;<\/A>&nbsp;";
		
		action = "changeMonth(";
		action += "'" + this.gReturnItem + "', '" + this.gMonth + "', '" + (parseInt(this.gYear)+1) + "', '" + this.gFormat + "', " + links;
		action += ")";
	    output += "&nbsp;<A HREF=\"javascript:"+action+"\" class=\"calendarNav\"";
	    output += "   onMouseOver=\"mouser('Next Year');return true;\" onMouseOut=\"mouseout()\">";
	    output += "   Year-&gt;&gt;<\/A>&nbsp;";
	
	    output += "</TD></TR>";
	
	}
	
	//month name and year
	output += "<TR><TD class=\"calendarMonthName\" COLSPAN=7>"+this.gMonthName + " " + this.gYear+"</TD></TR>";

	// Get the complete calendar code for the month..
	output += this.getMonthlyCalendarCode(links);
	output += "</TABLE>";
	return output;
}

/**
 * Formats date according to format
 *
 * @return string
 * @access public
 * @refer gMonth
 * @refer gYear
 * @refer gFormat
 */
Calendar.prototype.cal_data = function() {
	var vDate = new Date();
	vDate.setDate(1);
	vDate.setMonth(this.gMonth);
	vDate.setFullYear(this.gYear);

	var vFirstDay=vDate.getDay();
	var vDay=1;
	var vLastDay=Calendar.get_daysofmonth(this.gMonth, this.gYear);
	var vOnLastDay=0;
	var vCode = "";

	//if links
	var links = (arguments[0] == 1) ? true : false;

	/*
	Get day for the 1st of the requested month/year..
	Place as many blank cells before the 1st day of the month as necessary. 
	*/

	vCode += "<TR>";
	for (i=0; i<vFirstDay; i++) {
		vCode += "<TD CLASS=\"calendarEmptyCell\" WIDTH='14%'" + this.write_cell_style(-1,i) + ">&nbsp;</TD>";
	}

	// Write rest of the 1st week
	for (j=vFirstDay; j<7; j++) {
		vCode += "<TD ALIGN=\"center\" WIDTH='14%'" + this.write_cell_style(vDay,j) + ">";
		if (links) {
		   if ( this.gReturnItem.search(/\(DATE/)!=-1 )
			   action = this.gReturnItem.replace(/\(DATE/, "('" + this.format_data(vDay) + "'" );
		   else if ( (this.gReturnItem.search(/\./)!=-1) )
			   action = this.gReturnItem+".value='"+this.format_data(vDay)+"'";
		   else action = this.gReturnItem;
				
		   vCode += "<A HREF=\"javascript:"+action+"\" onMouseOver=\"mouser('"+this.format_data(vDay)+"');return true\" ";
	       vCode += "   class=\"calendarDayLinks\" onMouseOut=\"mouseout();\">"+this.format_day(vDay)+"</A>";
		} else vCode += this.format_day(vDay);
		vCode += "</TD>";
		vDay=vDay + 1;
	}
	vCode += "</TR>";

	// Write the rest of the weeks
	for (k=2; k<7; k++) {
		vCode += "<TR>";

		for (j=0; j<7; j++) {
			vCode += "<TD ALIGN=\"center\" WIDTH='14%'" + this.write_cell_style(vDay,j) + ">";
			if (links) {				
		       if ( this.gReturnItem.search(/\(DATE/)!=-1 )
			     action = this.gReturnItem.replace(/\(DATE/, "('" + this.format_data(vDay) + "'" );
			   else if ( (this.gReturnItem.search(/\./)!=-1) )
			     action = this.gReturnItem+".value='"+this.format_data(vDay)+"'";
			   else action = this.gReturnItem;
				
			   vCode += "<A HREF=\"javascript:"+action+"\" onMouseOver=\"mouser('"+this.format_data(vDay)+"');return true\" ";
	           vCode += "   class=\"calendarDayLinks\" onMouseOut=\"mouseout();\">"+this.format_day(vDay)+"</A>";
		    } else vCode += this.format_day(vDay);
		    vCode += "</TD>";
			vDay=vDay + 1;

			if (vDay > vLastDay) {
				vOnLastDay = 1;
				break;
			}
		}

		if (j == 6)
			vCode += "</TR>";
		if (vOnLastDay == 1)
			break;
	}
	
	// Fill up the rest of last week with proper blanks, so that we get proper square blocks
	for (m=1; m<(7-j); m++) {
		vCode += "<TD CLASS=\"calendarEmptyCell\" WIDTH='14%'" + this.write_cell_style(0,(m+j)) + ">&nbsp;</TD>";
	}
	
	return vCode;
}
