function TLCalendar(input) {
	// An options property may be written into this class elsewhere to allow the 
	// calendar widget to be configured by PHP, give it some sensible defaults.
	this.options = Object.extend({
		'maxDateRange': 28
	}, this.options);

	this.dayLabels 	 = ['Mo','Tu','We','Th','Fr','Sa','Su'];
	this.monthLabels = ['January','February','March','April','May','June','July','August','September','October','November','December'];
	this.daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

	this.input = input;
	this.inputDate = this.getInputDate(this.input);
	this.selectedDate = this.inputDate;

	this.today = new Date();
	this.today.setHours(0,0,0,0);

	// max & min date (JonC)
	this.maxDate = new Date();
	this.maxDate.setFullYear(this.maxDate.getFullYear()+1);
	if (this.input.id == 'checkOutDate') {
		if ($F($('checkInDate'))) {
			// Max number of days between check-in/out defaults to 28, but may be reduced to 7 when in BBC mode.
			this.maxDate = this.getInputDate($('checkInDate'));
			this.maxDate.setDate(this.maxDate.getDate() + this.options.maxDateRange);
		}
	}

	this.minDate = new Date();

	if (this.input.id == 'corp_search_arrive_from' || this.input.id == 'corp_search_arrive_to' || this.input.id == 'corp_search_booking_from' || this.input.id == 'corp_search_booking_to') {

		this.minDate.setFullYear(this.minDate.getFullYear()-2);

	} else {

		this.minDate = this.today;

		if (this.selectedDate < this.today) {
			this.selectedDate = this.today;
		}
	}

	if (this.input.id == 'checkOutDate') {
		if ($F($('checkInDate'))) {
			this.minDate = this.getInputDate($('checkInDate'));
			this.minDate.setDate(this.minDate.getDate()+1);
		}
	}

	// if (this.selectedDate < this.today) {
		// this.selectedDate = this.today;
	// }

	this.d = this.selectedDate.getDay();
	this.m = this.selectedDate.getMonth();
	this.y = this.selectedDate.getFullYear();

	this.container = new Element('div');
	$(document.body).insert({bottom: this.container});
	this.container.setStyle({position:'absolute',zIndex:'102'});
	this.container.clonePosition(this.input, {setWidth:false, setHeight:false, offsetTop:-20, offsetLeft:this.input.getWidth()+5 });

	this.html = '';
	this.refresh();
	this.generateIFrame(this.container);
}

TLCalendar.prototype.hide = function(e){
	this.container.remove();
	if (this.shim) this.shim.remove();
	document.stopObserving('click');
}

TLCalendar.prototype.prevMonth = function(){
	if (this.m == 0) {
		this.y--; this.m = 11;
	} else {
		this.m--;
	}
	this.refresh();
}

TLCalendar.prototype.nextMonth = function(){
	if (this.m == 11) {
		this.y++; this.m = 0;
	} else {
		this.m++;
	}
	this.refresh();
}

TLCalendar.prototype.generateHTML = function(){

	// get first day of month
	var startDay = new Date(this.y, this.m, 1).getDay();
	if (startDay == 0) startDay = 7;

	// find number of days in month
	var monthLength = this.daysInMonth[this.m];
	// compensate for leap year
	if (this.m == 1) {
		if ((this.y%4 == 0 && this.y%100) || this.y%400 == 0) {
			monthLength = 29;
		}
	}

	var monthHtml = this.monthLabels[this.m] + ' ' + this.y;
	var tableHtml = '<th>' + this.dayLabels.join('</th><th>') + '</th>';

	var day = 1;
	for (var i=0; i<=5; i++) { // weeks or rows
		var rowHtml = '';
		for (var j=1; j<=7; j++) { // days or cells
			if (day <= monthLength && (i>0 || j>=startDay)) {
				var dayDate = new Date(this.y, this.m, day);
				if ((dayDate >= this.minDate) && (dayDate <= this.maxDate)) {
					if (dayDate.toDateString() == this.inputDate.toDateString()) {
						rowHtml += '<td class="active"><span>' + (day++) + '</span></td>';
					} else {
						rowHtml += '<td>' + (day++) + '</td>';
					}
				} else {
					rowHtml += '<td class="past">' + (day++) + '</td>';
				}
			} else {
				rowHtml += '<td class="inactive"></td>';
			}
		}
		tableHtml += '<tr>'+rowHtml+'</tr>';
		// stop making rows if we've run out of days
		if (day > monthLength) break;
	}

	var h = ''
	+ '<div class="basicBox gradBox singleCol flush" id="calendarOverlay">'
		+ '<div class="bTop"><div class="bTopL"></div><div class="bTopR"></div></div>'
		+ '<div class="bContent"><div class="bPadding">'
			+ '<div class="mb">'
				+ '<div class="fl al"><a href="#" class="prev"><img src="/assets/img/buttons/left_arrow_small.gif" alt="previous month" /></a></div>'
				+ '<div class="fr ar"><a href="#" class="next"><img src="/assets/img/buttons/right_arrow_small.gif" alt="next month" /></a></div>'
				+ '<div class="ac"><strong>'+monthHtml+'</strong></div>'
				+ '<div class="clear"></div>'
			+ '</div>'
			+ '<table class="calendar">'+tableHtml+'</table>'
		+ '</div></div>'
		+ '<div class="bBtm"><div class="bBtmL"></div><div class="bBtmR"></div></div>'
	+ '</div>';

	this.html = h;
}

TLCalendar.prototype.generateIFrame = function(el){
	// Fixes select box overlay problem in IE6
	$(el).insert({after:'<iframe id="calendarOverlayShim" src="/javascript/blank.html" scroll="no" frameborder="no"></iframe>'});
	this.shim = $('calendarOverlayShim');
	if (this.shim) {
		this.shim.clonePosition(el);
		this.shim.setStyle({ zIndex: $(el).getStyle('zIndex') });
		$(el).setStyle({ zIndex: $(el).getStyle('zIndex') + 1 });
	}
}

TLCalendar.prototype.getDateString = function(d,m,y) {
	m += 1;
	if (d < 10) d = '0'+d;
	if (m < 10) m = '0'+m;
	return d+'/'+m+'/'+y;
}

TLCalendar.prototype.setInputDate = function(day) {
	this.d = day;
	this.input.value = this.getDateString(this.d, this.m, this.y);
	this.input.focus();
}

TLCalendar.prototype.getInputDate = function(input) {
	var bits = input.value.split('/');
	var d = parseInt(bits[0], 10);
	var m = parseInt(bits[1], 10) - 1;
	var y = parseInt(bits[2], 10);
	var date = new Date(y,m,d);

	if (isNaN(date) || date == 'Invalid Date') {
		date = new Date();
	}

	return date;
}

TLCalendar.prototype.getHTML = function() {
	this.generateHTML();
	return this.html;
}

TLCalendar.prototype.refresh = function() {
	var self = this;

	this.container.update(this.getHTML());
	this.container.down('a.next').observe('click',function(e){ self.nextMonth(); e.stop(); });
	this.container.down('a.prev').observe('click',function(e){ self.prevMonth(); e.stop(); });
	this.container.select('tr td').each(function(cell){
		if (cell.hasClassName('inactive') || cell.hasClassName('past')) return;

		cell.setStyle({cursor:'pointer'});
		cell.observe('click',function(e){
			var day = parseInt(e.element().innerHTML,10);
			self.setInputDate(day);

			if ((self.input.id == 'checkInDate') && $('checkOutDate')) {
				var checkOutDate = new Date(self.y,self.m,(day+1));
				$('checkOutDate').value = self.getDateString(checkOutDate.getDate(), checkOutDate.getMonth(), checkOutDate.getFullYear());
			}
			//This is a quick hack for the group booking form, should be done better - JonC
			if ((self.input.id == 'checkInDate2') && $('checkOutDate2')) {
				var checkOutDate = new Date(self.y,self.m,(day+1));
				$('checkOutDate2').value = self.getDateString(checkOutDate.getDate(), checkOutDate.getMonth(), checkOutDate.getFullYear());
			}

			self.hide();
			e.stop();
		});

	});

	// Prevent clicks inside the calendar container from triggering
	// the cal.hide() handler attached to the document as a whole
	this.container.observe('click',function(e){ e.stop(); });
}
