function EventCalendar(inFrom, inTo, inElementID, inLanguage) {
	var myWeekdays = new Array();
	myWeekdays["de"] = new Array("Mo", "Di", "Mi", "Do", "Fr", "Sa", "So");
	myWeekdays["en"] = new Array("Mo", "Tu", "We", "Th", "Fr", "Sa", "Su");
	var myMonths = new Array();
	myMonths["de"] = new Array("Jänner", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember");
	myMonths["en"] = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
	
	var me = this;
	var myLanguage = inLanguage ? inLanguage : "de";
	var myEventDates = new Array();
	var myEventNames = new Array();
	var mySelection = new Array();
	var myCalendarCells = new Array();
	var myLastSelection;
	var myElement = null;
	var myFrom = null;
	var myTo = null;
	var isSelectable = true;
	var isDragging = false;
	
	// components
	var myTable, myTableBody;
	var myEventWindow;
	
	
	/*************************************************************/
	/*                        CREATION                           */
	/*************************************************************/
	
	var createCalendar = function(inFrom, inTo, inElementID) {
		myElement = document.getElementById(inElementID);
		myFrom = parseDate(inFrom, "dd.mm.yyyyy");
		myTo = parseDate(inTo, "dd.mm.yyyy");
		
		myTable = document.createElement("table");
		myTable.cellSpacing = "2px";
		myTable.cellPadding = "0px";
		myTable.className = "calendar";
		
		myTable.onmousedown = onTableMouseDown;
		document.body.onmouseup = onDocumentMouseUp;
		
		myTableBody = document.createElement("tbody");
		
		createWeekdays();
		createDays();
		createEventWindow();
		me.setSelectable(true);
		
		myTable.appendChild(myTableBody);
		myElement.appendChild(myTable);
		myElement.onselectstart = function() { return false; }
	}
	
	var createWeekdays = function() {
		var header = document.createElement("tr");
		
		for(var i=0; i<myWeekdays[myLanguage].length; i++) {
			var weekday = document.createElement("td");
			weekday.className = "calendar-weekday";
			weekday.appendChild(document.createTextNode(myWeekdays[myLanguage][i]));
			
			header.appendChild(weekday);
		}
		
		myTableBody.appendChild(header);
	}

	var createMonths = function(inMonth) {
		var row = document.createElement("tr");
		var cell = document.createElement("td");
		cell.colSpan = 7;
		cell.className = "calendar-month";
		cell.appendChild(document.createTextNode(myMonths[myLanguage][inMonth]));
		
		row.appendChild(cell);
		myTableBody.appendChild(row);
	}
	
	var createDays = function() {
		var row = document.createElement("tr");
		var date = myFrom;

		// place month into empty cells
		/*
		var cell = document.createElement("td");
		cell.colSpan = date.getDay()-1;
		cell.className = "calendar-month";
		cell.appendChild(document.createTextNode("Mai"));
		row.appendChild(cell);
		*/
		createMonths(date.getMonth());
		for(var i=0; i<date.getDay()-1; i++)
			row.appendChild(document.createElement("td"));
		
		while(date <= myTo) {
			var prefix = date.getDate() < 10 ? "0" : "";
			
			// create a new row for each week
			if(date.getDay() == 1) {
				myTableBody.appendChild(row);
				row = document.createElement("tr");
			}
			
			// switch month
			if(date.getDate() == 1) {
				// fill row after the last day of the old month
				for(var i=date.getDay()-1; i<7; i++)
					row.appendChild(document.createElement("td"));
				
				// create a new row for the new month
				myTableBody.appendChild(row);
				row = document.createElement("tr");
				createMonths(date.getMonth());
				
				// fill row until the first day of the new month
				for(var i=0; i<date.getDay()-1; i++)
					row.appendChild(document.createElement("td"));
			}
			
			var cell = document.createElement("td");
			cell.appendChild(document.createTextNode(prefix + date.getDate()));
			cell.className = "calendar-day";
			cell.date = date;
			
			cell.onmousedown = onCellMouseDown;
			cell.onmouseup = onCellMouseUp;
			cell.onmouseover = onCellMouseOver;
			cell.onmouseout = onCellMouseOut;
			
			row.appendChild(cell);
			myCalendarCells[date] = cell;
			
			date = getNextDay(date);
		}
		while(date.getDay() != 1) {
			row.appendChild(document.createElement("td"));
			date = getNextDay(date);
		}
		
		myTableBody.appendChild(row);
	}
	
	var createEventWindow = function() {
		myEventWindow = document.createElement("div");
		myEventWindow.className = "calendar-event-window";
	}
	
	
	/*************************************************************/
	/*                    PUBLIC INTERFACE                       */
	/*************************************************************/
	
	me.addEvent = function(inDate, inName) {
		inDate = typeof(inDate) == "string" ? parseDate(inDate, "dd.mm.yyyy") : inDate;
		myEventDates[myEventDates.length] = inDate;
		myEventNames[myEventNames.length] = inName;
		
		if(myCalendarCells[inDate])
			myCalendarCells[inDate].style.fontWeight = "bold";
	}
	
	me.setSelection = function(inStart, inEnd) {
		inStart = typeof(inStart) == "string" ? parseDate(inStart, "dd.mm.yyyy") : inStart;
		inEnd = typeof(inEnd) == "string" ? parseDate(inEnd, "dd.mm.yyyy") : inEnd;
		if(inStart) inStart = inStart.getTime();
		if(inEnd) inEnd = inEnd.getTime();
				
		var select = false;
		me.clearSelection();
		
		for(var i=1; i<myTableBody.childNodes.length; i++) {
			for(var j=0; j<myTableBody.childNodes[i].childNodes.length; j++) {
				var cell = myTableBody.childNodes[i].childNodes[j];
				
				if(cell.date && (cell.date.getTime() == inStart || cell.date.getTime() == inEnd)) {
					cell.className = "calendar-day-selected";
					mySelection.push(cell);
					
					if(inStart != inEnd)
						select = !select;
					
					continue;
				}
				
				if(select && cell.date) {
					cell.className = "calendar-day-selected";
					mySelection.push(cell);
				}
			}
		}
	}
	
	me.getSelection = function() {
		var array = new Array();
		
		for(var i=0; i<mySelection.length; i++) {
			if(isEventDate(mySelection[i].date) == true)
				array.push(formatDate(mySelection[i].date));
		}
		
		return array;
	}
	
	me.getSelectionStart = function() {
		if(mySelection.length == 0)
			return "";
		
		var start = mySelection[0].date;
		var end = mySelection[mySelection.length-1].date;
		
		if(start.getTime() < end.getTime())
			return formatDate(start);
		else
			return formatDate(end);
	}
	
	me.getSelectionEnd = function() {
		if(mySelection.length == 0)
			return "";
		
		var start = mySelection[0].date;
		var end = mySelection[mySelection.length-1].date;
		
		if(end.getTime() > start.getTime())
			return formatDate(end);
		else
			return formatDate(start);
	}
	
	me.clearSelection = function() {
		for(var i=0; i<mySelection.length; i++) {
			if(mySelection[i].date)
				mySelection[i].className = "calendar-day";
		}
		
		mySelection.length = 0;
	}
	
	me.setSelectable = function(inSelectable) {
		isSelectable = inSelectable;
		
		// adjust cursor appearance
		for(var date in myCalendarCells) {
			myCalendarCells[date].style.cursor = isSelectable == true ? "pointer" : null;
		}
	}
	
	
	/*************************************************************/
	/*                     PRIVATE METHODS                       */
	/*************************************************************/
	
	var getNextDay = function(inDate) {
		return new Date(inDate.getTime() + 1 * 24 * 60 * 60 * 1000);
	}
	
	var isEventDate = function(inDate) {
		for(var i=0; i<myEventDates.length; i++)
			if(myEventDates[i].getTime() == inDate.getTime()) return true;
		
		return false;
	}
	
	var getEventList = function(inDate) {
		var cache = new Array();
		var events = "";
		
		for(var i=0; i<myEventDates.length; i++) {
			if(myEventDates[i].getTime() == inDate.getTime() && cache[myEventNames[i]] == null) {
				events = events + (events.length > 0 ? "<br>" : "") + myEventNames[i];
				cache[myEventNames[i]] = true;
			}
		}
		
		return events;
	}
	
	var parseDate = function(inString, inFormat) {
		if(inString == null || inString.length == 0) return null;
		
		var aDate = new Date();
		var day, month, year;
		
		if(inString == "" || inFormat == "") return aDate;
		inString = inString.replace("/", "@").replace("/", "@");
		inString = inString.replace("-", "@").replace("-", "@");
		inString = inString.replace(".", "@").replace(".", "@");
		
		// check again
		if (inString.indexOf("/")>=0 || inString.indexOf("-")>=0 || inString.indexOf(".")>=0) return aDate;
		
		// validate all other stuff
		var data = inString.split("@");
		if (data.length != 3) return aDate;
		for (i=0; i<3; i++) {
			data[i] = parseFloat(data[i]);
			if(isNaN(data[i])) return aDate;
		}
		
		if (inFormat.substring(0,1).toUpperCase() == "D"){
			aDate.setFullYear(formatYear(data[2]));
			aDate.setMonth(data[1]-1);
			aDate.setDate(data[0]);
		} else if (inFormat.substring(0,1).toUpperCase() == "Y"){
			aDate.setFullYear(formatYear(data[0]));
			aDate.setMonth(data[1]-1);
			aDate.setDate(data[2]);
		} else if (inFormat.substring(0,1).toUpperCase() == "M"){
			aDate.setFullYear(formatYear(data[2]));
			aDate.setMonth(data[0]-1);
			aDate.setDate(data[1]);
		}
		aDate.setHours(0);
		aDate.setMinutes(0);
		aDate.setSeconds(0);
		aDate.setMilliseconds(0);
		
		return aDate;
	}
	
	var formatYear = function(inYear) {
		if(inYear < 99){
			if (inYear >= 30)
				inYear += 1900;
			else
				inYear += 2000;
		}	
		return inYear;
	}
	
	var formatDate = function(inDate) {
		var day = inDate.getDate() < 10 ? "0" + inDate.getDate() : inDate.getDate();
		var month = inDate.getMonth()+1 < 10 ? "0" + (inDate.getMonth()+1) : (inDate.getMonth()+1);
		var year = inDate.getFullYear();
		
		return day + "." + month + "." + year;
	}
	
	var getEventSource = function(inEvent) {
		var event = inEvent ? inEvent : window.event;
		
		if(event.target) {
			return event.target;
		} else if(event.srcElement) {
			if(event.srcElement.nodeType == 3)
				return event.srcElement.parentNode;
			else
				return event.srcElement;
		}
	}
	
	var getEventTarget = function(inEvent) {
		var event = inEvent ? inEvent : window.event;
		
		if(event.type == "mouseover") {
			return event.relatedTarget ? event.relatedTarget : event.fromElement;
		} else if(event.type == "mouseout") {
			if(event.target) {
				return event.target;
			} else if(event.srcElement) {
				if(event.srcElement.nodeType == 3) return event.srcElement.parentNode;
				else return event.srcElement;
			}
		} else {
			return null;
		}
	}
	
	
	/*************************************************************/
	/*                      EVENT HANDLING                       */
	/*************************************************************/
	
	var onDocumentMouseUp = function() {
		isDragging = false;
	}
	
	var onTableMouseDown = function() {
		return false;
	}
	
	var onCellMouseDown = function(inEvent) {
		if(isSelectable == false) return;
		
		var event = inEvent ? inEvent : window.event;
		var source = getEventSource(inEvent);
		
		if(event.shiftKey == true && myLastSelection) {
			me.setSelection(myLastSelection.date, source.date);
		} else {
			source.className = "calendar-day-selected";
			me.clearSelection();
			mySelection.push(source);
			
			myLastSelection = source;
			isDragging = true;
		}
	}
	
	var onCellMouseOver = function(inEvent) {
		var date = getEventSource(inEvent).date;
		
		if(isDragging == true) {
			me.setSelection(myLastSelection.date, date);
		}
		
		if(isEventDate(date) == true) {
			myEventWindow.innerHTML = getEventList(date);
			
			getEventSource(inEvent).onmousemove = onCellMouseMove;
			document.body.appendChild(myEventWindow);
		}
	}
	
	var onCellMouseOut = function(inEvent) {
		try {
			getEventSource(inEvent).onmousemove = null;
			document.body.removeChild(myEventWindow);
		} catch(Exception) {}
	}
	
	var onCellMouseMove = function(inEvent) {
		var event = inEvent ? inEvent : window.event;
		
		myEventWindow.style.left = event.clientX + "px";
		myEventWindow.style.top = (event.clientY + 24) + "px";
	}
	
	var onCellMouseUp = function(inEvent) {
		if(isSelectable == false) return;
		
		me.setSelection(myLastSelection.date, getEventSource(inEvent).date);
	}
	
	createCalendar(inFrom, inTo, inElementID);
}