// Calendar control class (requires basic.js)
function Calendar(calendarDivElement, calendarOutputDivElement) {
// Fixed properties
	this.reLeftTagBracketSubstitutes = /\[\[/g;
	this.reRightTagBracketSubstitutes = /\]\]/g;
	this.reNewLine = /\n/g;
	this.reHTMLPara = /<p>/i;
	this.aMonths = ["January","February","March","April","May","June","July","August","September","October","November","December"];
	this.aDays = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
	this.aDayInitials = ["S","M","T","W","T","F","S"];

// Initialised properties
	this.oCalendarDiv = document.getElementById(calendarDivElement);
	this.oCalendarOutputDiv = document.getElementById(calendarOutputDivElement);
	this.dCurrentDate = new Date();
	this.dCurrentDate.setHours(0);
	this.dCurrentDate.setMinutes(0);
	this.dCurrentDate.setSeconds(0);
	this.dCurrentDate.setMilliseconds(0);
	this.iCurrentDate = Date.parse(this.dCurrentDate);
	this.dStartDate = new Date(this.dCurrentDate);
	this.dStartDate = new Date(this.dStartDate.setDate(1));
	this.iSelectedMonth = this.dStartDate.getMonth();
	this.aBodyCells = new Array();
	
	this.timeRemaining = 0;
	
	var me = this;
	this.oCalendarDiv.onmouseout = function(oEvent) { 
		if (!oEvent) var oEvent = window.event;
		me.hideDetails(oEvent); 
	}
	this.oCalendarOutputDiv.onmouseout = function(oEvent) { 
		if (!oEvent) var oEvent = window.event;
		me.hideDetails(oEvent); 
	}
	
	this.oCalendarDiv.onmouseover = function(oEvent) { 
		if (me.timeRemaining > 0) {
			me.timeRemaining = 10;
		}
	}

	this.oCalendarOutputDiv.onmouseover = function(oEvent) { 
		if (me.timeRemaining > 0) {
			me.timeRemaining = 10;
		}
	}

// Undinitialised properties
	Calendar.prototype.oTable;
	Calendar.prototype.aCalendarRecords;

// Draw the calendar	
	this.redraw();
}

// Methods

// Count down to hide pop up div
Calendar.prototype.countDown = function() {
	this.timeRemaining -= 1;
	if (this.timeRemaining < 1) {
    	this.oCalendarOutputDiv.style.display = "none";
	} else {
		var thisObject = this;
		setTimeout(function() { thisObject.countDown() }, 1000);
	}
}

// Move to previous month
Calendar.prototype.previousMonth = function() {
	this.dStartDate.setMonth(this.dStartDate.getMonth() - 1);
	this.redraw();
}

// Move to next month
Calendar.prototype.nextMonth = function() {
	this.dStartDate.setMonth(this.dStartDate.getMonth() + 1);
	this.redraw();
}

// Show the event details in pop up div
Calendar.prototype.showDetails = function(oTableCell) {
	var iTableCellValue = parseInt(oTableCell.firstChild.nodeValue);
	var dTableCellDate = new Date(this.dStartDate);
	dTableCellDate = new Date(dTableCellDate.setDate(iTableCellValue));
	var sRecordHeader = this.aDays[dTableCellDate.getDay()] + " ";
	sRecordHeader += dTableCellDate.getDate() + " ";
	sRecordHeader += this.aMonths[dTableCellDate.getMonth()] + " ";
	sRecordHeader += dTableCellDate.getFullYear();
	
	var sDateRecord = this.dateRecord(dTableCellDate);
	var oHeader = document.createElement("h3");
	oHeader.appendChild(document.createTextNode(sRecordHeader))
	var oRecord = document.createElement("div");
	oRecord.innerHTML = sDateRecord;
// Empty the div	
	while (this.oCalendarOutputDiv.hasChildNodes()) {
		this.oCalendarOutputDiv.removeChild(this.oCalendarOutputDiv.lastChild);
	}
// Refill the div	
	this.oCalendarOutputDiv.appendChild(oHeader);
	this.oCalendarOutputDiv.appendChild(oRecord);
// Show the div	
	this.oCalendarOutputDiv.style.display = "block";
	if (this.timeRemaining > 0) {
		this.timeRemaining = 10;
	} else {
		this.timeRemaining = 10;
		this.countDown();
	}
}

// Hide the pop up div
Calendar.prototype.hideDetails = function(e) {
	if (!e) var e = window.event;
	var tg = (window.event) ? e.srcElement : e.target;
	if (tg.nodeName != 'DIV') return;
	if (!tg.attributes['id']) return;
	var tagId = tg.attributes['id'].value;
	var outputDivId = this.oCalendarOutputDiv.attributes['id'].value;
	var calendarDivId = this.oCalendarDiv.attributes['id'].value;
	if (tagId != outputDivId && tagId != calendarDivId) return;
	var reltg = (e.relatedTarget) ? e.relatedTarget : e.toElement;
	while (reltg != tg && reltg.nodeName != 'BODY')
		reltg = reltg.parentNode
	if (reltg == tg) return;
	
    this.oCalendarOutputDiv.style.display = "none";
	this.timeRemaining -= 1;
}

// Check if there is a calendar record for a date
Calendar.prototype.dateHasRecord = function(dDateToCheck) {
	var iDateToCheck = Date.parse(dDateToCheck);
	var bHasRecord = false;
	for (i=0; i<this.aCalendarRecords.length; i++) {
		if (iDateToCheck == this.aCalendarRecords[i][0]) {
			bHasRecord = true;
			break;
		}
	}
	return bHasRecord;
}

// Get the calendar record for a date
Calendar.prototype.dateRecord = function(dDateToCheck) {
	var iDateToCheck = Date.parse(dDateToCheck);
	var sDetails = "";
	for (i=0; i<this.aCalendarRecords.length; i++) {
		if (iDateToCheck == this.aCalendarRecords[i][0]) {
			sDetails = this.aCalendarRecords[i][1];
			break;
		}
	}
	return sDetails;
}

// Load the calendar records for the current month
Calendar.prototype.loadRecords = function (startDate) {
	try {
		var oRequest = getXMLHttpRequest(); // getXMLHttpRequest is in basic.js
	} catch (oError) {
		throw new Error("Can't get XMLHttpRequest");
	}
	try {
		oRequest.open('GET', "/xml/xml_calendar.php?sd=" + startDate, false);
		oRequest.send(null);
	} catch (oError) {
		throw new Error("Can't get XML data");
	}
	if (oRequest.status == 200) {
		if (oRequest.responseXML.xml == "") {
			throw new Error("No XML data");
		} else {
			 var xmlDoc = oRequest.responseXML;
			 var oCalendarRecords = xmlDoc.getElementsByTagName("Record");
			 return oCalendarRecords;
		}
	} else {
		throw new Error("There was a problem with the request.");
	}
}

Calendar.prototype.redraw = function() {
// Get the calendar records for the current month
	var oCalendarRecords = this.loadRecords(this.dStartDate.getFullYear() + "-" + (this.dStartDate.getMonth() + 1) + "-" + (this.dStartDate.getDate()));
// Transfer the records into the aCalendarRecords array
	var oCalendarDateNode, iCalendarDate, oCalendarDetailsNode, sCalendarDetails, aCalendarRecord;
	this.aCalendarRecords = new Array();
	for (var i=0; i<oCalendarRecords.length; i++) {
		oCalendarDateNode = oCalendarRecords[i].childNodes[0];
		iCalendarDate = Date.parse(oCalendarDateNode.firstChild.nodeValue);
		oCalendarDetailsNode = oCalendarRecords[i].childNodes[1];
		sCalendarDetails = oCalendarDetailsNode.firstChild.nodeValue;
		sCalendarDetails = sCalendarDetails.replace(this.reLeftTagBracketSubstitutes, "<").replace(this.reRightTagBracketSubstitutes, ">");
		if (!sCalendarDetails.match(this.reHTMLPara)) {
			sCalendarDetails = sCalendarDetails.replace(this.reNewLine, "<br />");
		}
		aCalendarRecord = [iCalendarDate, sCalendarDetails];
		this.aCalendarRecords[i] = aCalendarRecord;
	}
	
	var iStartDayOfWeek = this.dStartDate.getDay();
	this.iSelectedMonth = this.dStartDate.getMonth();
	
// Create a pointer to the calendar object to use in event handlers	
	var me = this; 
	
// Build the calendar table
	this.oTable = document.createElement("table");
	this.oTable.className = "calendar";
	
// Create the table header	
	var oTHead = document.createElement("thead");
	
// Add the month row
// Add the 'previous month' link
	var oTR = document.createElement("tr");
	var oTH = document.createElement("th");
	var oTHT = document.createTextNode("<<");
	oTH.appendChild(oTHT);
	oTH.onmouseover = function() { document.body.style.cursor="pointer"; }
	oTH.onmouseout = function() { document.body.style.cursor="auto"; }
	oTH.onclick = function() { me.previousMonth(); }
	oTR.appendChild(oTH);
	
// Add the month label
	var oTH = document.createElement("th");
	var oTHT = document.createTextNode(this.aMonths[this.iSelectedMonth] + " " + this.dStartDate.getFullYear());
	oTH.appendChild(oTHT);
	oTH.colSpan = 5;
	oTR.appendChild(oTH);
	
// Add the 'next month' link
	var oTH = document.createElement("th");
	var oTHT = document.createTextNode(">>");
	oTH.appendChild(oTHT);
	oTH.onmouseover = function() { document.body.style.cursor="pointer"; }
	oTH.onmouseout = function() { document.body.style.cursor="auto"; }
	oTH.onclick = function() { me.nextMonth(); }
	oTR.appendChild(oTH);
	oTHead.appendChild(oTR);
	
// Add the days row
	var oTR = document.createElement("tr");
	for (var i=0; i<7; i++) {
		var oTH = document.createElement("th");
		oTH.className = "calendar_day_header";
		var oTHT = document.createTextNode(this.aDayInitials[i]);
		oTH.appendChild(oTHT);
		oTR.appendChild(oTH);
	}
	oTHead.appendChild(oTR);
	
	this.oTable.appendChild(oTHead);
// End of table header

// Add the table body
	var oTBody = document.createElement("tbody");
	var iCell = 0, iDay = 1;
	var dNextDate = new Date(this.dStartDate);
	for (var i=0; i<6; i++) { // For each row in the table
		var oTR = document.createElement("tr");
		for (var j=0; j<7; j++) { // For each cell in the row
			var iNextDate = Date.parse(dNextDate);
			this.aBodyCells[i,j] = document.createElement("td");
			this.aBodyCells[i,j].className = "calendar_day";
			if ((iCell < iStartDayOfWeek) || (dNextDate.getMonth() != this.iSelectedMonth)) { 
// Before and after days of month, leave the cell empty
				var oTDT = document.createTextNode("");
				this.aBodyCells[i,j].appendChild(oTDT);
			} else {
// During the days of month, insert the date
				var oTDT = document.createTextNode(iDay);
				this.aBodyCells[i,j].appendChild(oTDT);
// If the date is today, set its class to today
				if (iNextDate == this.iCurrentDate) { 
					this.aBodyCells[i,j].className = this.aBodyCells[i,j].className + " today";
				}
// If the date has a record, set its class and event handlers
				if (this.dateHasRecord(dNextDate)) { 
					this.aBodyCells[i,j].className = this.aBodyCells[i,j].className + " record";
					this.aBodyCells[i,j].onmouseover = function() { 
						document.body.style.cursor="pointer";
					}
					this.aBodyCells[i,j].onclick = function() { 
						me.showDetails(this);
					}
					this.aBodyCells[i,j].onmouseout = function() { 
						document.body.style.cursor="auto"; 
					}
				}
				iDay++; // Increment the day of the month
				dNextDate.setDate(dNextDate.getDate() + 1); // Increment the date
			}
			oTR.appendChild(this.aBodyCells[i,j]); // Add the cell to the row
			iCell++; // Increment the cell number
		}
		oTBody.appendChild(oTR); // Add the row to the table body
	}
	this.oTable.appendChild(oTBody); // Add the table body to the table
	
	// Empty the calendar div
	while (this.oCalendarDiv.hasChildNodes()) {
		this.oCalendarDiv.removeChild(this.oCalendarDiv.lastChild);
	}
	// Add the table to the calendar div
	this.oCalendarDiv.appendChild(this.oTable);
}


