DabbleCalendar = {
	ONEDAY: 86400000, // shorthand for 1 day in milliseconds
	months: ["January","February","March","April","May","June","July","August","September","October","November","December"],
	shortMonths: ["Jan","Feb","Mar","Apr","May","June","July","Aug","Sept","Oct","Nov","Dec"],
	days: ["Su","M","Tu","W","Th","F","Sa"],
	rangeText: "Select a date range",
	helpText: {
		previousMonth: "Previous month",
		thisMonth: "This month",
		nextMonth: "Next month",
		time: "Time:",
		startTime: "Start time:",
		endTime: "End time:",
		help: 'Click above or just type e.g.&nbsp;&ldquo;Today&rdquo; or &ldquo;Now&rdquo;<br /><a href="http://dabbledb.com/help/index/date_and_time_values" target="help">More info</a>'
	},
	selectRangeByDefault: false,
	selectRangeIsOptional: true,
	allowTime: true,
	showHelp: true,
	locale: "american",
	firstDay: 0, // First day of the week; 0 = Sunday
	localeConversions: {
		zeroPad: function(integer) { return (integer < 10) ? "0" + integer : integer; },
		"yyyyMmDd": function(date) { return date.getFullYear() + "-" + this.zeroPad(date.getMonth() + 1) + "-" + this.zeroPad(date.getDate()); },
		"mmDdYy": function(date) { return this.zeroPad(date.getMonth() + 1) + "/" + this.zeroPad(date.getDate()) + "/" + date.getFullYear(); },
		"ddMmYy": function(date) { return this.zeroPad(date.getDate()) + "/" + this.zeroPad(date.getMonth() + 1) + "/" + date.getFullYear(); },
		"mmDdYyyy": function(date) { return this.zeroPad(date.getMonth() + 1) + "/" + this.zeroPad(date.getDate()) + "/" + date.getFullYear(); },
		"ddMmYyyy": function(date) { return this.zeroPad(date.getDate()) + "/" + this.zeroPad(date.getMonth() + 1) + "/" + date.getFullYear(); },
		"british": function(date) { return date.getDate() + " " + DabbleCalendar.months[date.getMonth()] + " " + date.getFullYear(); },
		"britishShort": function(date) { return date.getDate() + " " + DabbleCalendar.shortMonths[date.getMonth()] + " " + date.getFullYear(); },
		"americanShort": function(date) { return DabbleCalendar.shortMonths[date.getMonth()] + " " + date.getDate() + " " + date.getFullYear(); },
		"american": function(date) { return DabbleCalendar.months[date.getMonth()] + " " + date.getDate() + ", " + date.getFullYear(); }
	},
	localeString: function(date) {
		return this.localeConversions[this.locale](date);
	},
	buildMonth: function(monthNumber, yearNumber) {
		var printDate = new Date();
		printDate.setDate(1);
		printDate.setMonth(monthNumber);
		printDate.setFullYear(yearNumber);
		printDate.setHours(12,0,0);

// calendar logic for beginning and end of the year
		if (monthNumber == 0) {
			this.previousMonth = 11;
			this.previousYear = yearNumber - 1;
		} else {
			this.previousMonth = monthNumber - 1;
			this.previousYear = yearNumber;		
		}
		if (monthNumber < 11) {
			this.nextMonth = monthNumber + 1;
			this.nextYear = yearNumber;
		} else {
			this.nextMonth = 0;
			this.nextYear = yearNumber + 1;
		}

// build the table
		var table = $E("table.datePickerTable.noClickThrough");
		var thead = $E("thead");
		var navigationRow = $E("tr");
		var titleRow = $E("tr");
		var weekdaysRow = $E("tr");
		var title = $E("th.datePickerTitle", { innerHTML: DabbleCalendar.months[monthNumber] + " " + yearNumber });
		title.setColspan("7");

// previous month link
		var plc = $E("th.datePickerNav.datePickerPreviousMonthLink");
		plc.setColspan("2");
		var pl = $E("a.noClickThrough", { innerHTML: "&laquo;", href: "javascript:void(0)", title: DabbleCalendar.helpText.previousMonth });
		pl.observe('click', function() { this.buildMonth(this.previousMonth,this.previousYear);	}.bindAsEventListener(this));
		plc.appendChild(pl);

// current month link
		var clc = $E("th.datePickerNav.datePickerCurrentMonthLink");
		clc.setColspan("3");
		var cl = $E("a.noClickThrough", { innerHTML: "&bull;", href: "javascript:void(0)", title: DabbleCalendar.helpText.thisMonth });
		cl.observe('click', function() { this.buildMonth(DabbleCalendar.now.getMonth(), DabbleCalendar.now.getFullYear()); }.bindAsEventListener(this));
		clc.appendChild(cl);

// next month link
		var nlc = $E("th.datePickerNav.datePickerNextMonthLink");
		nlc.setColspan("2");
		var nl = $E("a.noClickThrough", { innerHTML: "&raquo;", href: "javascript:void(0)", title: DabbleCalendar.helpText.nextMonth });
		nl.observe('click', function() { this.buildMonth(this.nextMonth,this.nextYear); }.bindAsEventListener(this));
		nlc.appendChild(nl);
		
// weekdays
		for (var dayToPrint, i = 0; i < 7; i++) {
			dayToPrint = DabbleCalendar.firstDay + i;
			if (dayToPrint > 6) {
				dayToPrint -= 7;
			}
			var day = $E("th.datePickerWeekday", { innerHTML: DabbleCalendar.days[dayToPrint] });
			weekdaysRow.appendChild(day);
		}
		
		navigationRow.appendChild(plc);
		navigationRow.appendChild(clc);
		navigationRow.appendChild(nlc);
		titleRow.appendChild(title);
		thead.appendChild(weekdaysRow);
		thead.insertBefore(navigationRow, weekdaysRow);
		thead.insertBefore(titleRow, weekdaysRow);
		
		var tbody = $E("tbody");

// Rewind to the first day of the week before the 1st of the month
// Takes into account the firstDay setting
		var numberDaysToRewind = printDate.getDay();
		if (DabbleCalendar.firstDay < numberDaysToRewind) {
			numberDaysToRewind -= DabbleCalendar.firstDay;
		} else if (DabbleCalendar.firstDay > numberDaysToRewind) {
			numberDaysToRewind = 7 - DabbleCalendar.firstDay + numberDaysToRewind;
		} else {
			numberDaysToRewind = 0;
		}

		for (var i = numberDaysToRewind; i > 0; i--) {
			printDate.setTime(printDate.getTime() - DabbleCalendar.ONEDAY);
		}

		var previousMonthValid = (monthNumber > 0) ? monthNumber - 1 : 11;

// Print the days of the month
		while (printDate.getMonth() == monthNumber || printDate.getMonth() == previousMonthValid) {
			var row = $E("tr");
			for (var weekday = 0; weekday < 7; weekday++) {
				var cell = $E("td", { className: "datePickerDay" + ((printDate.getMonth() == monthNumber) ? " datePickerDayThisMonth" : " datePickerDayOtherMonth") });
				if (DabbleCalendar.localeString(DabbleCalendar.now) == DabbleCalendar.localeString(printDate)) {
					cell.className += " datePickerDayToday";
				}
				if (this.firstDate) {
					if (DabbleCalendar.localeString(this.firstDate) == DabbleCalendar.localeString(printDate)) {
						cell.className += " datePickerStartRange";
					}
				}
				var link = $E("a#d" + printDate.getTime(), { innerHTML: printDate.getDate(), href: "javascript:void(0)" });
				link.observe('click', DabbleCalendar.setFieldValueOnClick.bindAsEventListener(this));
				printDate.setTime(printDate.getTime() + DabbleCalendar.ONEDAY);
				cell.appendChild(link);
				row.appendChild(cell);
			}
			tbody.appendChild(row);
		}
		
		var tfoot = $E("tfoot");
		
		if (this.allowTime) {
			var timeRow = tfoot.appendChild($E("tr"));
			var timeCell = $E("th.datePickerTimeBox");
			var idPrefix = this.target.id;
			timeCell.setColspan("7");
			this.startTimeBox = $E("span.datePickerStartTime");
			this.endTimeBox = $E("span.datePickerEndTime");
			if (!this.selectRange) {
				this.endTimeBox.setStyle({ display: 'none' });
			}
			this.startTimeLabel = $E("label", { innerHTML: (this.selectRange ? DabbleCalendar.helpText.startTime : DabbleCalendar.helpText.time) });
			this.endTimeLabel = $E("label", { innerHTML: DabbleCalendar.helpText.endTime + " " });
			var startTime = $E("input.text.formTextInput#" + idPrefix + "StartTime", { type: "text", value: this.startTime });
			var endTime = $E("input.text.formTextInput#" + idPrefix + "EndTime", { type: "text", value: this.endTime });
			this.startTimeLabel.setAttribute("for", startTime.id);
			this.endTimeLabel.setAttribute("for", endTime.id);
			this.startTimeBox.appendChild(this.startTimeLabel);
			this.startTimeBox.appendChild(document.createTextNode(" "));
			this.startTimeBox.appendChild(startTime);
			this.endTimeBox.appendChild(this.endTimeLabel);
			this.startTimeBox.appendChild(document.createTextNode(" "));
			this.endTimeBox.appendChild(endTime);
			timeCell.appendChild(this.startTimeBox);
			timeCell.appendChild(this.endTimeBox);
			timeRow.appendChild(timeCell);
			startTime.observe('keyup', function(event) { this.startTime = Event.element(event).value;	}.bindAsEventListener(this));
			endTime.observe('keyup', function(event) { this.endTime = Event.element(event).value; }.bindAsEventListener(this));
			var cancelReturnKey = function(event) {
				event = event || window.event;
				var key = event.keyCode || event.which;
				return (key != 13);
			};
			startTime.onkeypress = cancelReturnKey;
			endTime.onkeypress = cancelReturnKey;
		}

// Show checkbox for selecting ranges (if applicable)
		if (this.selectRangeIsOptional) {
			var footerRow = tfoot.appendChild($E("tr"));
			var footerCell = $E("th.datePickerOptions");
			footerCell.setColspan("7");
			var checkbox = $E("input.checkbox#" + this.field.id + "RangeCheckbox-" + printDate.getFullYear() + printDate.getMonth(), { type: "checkbox" });
			checkbox.observe('click', function(event) {
				var checkbox = Event.element(event);
				this.selectRange = checkbox.checked;
				if (this.selectRange && this.allowTime) {
					this.endTimeBox.show();
					this.startTimeLabel.innerHTML = DabbleCalendar.helpText.startTime;
				} else if (!this.selectRange && this.allowTime) {
					this.endTimeBox.hide();				
					this.startTimeLabel.innerHTML = DabbleCalendar.helpText.time;
				}
			}.bindAsEventListener(this));
			var label = $E("label", { innerHTML: " " + DabbleCalendar.rangeText });
			label.setAttribute("for", checkbox.id);
			footerCell.appendChild(checkbox);
			footerCell.appendChild(label);
			footerRow.appendChild(footerCell);
			checkbox.checked = this.selectRange;
		}

// Show help link (if applicable)
		if (DabbleCalendar.showHelp) {
			var helpRow = $E("tr");
			var helpCell = $E("th.datePickerHelp", { innerHTML: DabbleCalendar.helpText.help });
			helpCell.setColspan("7");
			helpRow.appendChild(helpCell);
			tfoot.appendChild(helpRow);
		}

		table.appendChild(thead);
		table.appendChild(tbody);
		table.appendChild(tfoot);
		
		this.target.getElementsBySelector('table.datePickerTable').invoke('remove');

		table = this.target.appendChild(table);
		if (typeof(DabblePageForm) != 'undefined') {
			DabblePageForm.fixIESelect(this.target);
		} else if (this.target.visible()) {
			fixOnePopMenu(this.target);
		}
	},
	setFieldValueOnClick: function(event) {
		var link = Event.element(event);
		var date = new Date();
		date.setTime(link.id.substr(1)); // copy the date to set from the link's ID
		var startTime = (this.startTime == "") ? "" : " " + this.startTime;
		if (this.selectRange) { // if ranges are enabled, then check whether a start date has been chosen
			var endTime = (this.endTime == "") ? "" : " " + this.endTime;
			if (!this.firstDate) {
				this.firstDate = date;
				this.field.value = DabbleCalendar.localeString(date) + startTime + " - ";
				link.addClassName("datePickerStartRange");
			} else {
				if (this.firstDate.getTime() < date.getTime()) {
					this.field.value = DabbleCalendar.localeString(this.firstDate) + startTime + " - " + DabbleCalendar.localeString(date) + endTime;
				} else {
					this.field.value = DabbleCalendar.localeString(date) + startTime + " - " + DabbleCalendar.localeString(this.firstDate) + endTime;
				}
				var firstDateLink = $("d" + this.firstDate.getTime());
				if (firstDateLink) {
					firstDateLink.removeClassName("datePickerStartRange");
				}
				this.firstDate = null;
				this.target.hide();
			}
		} else {
			this.field.value = DabbleCalendar.localeString(date) + startTime;
			this.target.hide();
		}
	},
	field: null,
	target: null,
	now: new Date(),
	runOnce: false,
	initialize: function() {
		if (this.firstDay > 6 || this.firstDay < 0) {
			this.firstDay = 0;
		}
		if (typeof(DabblePageForm) != 'undefined') { // if we're on a page, don't show help
			this.showHelp = false;
		}
	},
	Create: function(fieldID, calendarBoxID, attributes) {
		if (!this.runOnce) {
			this.initialize();
		}
		var newCalendar = {
			field: $(fieldID),
			target: $(calendarBoxID),
			selectRange: DabbleCalendar.selectRangeByDefault,
			selectRangeIsOptional: DabbleCalendar.selectRangeIsOptional,
			allowTime: DabbleCalendar.allowTime,
			startTime: '',
			endTime: '',
			firstDate: null,
			buildMonth: DabbleCalendar.buildMonth,
			buildTime: DabbleCalendar.buildTime,
			initialized: false
		};
		for (a in attributes) {
			newCalendar[a] = attributes[a];
		}
		if (!newCalendar.field || !newCalendar.target) return null;
		var calendarPopupLink = newCalendar.target.previous('a');
		var initializeNewCalendar = function() {
			if (!newCalendar.initialized) {
				newCalendar.buildMonth(DabbleCalendar.now.getMonth(), DabbleCalendar.now.getFullYear());
				newCalendar.initialized = true;
			}
		};
		calendarPopupLink.observe('click', initializeNewCalendar);
		return newCalendar;
	}
};

Element.addMethods({
	setColspan: function(element, value) {
		if (element.colSpan) {
			element.colSpan = value;
		} else {
			element.setAttribute('colspan', value);
		}
	}
});

