/*
Copyright 2004, Paul Tero, Version 1.0.0
This is a popup calendar written entirely in Javascript. It puts the date into a text field
which can be hidden. The colours and fonts are controlled by the styles below. It is implemented
as a calendar object which has several methods, and there a few additional methods for
the date object as well.

The calendar can be invoked with a line like this, where "test" is the name of the calendar. You
can also pass it a default date, a function to be called when a date is clicked, and whether to
hide the weekends.
<a href="javascript:void new Calendar('test','20 December 2004');">show calendar</a>

Along with that, you'll need a div to show the calendar in. This must have the corresponding
name (such as "calendar_test").
<div id="calendar_test" class="calendar"></div>

The call back function is passed the calendar name and the chosen date as an object. If there
is no call back function it refreshes the page, passing in the calendar name, time (as a number)
and GMT date. An example call back function is:
function process(name,date) {alert (name + "=>" + date.toGMTString());}

The following styles are available to customise the calendar. It is displayed as a table with
five or seven (depending on if weekends are shown) equally wide columns:
div.calendar {visibility: hidden; position: absolute;}
div.calendar a {text-decoration: none; color: black;}
div.calendar table {background: yellow; width:300px;}
div.calendar th {text-align: center; border: 1px solid red; background: white;}
div.calendar th.calendarmonth {}
div.calendar th.calendararrows { color: white;}
div.calendar th.calendardayofweek {background: red;}
div.calendar td {text-align: center; border: 1px solid red;}
div.calendar td.calendarinitial {background: green;}
div.calendar td.calendarinitial a {color: white;}
div.calendar td.calendartoday {background: blue;}
div.calendar td.calendartoday a {color: white;}
div.calendar td.calendarblank {background: white;}
div.calendar td.calendarnormal {}
div.calendar td.calendarclose {}
*/


//Show the properties of an object
function showProperties (o) {var r = ""; for (var p in o) {r += p + ": " + o[p] + "\t";} alert (r);}

//This uses a calendar object.
function Calendar (name, initialdate, callback, noweekends) {
	this.name = name;
	this.fullname = "calendar_" + name;
	this.displaydate = new Date(); //the date to display
	if (document[this.fullname]) { //the calendar has already been used before, get the old data
		this.displaydate = document[this.fullname].displaydate;
		this.initialdate = document[this.fullname].initialdate;
	}
	else {
		if (initialdate) {this.displaydate.setTime (Date.parse (initialdate));} //set to the initialisation date
		this.initialdate = new Date (this.displaydate.getTime()); //store the initial date
	}
	this.callback = callback; //the callback function which is passed the date
	this.weekends = noweekends ? false : true; //display weekends or not
	this.format(); //format this calendar
	document[this.fullname] = this; //save a copy of this object (which we will reference in links)
}

//Change the text of the calendar and display or hide it
function Calendar_change (text, visible) {
	if (!document.getElementById) return; //IE and Firefox only for now
	var el = document.getElementById(this.fullname); //get the element
	if (text) el.innerHTML = text; //set the text
	el.style.visibility = visible ? "visible": "hidden"; //show or hide it
}

//Get a link which calls a function in the calendar
function Calendar_getLink (callback, text) { 
	var r = "<a href=\"javascript:void document." + this.fullname + "."; //start the link
	r += callback + "\">" + text + "</a>"; //add the call back and text
	return r; //return the link
}

//Add a month to the calendar display date
function Calendar_addMonths (howmany) {this.displaydate.addMonths(howmany); this.format(true);}

//Return the name of the calendar and the date to the requested function. The defautl just calls
//the same page passing in the name, display time and display date.
function Calendar_returnDate (day) {
	this.displaydate.setDate(day);
	if (this.callback) eval ([this.callback] + "(this.name, this.displaydate)"); //call the call back function
	else document.location.href = document.location.pathname + "?calendarname=" + this.name + 
		"&calendartime=" + Math.floor(this.displaydate.getTime()/1000) + //because it's in milliseconds
		"&calendardate=" + this.displaydate.toGMTString();
}

//Output the table
function Calendar_format() {
	var days = new Array ("So", "Mo", "Tu", "We", "Th", "Fr", "Sa");
	var firstcol = this.weekends ? 0 : 1; //start from day 0 if we have weekends
	var lastcol = this.weekends ? 6 : 5; //end at day 6 if we have weekends
	//////////////// display the header rows ////////////////
	var r = "<table cellspacing=\"0\">\n"; //start the table
	r += "<tr><th class=\"calendararrows\">" + this.getLink ("addMonths(-1)", "&lt;") + "</th>\n"; //back a month
	r += "<th class=\"calendarmonth\" colspan=\"" + (this.weekends ? 5 : 3) + "\">"; //colspan of the title
	r += this.displaydate.getMonthName() + " " + this.displaydate.getActualYear(); //the month and year
	r += "</th>\n<th class=\"calendararrows\">" + this.getLink ("addMonths(1)", "&gt;") + "</th>\n</tr>\n"; //forward a month
	var cellwidth = Math.floor (100/ (this.weekends ? 7 : 5)); //width of each weekday column
	for (var day=firstcol; day<=lastcol; day++) {r += "<col width=\"" + cellwidth + "%\" />\n";} //width of cells
	r += "<tr>"; //start the days of the week
	for (var day=firstcol; day<=lastcol; day++) {r += "<th class=\"calendardayofweek\">" + days[day] + "</th>\n";}
	r += "</tr>\n"; //end of days of week
	//////////////// get the start and end day for this month ////////////////
	var td = new Date (this.displaydate.getTime()); //for this we will need to use a temporary date
	var initialnumber = (td.getMonth() == this.initialdate.getMonth()) && (td.getYear() == this.initialdate.getYear()) ?
		this.initialdate.getDate() : 0; //set to the initial date's number or 0
	td.setDate(1); //go to the first of the month
	var startday = td.getDay(); //get the day of the week
	td.addMonths(1); //add a month to the temporary date
	var endday = (td.getDay() + 6) % 7; //end on this day of the week (and subtract one)
	var td = new Date(); //reset to now
	var todaynumber = (td.getMonth() == this.displaydate.getMonth()) && (td.getYear() == this.displaydate.getYear()) ?
		td.getDate() : 0; //set to today's date's number or 0
	//////////////// figuring out starting week and number of weeks ////////////////
	//var numweeks = (startday==0 && endday==6) ? 5 : 6; //the number of weeks to display (4 in February)
	var startweek = 1; //which week to start with
	if (!this.weekends && startday==6) startweek=2; //start on week 2 if the first day is a Sat or Sun
	var numweeks = 6; //maximum number of weeks needed to display this month
	if (endday > startday-2) numweeks = 5; //can be displayed in 5 weeks
	if (startday == 0 && endday == 6) numweeks = 4; //a February starting on a Sunday
	var endweek = numweeks; //the last week do display (so that the last week is shown if the next statement is true)
	if (!this.weekends && (endday%6 < 1)) numweeks--; //end a week early if the last day is a Sat or Sun
	//////////////// display the numbers ////////////////
	for (var week=startweek; week<=numweeks; week++) { //for each week (4 only in February)
		r += "<tr>";
		for (var day=firstcol; day<=lastcol; day++) { //for each day of the week
			var daynumber = (week-1)*7 + day - startday + 1; //the day number
			var dayclass="normal"; var daylink=true; //the dayclass and daylink for this day
			if (week==1 && day<startday) {dayclass="blank"; daylink=false;} //before the first day
			else if (week==endweek && day>endday) {dayclass="blank"; daylink=false;} //after the last day
			else if (daynumber==todaynumber) {dayclass="today"; daylink=true;} //todays' date
			else if (daynumber==initialnumber) {dayclass="initial"; daylink=true;} //initial date
			r += "<td class=\"calendar" + dayclass + "\">";
			r += daylink ?  this.getLink ("returnDate(" + daynumber + ")", daynumber) : "&nbsp;";
			r += "</td>\n";
		}
		r += "</tr>\n";
	}
	//////////////// finish the table ////////////////
	r += "<tr><th class=\"calendarclose\" colspan=\"" + (this.weekends ? 7 : 5) + "\">";
	r += this.getLink ("change('',false)", "close") + "</th></tr>\n"; //end the days of the week
	r += "</table>"; //end the table
	this.change (r, true);
}

//Get the month name from a date object
function Date_getMonthName() {
	var months = new Array ("January", "February", "March", "April", "May", "June",
				"July", "August", "September", "October", "November", "December");
	return months[this.getMonth()];
}
//Get the actual year from a date object
function Date_getActualYear() {var year = this.getYear(); return year < 500 ? year + 1900 : year;}	

//Change the month of a date by adding or subtracting months
function Date_addMonths (howmany) {
	var month = this.getMonth() + howmany;
	this.setYear (this.getActualYear() + Math.floor (month / 12)); //change the year
	this.setMonth ((month+12)%12); //change the month
}

Calendar.prototype.returnDate = Calendar_returnDate;
Calendar.prototype.change = Calendar_change;
Calendar.prototype.getLink = Calendar_getLink;
Calendar.prototype.format = Calendar_format;
Calendar.prototype.addMonths = Calendar_addMonths;
Date.prototype.getMonthName = Date_getMonthName;
Date.prototype.getActualYear = Date_getActualYear;
Date.prototype.addMonths = Date_addMonths;

