/*
	calendar.js
	
	Simple DHTML calendar (IE5)
	
	Ruben Prins
*/
function Calendar(inputDate, loc, update, dateFormat) {
	//TODO : missing inputDate?
	//TODO : literal dates (convert)
	this.id = registerCalendar(this);
	this.locationId = loc;
	
	if(dateFormat != null)
		this.dateFormat = dateFormat;
	else
		this.dateFormat = Calendar.dateFormat;
		
	if(update != null) 	{
		if(typeof update == "string") {
			this.fieldName = update;
			this.onautochange = function() {
				document.getElementByName(this.fieldName).value = 
					this.getDate();
			}
		}
		else {
			this.formField = null;
			this.onautochange = update;
		}
	}
	this.selectedDate = Calendar.parseDate(inputDate);
	if(this.selectedDate == null)
		this.viewDate = Calendar.today;
	else
		this.viewDate = this.selectedDate;
	
	this.holidays = [];
	this.holidays.add = Calendar.holidays.add;
	
	this.onautochange();
	
	function registerCalendar(cal) {
		Calendar._registeredCalendars[Calendar._registeredCalendars.length] = cal;
		return Calendar._registeredCalendars.length - 1;
	}
}
// Defaults
// Static constants
Calendar.months = {january : 0, february : 1, march : 2, april : 3,
				   may : 4, june : 5, july : 6, august : 7, september : 8,
				   october : 9, november : 10, december : 11};
Calendar.days = {sunday : 0, monday : 1, tuesday : 2, wednesday : 3,
				 thursday : 4, friday : 5, saturday : 6};
Calendar.today = new Date();
Calendar.today = new Date(Calendar.today.getFullYear(), Calendar.today.getMonth(), Calendar.today.getDate());
// Localization
Calendar.names = {};
Calendar.names.months = {longNames :  ["January", "February", "March", "April",
								  "May", "June", "July", "August", "September", 
								  "October", "November", "December"],
						 shortNames : ["Jan", "Feb", "Mar", "Apr",
								  "May", "Jun", "Jul", "Aug", "Sep", 
								  "Oct", "Nov", "Dec"]};
Calendar.names.days = {letterNames : ["S", "M", "T", "W", "T", "F", "S"],
					   longNames :   ["Sunday", "Monday", "Tuesday", "Wednesday", 
								 "Thursday", "Friday", "Saturday"],
					   shortNames :  ["Sun", "Mon", "Tue", "Wed", 
								 "Thu", "Fri", "Sat"]};
Calendar.tooltips = { today : '', next : '', back : '', clear : 'Clear date', nextYear : '', backYear : ''}
Calendar.strings = { today : 'Today: {mmm} {d}, {yyyy}',
					 title : '{MMM} {yyyy}',
					 back : '&lt;',
					 next : '&gt;',
					 backYear : '&#171;',
					 nextYear : '&#187;',
					 clear : 'Clear date',
					 disabledBack : '',
					 disabledNext : '',
					 disabledBackYear : '',
					 disabledNextYear : '',
					 day : '{D}'};
Calendar.dateFormat = '{yyyy}/{mm}/{dd}';
Calendar.dateParser = { pattern : null, // RegExp
						year : 0,
						month : 0,
						day : 0}
Calendar.defaultDateFormat  = /^\s*(\d{4})\s*[\/\-]\s*([01]?\d)\s*[\/\-]\s*([0123]?\d)\s*$/;
Calendar.europeanDateFormat = /^\s*([0123]?\d)\s*[\.\/\-]\s*([01]?\d)\s*[\.\/\-]\s*(\d{4})\s*$/;
Calendar.usDateFormat = /^\s*([01]?\d)\s*[\-\.\/]\s*([0123]?\d)\s*[\-\.\/]\s*(\d{4})\s*$/;

Calendar.prototype.holidays = null;
// Properties
Calendar.prototype.id = 0;
Calendar.prototype.dateFormat = '{yyyy}/{m}/{d}';
Calendar.prototype.startAt = 1;
Calendar.prototype.showToday = true;
Calendar.prototype.showDays = true;
Calendar.prototype.showTitle = true;
Calendar.prototype.fieldName = null;
Calendar.prototype.onchange = function(){}
Calendar.prototype.onautochange = function(){}
Calendar.prototype.showHolidays = true;
Calendar.prototype.showWeekends = true;
Calendar.prototype.selectedDate = Calendar.today;
Calendar.prototype.viewDate = null;
Calendar.prototype.minDate = null;
Calendar.prototype.maxDate = null;
Calendar.prototype.locationId = null;
Calendar.prototype.visible = false;
Calendar.prototype.disabled = false;
Calendar.prototype.interaction = true;
Calendar.prototype.allowEmpty = false;
Calendar.prototype.selectorDropDown = null;

Calendar.prototype.enable = function() {
	this.disabled = false;
	if(this.visible)
		this.show();
}

Calendar.prototype.disable = function() {
	this.disabled = true;
	if(this.visible)
		this.show();
}
// Methods
Calendar.prototype.show = function() {
	this.minDate = Calendar.parseDate(this.minDate);
	this.maxDate = Calendar.parseDate(this.maxDate);
	var selectedDate = Calendar.parseDate(this.selectedDate),
		currentDate = Calendar.today,
		viewDate = this.viewDate,
		minDate = this.minDate,
		maxDate = this.maxDate,
		minTime = minDate != null ? minDate.getTime() : 0, 
		maxTime = maxDate != null ? maxDate.getTime() : 0,
		selectedTime = selectedDate != null ? selectedDate.getTime() : 0,
		currentTime = currentDate.getTime();
	var currDate  = viewDate.getDate(),
		currMonth = viewDate.getMonth(), 
		currYear  = viewDate.getFullYear();
	var firstDate = new Date(currYear, currMonth, 1);
	var renderString = "<div class=\"TBmCalendar\">";
	var loc = document.getElementById(this.locationId);
	var i;
	
	if (this.selectorDropDown)
		this.selectorDropDown.close();
	
	if(loc == null) {
		alert('Document corrupted.');
		return;
	}
	this.visible = true;
	
	if(this.showTitle) {
		renderString += '<table border="0" cellspacing="0" cellpadding="0" width="100%">'
		
		renderString += '<tr class="TBmCalendar-nav"><td class="TBmCalendar-nav-back" align="left" width="10%">';
			if(!this.disabled && Calendar.strings.backYear.length > 0 && (maxDate == null || maxTime > new Date(currYear-1, currMonth, 1).getTime()))
				renderString += '<a href="#" title="' + Calendar.tooltips.backYear + '" onclick="Calendar.setYear(\'' + this.id + '\',\'' + (currYear-1) + '\');window.event.cancelBubble=true;return false;">' + Calendar.strings.backYear + '</a>';
			else
				renderString += Calendar.strings.disabledBackYear;
			renderString +=	'</td>'

		renderString += '<td class="TBmCalendar-nav-back" align="left" width="10%">';
			if(!this.disabled && (minDate == null || minTime < firstDate.getTime()))
				renderString += '<a href="#" title="' + Calendar.tooltips.back + '" onclick="Calendar.setMonth(\'' + this.id + '\',\'-\');window.event.cancelBubble=true;return false;">' + Calendar.strings.back + '</a>';
			else
				renderString += Calendar.strings.disabledBack;
			renderString +=	'</td>'
		
		if (this.disabled)
			renderString += '<th class="TBmCalendar-nav-title" width="60%">' +
				Calendar.formatDate(viewDate, Calendar.strings.title) + 
				'</th>';
		else {
			renderString += '<th class="TBmCalendar-nav-title" width="60%">';
			if (!(bw.ie && bw.mac)) // doesn't work right for IE/Mac.
				renderString += 
					'<a id="' + this.id + '_MonthSelectAnchor" href="#" onclick="Calendar.selectMonths(\'' + this.id + '\');window.event.cancelBubble=true; return false;">' +
					Calendar.formatDate(viewDate, Calendar.strings.title) + 
					'</a>';
			else
				renderString += Calendar.formatDate(viewDate, Calendar.strings.title);
			renderString += '</th>';
		}
		
		renderString += '<td class="TBmCalendar-nav-next" align="right" width="10%">';
			if(!this.disabled && (maxDate == null || maxTime > new Date(currYear, currMonth + 1, 1).getTime()))
				renderString += '<a href="#" title="' + Calendar.tooltips.next + '" onclick="Calendar.setMonth(\'' + this.id + '\',\'+\');window.event.cancelBubble=true;return false;">' + Calendar.strings.next + '</a>';
			else
				renderString += Calendar.strings.disabledNext;
			renderString +=	'</td>'

		renderString += '<td class="TBmCalendar-nav-next" align="right" width="10%">';
			if(!this.disabled && Calendar.strings.nextYear.length > 0 && (maxDate == null || maxTime > new Date(currYear + 1, currMonth, 1).getTime()))
				renderString += '<a href="#" title="' + Calendar.tooltips.nextYear + '" onclick="Calendar.setYear(\'' + this.id + '\',\'' + (currYear+1) + '\');window.event.cancelBubble=true;return false;">' + Calendar.strings.nextYear + '</a>';
			else
				renderString += Calendar.strings.disabledNextYear;
			renderString += '</td>'
		
		renderString += '</tr><tr><td colspan="5">';
	}
	
	renderString += '<table class="TBmCalendar-body" border="0" cellspacing="0" cellpadding="0" width="100%">';
	
	if(this.showDays) {
		renderString += '<tr class="TBmCalendar-days">';
		for(i = 0; i < 7; i++) {
			var day = (i + this.startAt) % 7;
			var dayName = Calendar.strings.day;
			dayName = dayName.replace('{W}', Calendar.names.days.letterNames[day]);
			dayName = dayName.replace('{D}', Calendar.names.days.longNames[day].substring(0,1));
			dayName = dayName.replace('{DD}', Calendar.names.days.shortNames[day]);
			dayName = dayName.replace('{DDD}', Calendar.names.days.longNames[day]);
			renderString += '<th class="TBmCalendar-days-name">' 
			+ dayName + '</th>';
		}
		renderString += '</tr>';
	}
		
	var offset = firstDate.getDay() - this.startAt;
	if(offset < 0)
		offset += 7;
	
	var scrapDate = new Date(currYear, currMonth, 1);
	scrapDate.setDate(1-offset);
	
	do {
		renderString += '<tr>';
		for(var d = 0; d < 7; d++) {
			var workingDay = scrapDate.getDay(), 
				workingDate = scrapDate.getDate(),
				workingMonth = scrapDate.getMonth(),
				workingYear = scrapDate.getFullYear(),
				workingTime = scrapDate.getTime();
			var className = new String(),
				dateString = workingDate,
				disabled = this.disabled,
				holidayTooltip = new String();
			
			if(workingTime == currentTime) {
				className = ' TBmCalendar-day-current';
			}
			if(this.showWeekends && (workingDay == 0 || workingDay == 6)) {
				className += ' TBmCalendar-day-weekend';
			}
			if(this.showHolidays && workingMonth == currMonth) {
				var holiday;
				for(i = 0; i < Calendar.holidays.length; i++) {
					holiday = Calendar.holidays[i]
					if(holiday.date.getTime() == workingTime) {
						if(holidayTooltip.length > 0)
							holidayTooltip += '; ' + holiday.description;
						else
							holidayTooltip = holiday.description;
					}
				}
				for(i = 0; i < this.holidays.length; i++) {
					holiday = this.holidays[i]
					if(holiday.date.getTime() == workingTime) {
						if(holidayTooltip.length > 0)
							holidayTooltip += '; ' + holiday.description;
						else
							holidayTooltip = holiday.description;
					}
				}
				if(holidayTooltip.length > 0)
					className += ' TBmCalendar-day-holiday';
			}
			if(this.disabled ||
			   (minDate != null && minTime > workingTime) ||
			   (maxDate != null && maxTime < workingTime)) {
				className += ' TBmCalendar-day-disabled'
				disabled = true;
			}
			else if(selectedDate != null && workingTime == selectedTime) {
				className += ' TBmCalendar-day-selected';
				dateString = '<strong>' + dateString + '</strong>';
			}
			else if(workingMonth != currMonth) {
				className += ' TBmCalendar-day-exterior';
			}
				
			renderString += '<td class="TBmCalendar-day' + className + '"'
				+ ' title="' + holidayTooltip.replace('"', '&quot;') + '">'
				
			if(!(disabled || this.disabled) && this.interaction) {
				renderString += '<a href="#" onclick="Calendar.clickDay(' + this.id + ','
							 + workingTime + ',true);return false;">';
			}
			
			renderString += dateString 
			
			if(!(disabled || this.disabled) && this.interaction)
				renderString += '</a>';
			
			renderString += '</td>';
			
			scrapDate.setDate(workingDate + 1);
		}
		renderString += '</tr>';
	}
	while(scrapDate.getMonth() == currMonth);
	
	if(this.showToday || this.allowEmpty && Calendar.strings.clear.length > 0) {
		renderString += '<tr class="TBmCalendar-footer"><td colspan="7">';
	
		if(this.showToday) {

			if(this.disabled || !this.interaction ||
				(minDate != null && minTime > workingTime) ||
				(maxDate != null && maxTime < workingTime))
					renderString += '<div class="TBmCalendar-today TBmCalendar-today-disabled"><span class="TBmCalendar-today-button">'
						+ Calendar.formatDate(currentDate, Calendar.strings.today) + '</span></div>';
			else
				renderString += '<div class="TBmCalendar-today">'
					+ '<a class="TBmCalendar-today-button" href="#" title="' + Calendar.tooltips.today + '" onclick="'
					+ 'Calendar.clickDay(' + this.id + ',' + currentTime + ',true);return false;">'
					+ Calendar.formatDate(currentDate, Calendar.strings.today) + '</a></div>';

		}
	
		if(this.allowEmpty && Calendar.strings.clear.length > 0) {
			if (this.disabled || !this.interaction)
				renderString += '<div class="TBmCalendar-clear"><span class="TBmCalendar-clear-button">' + Calendar.strings.clear + '</span></div>';
			else
				renderString += '<div class="TBmCalendar-clear"><a class="TBmCalendar-clear-button" href="#" title="' + Calendar.tooltips.clear + '" onclick="Calendar.clickDay(' + this.id + ',null);return false;">' + Calendar.strings.clear + '</a></div>';
		}
		
		renderString += '</td></tr>';
	}
	
	renderString += '</table>';
	
	if(this.showTitle)
		renderString += '</td></tr></table>';
	
	renderString += '</div>';
	
	loc.innerHTML = renderString;
	
	if (this.onrender)
		this.onrender();
	
}
Calendar.prototype.render = function(styleString, locId) {
	var docString;
	if(locId == null)
		locId = '__Calendar_' + this.id;
		
	docString = '<span id="' + locId + '"';
	if(styleString != null)
		docString += ' style="' + styleString + '"';
	docString += '></span>';
	
	this.locationId = locId
	
	document.write(docString);
	this.show();
}
Calendar.prototype.hide = function() {
	this.visible = false;
	
	var loc = document.getElementById(this.locationId);
	
	if(loc == null)
	{
		alert('Document corrupted.');
		return;
	}
	
	loc.innerHTML = '';
}
Calendar.prototype.getMonthSelectAnchor = function() {
	return document.getElementById(this.id + "_MonthSelectAnchor");
}
// Externals
Calendar.selectMonths = function(calId) {
	var calendar = Calendar.getById(calId);
	var div = calendar.selectorDropDown.element;
	
	if (div) {
		var viewDate = calendar.viewDate;
		var renderString = "<div class=\"TBmCalendar-monthSelect\">";
		var minMonthDate = new Date(viewDate.getFullYear(), viewDate.getMonth() - 5, 1);
		var maxMonthDate = new Date(viewDate.getFullYear(), viewDate.getMonth() + 7, 1);
		
		if (calendar.minDate && minMonthDate < calendar.minDate)
			minMonthDate = calendar.minDate;
		
		if (calendar.maxDate && maxMonthDate > calendar.maxDate)
			maxMonthDate = calendar.maxDate;
		
		minMonthDate = new Date(minMonthDate.getFullYear(), minMonthDate.getMonth(), 1);
		maxMonthDate = new Date(maxMonthDate.getFullYear(), maxMonthDate.getMonth(), 1);
		
		for (var date = minMonthDate; date < maxMonthDate; date = new Date(date.getFullYear(), date.getMonth() + 1, date.getDate())) {
			renderString += "<div><a class=\"TBmCalendar-monthSelect-item";
			
			if (date.getMonth() == viewDate.getMonth())
				renderString += " TBmCalendar-monthSelect-current";
			
			renderString += "\" href=\"#\" onclick=\"Calendar.setMonth('" + calId + "'," + date.getMonth() + ");window.event.cancelBubble=true;return false;\">" +
				Calendar.formatDate(date, Calendar.strings.title) + "</a></div>";
		}
		
		renderString += "</div>";
		
		div.innerHTML = renderString;
		calendar.selectorDropDown.anchor = calendar.getMonthSelectAnchor();
		calendar.selectorDropDown.open();
	}
}
Calendar.clickDay = function(calId, dateValue, select) {
	var calendar = Calendar.getById(calId);
	if(dateValue != null) {
		var date = new Date();
		date.setTime(dateValue);
		calendar.selectedDate = date;
		calendar.viewDate = date;
	}
	else {
		calendar.selectedDate = null;
	}
	calendar.onautochange();
	if(select == null || select == true) {
		calendar.onchange();
	}
	if(calendar.visible)
		calendar.show();
	window.event.cancelBubble = true;
}
Calendar.prototype.getDate = function(dateFormat) {
	return Calendar.formatDate(this.selectedDate, dateFormat || this.dateFormat || Calendar.dateFormat || '{yyyy}/{m}/{d}');
}
Calendar.prototype.getCanonicalDate = function() {
	if(selectedDate != null)
		return '' + this.selectedDate.getFullYear() + '/' + (this.selectedDate.getMonth() + 1) + '/' + this.selectedDate.getDate();
	else
		return '';
}
Calendar.prototype.setDate = function(newValue, setEmpty, setInvalid) {
	if(typeof setEmpty == 'undefined' || setEmpty == null)
		setEmpty = this.allowEmpty;
		
	if((newValue == '' || newValue == null) && setEmpty)
		this.selectedDate = null;
	else {
		var parsedDate = Calendar.parseDate(newValue);
		if(parsedDate != null) {
			var parsedTime = parsedDate.getTime();
			if((this.minDate != null && this.minDate.getTime() > parsedTime) ||
			   (this.maxDate != null && this.maxDate.getTime() < parsedTime))
			   return false;
			else {
				this.selectedDate = parsedDate;
				this.viewDate.setTime(this.selectedDate.getTime());
				this.viewDate.setDate(1);
			}
		}
		else {
			if(setInvalid == false)
				return false;
			this.selectedDate = null;
		}
	}
	this.onautochange();
	this.onchange();
	if(this.visible)
		this.show();
	return true;
}
Calendar.getDate = function(calId) {
	var calendar = Calendar.getById(calId);
	return calendar.getDate();
}
Calendar.getCanonicalDate = function(calId) {
	var calendar = Calendar.getById(calId);
	return calendar.getCanonicalDate();
}
Calendar.setDate = function(calId, date, setEmpty, setInvalid) {
	var calendar = Calendar.getById(calId);
	return calendar.setDate(date, setEmpty, setInvalid);
}
Calendar.prototype.setMonth = function(monthValue) {
	if(monthValue == '+')
		monthValue = this.viewDate.getMonth() + 1;
	else if(monthValue == '-')
		monthValue = this.viewDate.getMonth() - 1;
	
	this.viewDate = new Date(this.viewDate.getFullYear(), monthValue, 1);
	
	if(this.visible)
		this.show();
}
Calendar.prototype.getMonth = function() {
	return this.viewDate.getMonth();
}
Calendar.setMonth = function(calId, monthValue) {
	var calendar = Calendar.getById(calId);
	calendar.setMonth(monthValue);
}
Calendar.getMonth = function(calId) {
	var calendar = Calendar.getById(calId);
	return calendar.getMonth();
}
Calendar.prototype.setYear = function(yearValue) {
	this.viewDate = new Date(yearValue,this.viewDate.getMonth(),1);
	if(this.visible)
		this.show();
}
Calendar.prototype.getYear = function() {
	return this.viewDate.getFullYear();
}
Calendar.setYear = function(calId, yearValue) {
	var calendar = Calendar.getById(calId);
	calendar.setYear(yearValue);
}
Calendar.getYear = function(calId) {
	var calendar = Calendar.getById(calId);
	return calendar.getYear();
}
Calendar._registeredCalendars = [];
Calendar.getById = function(calId) {
	var index = parseInt(calId);
	if(index == null)
		return null;
	else
		return Calendar._registeredCalendars[index];
}
Calendar.formatDate = function(date, dateFormat) {
	if(date != null) {
		if(dateFormat == null || typeof dateFormat == 'undefined')
			dateFormat = Calendar.dateFormat;
		
		var d = dateFormat;
		var month = date.getMonth();
		var day = date.getDate();
		var year = date.getFullYear();
		var year2 = year % 100;
		var weekday = date.getDay();
		
		//alert("Formatting: " + day + ", " + month + ", " + year);
		
		d = d.replace('{M}', Calendar.names.months.longNames[month].substring(0,1));
		d = d.replace('{MM}', Calendar.names.months.shortNames[month]);
		d = d.replace('{MMM}', Calendar.names.months.longNames[month]);
		d = d.replace('{mmm}', Calendar.names.months.longNames[month]);
		d = d.replace('{mm}', (month < 9) ? '0' + (month + 1) : (month + 1));
		d = d.replace('{m}', month + 1);
		d = d.replace('{W}', Calendar.names.days.letterNames[weekday]);
		d = d.replace('{D}', Calendar.names.days.longNames[weekday].substring(0,1));
		d = d.replace('{DD}', Calendar.names.days.shortNames[weekday]);
		d = d.replace('{DDD}', Calendar.names.days.longNames[weekday]);
		d = d.replace('{ddd}', Calendar.names.days.longNames[weekday]);
		d = d.replace('{dd}', (day < 10) ? ('0' + day) : day);
		d = d.replace('{d}', day);
		d = d.replace('{yyyy}', year);
		d = d.replace('{yy}', (year2 < 10) ? ('0' + year2) : year2);
		d = d.replace('{y}', year2);
		
		return d;
	}
	else
		return '';
}
// Holidays
Calendar.holidays = [];
Calendar.holidays.add = function(date, description) {
	this[this.length] = new Holiday(date, description);
	
	function Holiday(date, description) {
		var parsedDate = Calendar.parseDate(date);
		if(parsedDate == null) {
			alert('The value ' + date + ' is not a valid date!');
		}
		this.date = parsedDate;
		this.description = description;
	}
}
// Date parsing
Calendar.parseDate = function(value) {
	if(value == null)
		return value;
	else if(value == '')
		return null;
	if(typeof(value) == "object") // Date object; instanceof not supported by IE/Mac
		return value;
	else {
		var matches;
		
		if(Calendar.dateParser.pattern != null && 
		   (matches = Calendar.dateParser.pattern.exec(value)) != null) {
		   //alert("pattern matched: " + matches[Calendar.dateParser.day+1] + "/" + matches[Calendar.dateParser.month+1] + "/" + matches[Calendar.dateParser.year+1] + " -- " + Calendar.dateParser.pattern + "; '" + value + "'");
			return new Date(parseInt(matches[Calendar.dateParser.year+1], 10), 
							parseInt(matches[Calendar.dateParser.month+1], 10) - 1, 
							parseInt(matches[Calendar.dateParser.day+1], 10));
		} else if((matches = Calendar.defaultDateFormat.exec(value)) != null) {
			return new Date(parseInt(matches[1], 10), parseInt(matches[2], 10) - 1, parseInt(matches[3], 10));
		}
		else if((matches = matches = Calendar.europeanDateFormat.exec(value)) != null) {
			return new Date(parseInt(matches[3], 10), parseInt(matches[2], 10) - 1, parseInt(matches[1], 10));
		}
		else {
			return null;
			// Werkt niet 100%
			var parsedDate = Date.parse(value);
			if(!isNaN(parsedDate) && parsedDate != null) {
				var newDate = new Date();
				newDate.setTime(parsedDate);
				return newDate;
			}
			else 
				return null;
		}
	}
}
