// Combined Calendar-class 
// 
// Copyright 2007-2010 René Lønstrup @ RLdesign.dk
// 
// May not be used commercially without permission from the author.
// 
// Version: 1.4.0.4
//
//
// CHANGELOG:
//
// 22-06-2010
//     1.4.0.3 -> 1.4.0.4
//           Fixed a bug in the static Init function which occured when the associated control was not an input control
// 23-01-2010
//     1.4.0.2 -> 1.4.0.3
//           Made a few changes to the static Init function to allow more flexible configuration of calendar events
// 22-01-2010
//     1.4.0.1 -> 1.4.0.2
//           Fixed a bug where the table cell for the selected date did not receive a class
// 04-11-2009
//     1.4.0.0 -> 1.4.0.1
//           Added the property ShowDisabledDates, allowing disabled dates to be shown as clear text
// 04-11-2009
//     1.3.0.0 -> 1.4.0.0
//           Added the possibility to allow filtering dates according to whether its allowed to select past, future or all dates
// 05-10-2008
//     1.2.0.1 -> 1.3.0.0
//           Added a static class for Initiating the calendar without hundreds of custom init code
// 05-10-2008
//     1.2.0.0 -> 1.2.0.1
//           Fixed a bug in the onChangeMonth event, meaning the cycle month/year buttons didn't work at all
// 05-10-2008
//     1.1.3.2 -> 1.2.0.0
//           Added ability to change text of cycle month/year buttons
//           Added possibility to show selected month/year without selection-ability
//           Added CSS-classes to the calendar cycle controls, adding to the styling possibilities
//           Added switch to indicate whether the cycle month/year buttons shall be rendered as form buttons or links
//           Added possibility to include an amount of dates that shall be marked in the calendar (not selected)
//           Added new event onChangeMonth, which fires when the user change month or year via the cycle month/year buttons
//           Rearranged all the functions so they hopefully are easier to find
//           Fixed a bug where the selected date and the current date would be highlighted even though they were in another month than the one shown
// 01-07-2008
//     1.1.3.1 -> 1.1.3.2
//           Fixed bugs in function RemoveSelectedDate and related functions
//			 Fixed a couple of small memory holes
// 27-06-2008
//     1.1.3.0 -> 1.1.3.1
//           Altered constructor of class to allow setting a flag so no default date is set
//			 Added function RemoveSelectedDate to enable clearing the calendar
// 24-03-2008
//     1.1.2.3 -> 1.1.3.0
//           Altered constructor of class to always require a settings object - which now also needs to include the ID of the container
// 17-03-2008
//     1.1.2.2 -> 1.1.2.3
//           Fixed a small bug in the Close-button, where it didn't cancel the click-event properly
// 14-03-2008
//     1.1.2.1 -> 1.1.2.2
//           Fixed Close-button so that it actually works!
// 13-03-2008
//     1.1.2.0 -> 1.1.2.1
//           Fixed several bugs relating to focusing and blurring of the calendar
// 09-03-2008
//     1.1.1.1 -> 1.1.2.0
//           Added Timeout logic to handle mouseover/-out displaying of calendar
//           Added support for settings-object in instantiation-arguments
// 27-02-2008
//     1.1.1.0 -> 1.1.1.1
//           Updated RLdesign.Events to newest version 1.0.0.1
// 20-02-2008
//     1.1.0.4 -> 1.1.1.0
//           Added ability to add a titlebar complete with minimize, maximize and close control-buttons
//     1.1.0.3 -> 1.1.0.4
//           Change header-tag from td to th because it is syntactically more correct
//     1.1.0.2 -> 1.1.0.3
//           Added GetDate() method for getting the selected date
//     1.1.0.1 -> 1.1.0.2
//           Change internal variable name id to ContainerID for clearer indication of variables usage
//

if (!RLdesign) {
	var RLdesign = function() { };
}

if (!RLdesign.Utils) {
	RLdesign.Utils = function() {
		return {
			// returns a crossbrowser event object
			DefineEvent: function(e) {
				return e || window.event;
			},

			// returns the target for the event in question, typically a click-event
			// ie. in the case of a click-event, the target is the element being clicked on
			DefineEventTarget: function(e) {
				var ev = RLdesign.Utils.DefineEvent(e);
				var element = ev.srcElement || ev.target;
				if (element.nodeType == 3) element = element.parentNode; // defeat Safari bug
				return element;
			},

			// tries to return an object from an arbitrary argument that can be a string or an object
			// if successfully finding an object, either from a search for the object-id or the object itself, 
			// returns the object. If unsuccessful, returns null
			DefineObject: function(el) {
				var elem = null;
				if (typeof(el) == "object") elem = el;
				else if (document.getElementById(el)) elem = document.getElementById(el);
				else if (typeof(el) == "string" && (el.indexOf("(") || el.indexOf("["))) eval("elem = " + el);
				else if (typeof(el) == "string") eval("elem = " + el);
				return elem;
			},

			// returns a crossbrowser return code designed to prevent further events i mozilla browser, if need be
			DefineReturnCode: function (e,r) {
				var ev = RLdesign.Utils.DefineEvent(e);
				if (r == false) {
					if (ev.stopPropagation) ev.stopPropagation();
					else if (ev.cancelBubble) ev.cancelBubble = true;
					if (ev.preventDefault) ev.preventDefault();
					else if (ev.returnValue) ev.returnValue = false;
				}
				else {
					if(typeof(ev.returnValue) == "string") ev.returnValue = r;
				}
				return r;
			},

			// returns the keycode for the key that was pressed during the event		
			DefineEventKeyCode: function(e) {
				var ev = RLdesign.Utils.DefineEvent(e);
				var keycode = 0;
				if (ev.which) keycode = ev.which; // Netscape4
				else if (ev.keyCode) keycode = ev.keyCode; // IE
				else if (ev.charCode) keycode = ev.charCode; // Gecko
				return keycode;
			},


			// removes any child nodes of the given object
			ClearChildElements: function(obj) {
				var elem = RLdesign.Utils.DefineObject(obj);
				if (elem == null) return;
				if (elem.hasChildNodes()) {
					while (elem.childNodes.length >= 1) {
						elem.removeChild(elem.firstChild);       
					} 
				}
			},
			
			// clears the specified global timeout variable (specified as a string), if exists
			ClearTimeout: function(sTimeout) {
				var bTimeoutExist = !!window[sTimeout];
				if (bTimeoutExist) window.clearTimeout(window[sTimeout]);
			},
		
			// returns a boolean representation of whether the given argument is numeric
			IsNumeric: function(sInput) {
				var sValidChars = "0123456789,.-";
				sInput = sInput.toString();
				if (sInput.length == 0) return false;
				var bResult = true;
				for (var i = 0; i < sInput.length; i++) {
					cChar = sInput.charAt(i);
					if (sValidChars.indexOf(cChar) == -1) {
						bResult = false;
						break;
					}
				}
				return bResult;
			},

			// creates a given html element in memory and returns the element
			MakeElement: function(tag, attrs, text) {
				var e = document.createElement(tag);
				if (attrs) {
					for (key in attrs) {
						if (typeof(attrs[key]) == "function") continue; // defeat bug caused by adding custom functions to arrays
						if (key == "class") { e.className = attrs[key]; }
						else if (key == "id") { e.id = attrs[key]; }
						else { e.setAttribute(key, attrs[key]); }
					}
				}
				if (text) e.appendChild(document.createTextNode(text));
				return e;
			},
			
			// evaluates whether the given argument object is an array, returning a boolean
			IsArray: function(a) {
				return (a && a.length && typeof(a) != "string" && !a.tagName && !a.alert && typeof(a[0]) != "undefined");
			},

			// evaluates whether the given argument object is an object, returning a boolean
			IsObject: function(o) {
				return (o && o.length == undefined && typeof(o) == "object" && !o.alert);
			}
		}
	} ();
}


if (!RLdesign.Arrays) {
	RLdesign.Arrays = function() {
		return {
			// Removes the index from the array
			// Usage:
			// RLdesign.Arrays.RemoveElement(myArray, 3);
			RemoveElement: function(haystack, index) {
				var size = haystack.length;
				if (parseInt(index) != "NaN" && index >= 0 && index < size) {
					for (i = index; i < size; i++) {
						haystack[i] = haystack[i + 1];
					}
					haystack.length = size - 1;
				}
				return haystack;
			},


			// Searches for needle in array (haystack), returns true if found, false if not
			// Usage:
			// var found = RLdesign.Arrays.InArray(myArray, "foo");
			InArray: function(haystack, needle) {
				for (var i = 0; i < haystack.length; i++) {
					if (haystack[i] === needle) return true;
				}
				return false;
			},


			// Equivalent to InArray, but searches associative arrays instead
			InAssociativeArray: function(haystack, needle) {
				if (haystack[needle] != undefined) return true;
				return false;
			},


			// Returns the index of needle in array (haystack) if found, otherwise returns null
			// Usage:
			// var index = RLdesign.Arrays.GetRowIndex(myArray, "foo");
			GetRowIndex: function(haystack, needle) {
				var size = haystack.length;
				var index = null;
				for (var i = 0; i < size; i++) {
					if (haystack[i] == needle) {
						index = i;
						break;
					}
				}
				return index;
			},


			// Returns true if array (haystack) is empty, or false if not
			// Usage:
			// var empty = RLdesign.Arrays.IsEmpty(myArray);
			// if (!empty) {  }
			IsEmpty: function(haystack) {
				var len = haystack.length;
				if (len == 0) {
					for (var key in this) {
						len++;
					}
				}
				return (len > 0) ? false : true;
			},


			// Searches for an array (needle) inside another array (haystack) returning true if found, false if not
			// Usage:
			// var exists = RLdesign.Arrays.FindArray(myArray, myInnerArray);
			// if (exists) {  }
			FindArray: function(haystack, needle) {
				var iNeedleSize = needle.length;
				var iSize = haystack.length;
				var bFound = false;
				var iFoundIndex = null;
				for (var i = 0; i < iSize; i++) {
					if (!bFound && RLdesign.Arrays.IsArray(haystack[i]) && haystack[i].length == iNeedleSize) {
						for (var y = 0; y < iNeedleSize; y++) {
							if (needle[y] == haystack[i][y]) {
								bFound = true;
							}
							else {
								bFound = false;
								break;
							}
						}
					}
					if (bFound) {
						iFoundIndex = i;
						break;
					}
				}
				return bFound;
			},


			// evaluates whether the given argument object is an array, returning a boolean
			IsArray: function(a) {
				return (a && a.length && typeof (a) != "string" && !a.tagName && !a.alert && typeof (a[0]) != "undefined");
			}
		}
	} ();
}


if (!RLdesign.Events) {
	// events hjælpe klasse
	RLdesign.Events = function() {
		var bPageLoaded = false;
		var aHandlers = new Array();
		var aStartASAPCollection = new Array();
		var aEventsToBeConnected = new Array();
		var aEventsConnected = new Array();
		var iTIMEOUT = 50;
		return {
			// attempts to run an userdefined function (fn) when the specified object (obj) has been loaded in memory
			StartWhenLoaded: function(obj, fn) {
				// fn should be referenced as a string representation of the functions name
				// if it is not, we will attempt to extract the function name from the function
				if (typeof(fn) != "string") {
					var sFn = fn.toString();
					var iStart = sFn.indexOf("function") + 9;
					var iEnd = sFn.indexOf("(");
					fn = sFn.substring(iStart,iEnd);
					if (fn == "") return false;
				}
				// if obj was referenced as a string, it will be checked for the correct syntax for this use
				if (typeof(obj) == "string" && obj.indexOf("'")) {
					obj = obj.replace(/'/g,"\"");
				}
				// saves info about what we are about to do
				var aStartASAP = new Array(obj,fn);
				if (!aStartASAPCollection.findArray(aStartASAP)) {
					aStartASAPCollection.push(aStartASAP);
				}
				// tries to extract an object from obj
				var elem = RLdesign.Utils.DefineObject(obj);
				if (elem) {
					// if object referenced was an array it will be iterated through 
					// to see if any of the objects is ready loaded and if so,
					// the function will be executed
					// and information on the objects and the function will be saved
					if (RLdesign.Utils.IsArray(elem)) {
						var elemA = null;
						for (var i = 0; i < elem.length; i++) {
							aEventsToBeConnected = new Array(elem[i],fn);
							elemA = RLdesign.Utils.DefineObject(obj);
							if (elemA) {
								if (!aEventsConnected.findArray(aEventsToBeConnected)) {
									aEventsConnected.push(aEventsToBeConnected);
									eval(fn + "(elem[i])");
								}
							}
						}
					}
					// if obj is a single object, the function will be executed directly
					// and information on the object and the function will be saved
					else if (RLdesign.Utils.IsObject(elem)) {
						aEventsToBeConnected = new Array(elem,fn);
						if (!aEventsConnected.findArray(aEventsToBeConnected)) {
							aEventsConnected.push(aEventsToBeConnected);
							eval(fn + "(elem)");
						}
					}
				}
				// recursive runs until page has been loaded
				if (!bPageLoaded) {
					setTimeout("RLdesign.Events.StartWhenLoaded('" + obj + "','" + fn + "')", iTIMEOUT);
				}
			},
	
	
			// appends an user-defined event handler of the specified type to the object (el)
			SetEventHandler: function(obj,type,funktion) {
				// if the object is an array, we sets the eventhandler to every contained element, one at a time
				if (RLdesign.Utils.IsArray(obj)) {
					var ok = true;
					for (var i = 0; i < el.length; i++) {
						ok = (this.SetEventHandler(obj[i],type,funktion) && ok);
					}
					return ok;
				}
				// if obj was referenced as a string, it will be checked for the correct syntax for this use
				// replaces ' (single pling) with " (quotation mark)
				if (typeof(obj) == "string" && obj.indexOf("'")) {
					obj = obj.replace(/'/g,"\"");
				}
				// tries to extract an object from obj
				var elem = RLdesign.Utils.DefineObject(obj);
				// saves event-handler information to array
				if (elem == null) {
					var aHandler = new Array(obj, type, funktion);
					aHandlers.push(aHandler);
					return false;
				}
				// wraps function in order to provide easy cross-browser event-trapping
				var fnWrapped = function(e) {
					return funktion(RLdesign.Utils.DefineEvent(e));
				};
				// adding event-handler to element
				if (elem.addEventListener) { elem.addEventListener(type,fnWrapped,false); }
				else if (elem.attachEvent) { elem.attachEvent("on"+type,fnWrapped); }
				else {
					var ontype = "on" + type;
					var old = elem[ontype];
					if (old) {
						elem[ontype] = function(event) {
							var res = old(event);
							return fnWrapped(event) && res;
						}
					}
					else { elem[ontype] = fnWrapped; }
				}
				return true;
			},


			RemoveEventHandler: function(obj, sEventType, funktion, bOptionalAvoidWrapping) {
				if (!bOptionalAvoidWrapping || bOptionalAvoidWrapping == undefined) bOptionalAvoidWrapping = false;
				else bOptionalAvoidWrapping = true;
				// if the object is an array, we remove the eventhandler from every contained element, one at a time
				if (RLdesign.Utils.IsArray(obj)) {
					var ok = true;
					for (var i = 0; i < el.length; i++) {
						ok = (this.RemoveEventHandler(obj[i],sEventType,funktion) && ok);
					}
					return ok;
				}
				// if obj was referenced as a string, it will be checked for the correct syntax for this use
				// replaces ' (single pling) with " (quotation mark)
				if (typeof(obj) == "string" && obj.indexOf("'")) {
					obj = obj.replace(/'/g,"\"");
				}
				// tries to extract an object from obj
				var elem = RLdesign.Utils.DefineObject(obj);
				// if object doesn't exists yet, returns because then it isn't possible to remove an eventhandler that doesn't exist
				if (elem == null) return false;
				// wraps function in order to provide easy cross-browser event-trapping
				var fnWrapped = null;
				if (!bOptionalAvoidWrapping) {
					fnWrapped = function(e) {
						return funktion(RLdesign.Utils.DefineEvent(e));
					};
				}
				else {
					fnWrapped = (typeof(funktion) == "string") ? eval(funktion) : funktion;
				}
				// adding event-handler to element
				if (elem.addEventListener) { elem.removeEventListener(sEventType,fnWrapped,false); }
				else if (elem.attachEvent) { elem.detachEvent("on"+sEventType,fnWrapped); }
				else {
					var ontype = "on" + sEventType;
					elem[ontype] = "";
				}
				return true;
			},

	
			// tries to add event-handlers to objects as soon as they are available to the DOM
			PreloadHandler: function() {
				var aDeletableHandlers = new Array();
				for (var i = 0; i < aHandlers.length; i++) {
					var aH = aHandlers[i];
					if (aH) {
						var oEl = RLdesign.Utils.DefineObject(aH[0]);
						if (oEl) {
							this.SetEventHandler(oEl, aH[1], aH[2]);
							aDeletableHandlers.push(i);
						}
					}
				}
				for (i = aDeletableHandlers.length; i > 0; i--) {
					aHandlers.removeElement(aDeletableHandlers[i-1]);
				}
				if (!bPageLoaded) {
					setTimeout("RLdesign.Events.PreloadHandler()",iTIMEOUT);
				}
			},
	
	
			// sets a flag telling the rest of the class that the page has been loaded
			// also flushes all obsolete data from memory
			_load: function() {
				bPageLoaded = true;
				if (!RLdesign.Arrays.IsEmpty(aStartASAPCollection)) {
					for (var i = 0; i < aStartASAPCollection.length; i++) {
						RLdesign.Events.StartWhenLoaded(aStartASAPCollection[i][0],aStartASAPCollection[i][1]);
					}
				}
				if (!RLdesign.Arrays.IsEmpty(aHandlers)) {
					RLdesign.Events.PreloadHandler();
				}
			}
		};
	} ();
	RLdesign.Events.SetEventHandler(window, "load", RLdesign.Events._load);
	RLdesign.Events.PreloadHandler();
}















RLdesign.Calendar = function(SettingsObject) {
	// Markup specific
	this.CellSpacing = 0; // cellspacing value on calendar table
	this.CellPadding = 0; // cellpadding value on calendar table
	this.AddFormTags = false; // whether to add a form around form-elements such as input or select

	// event handler placeholders
	this.onSelectDate = null; // selectdate event, fires when selecting a date in the calendar
	this.onChangeMonth = null; // changemonth event, fires when using the previous year/month and next year/month buttons

	this.ShowControlsAfterTable = false;
	this.UseControls = true; // whether to show controls for selecting month/year

	this.ShowWeekNo = false; // whether to show the week no. column
	this.ShowOuterMonthDates = true; // whether to allow clicking dates outside the currently selected month
	this.ShowDisabledDates = true; // whether to show disabled dates

	this.ShowTitleBar = false;

	this.ShowMinimizeButton = false;
	this.ShowMaximizeButton = false;
	this.ShowCloseButton = false;

	this.ShowCycleMonthButtons = true;
	this.ShowCycleYearButtons = true;
	this.ShowSelectMonthControl = true;
	this.ShowSelectYearControl = true;

	this.ShowStaticSelectedMonth = false; // whether to show the selected month as static text
	this.ShowStaticSelectedYear = false; // whether to show the currently selected year as static text

	// Language specific
	this.WeekCaption = "week"; // caption for the week no. column
	this.TitleText = "Calendar"; // caption for the calendar control
	this.NextMonthText = ">"; // Cycle month forward button text
	this.NextYearText = ">>"; // Cycle year forward button text
	this.PreviousMonthText = "<"; // Cycle month back button text
	this.PreviousYearText = "<<"; // Cycle year back button text

	this.ShortMonthLength = 3;
	this.ShortDayLength = 2;

	this.RenderCycleButtonsAs = "FormButtons"; // other valid options: Link

	this.AllowSelections = "All"; // other valid options: Past, Future


	this._hasbeenbuilt = false;
	this._adayspermonth = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
	var _adaynamesshort;
	var _adaynameslong;
	var _amonthnameslong;
	var _amonthnamesshort;
	var _selecteddate;
	this._dateselected = false;
	var _day;
	var _month;
	var _year;
	var _oCalendarContainer;
	this._displaystatus = true;
	this._aCalendar = null;
	this._hasFocus = false;

	this._markeddates = new Array();

	// contructor
	this._init = function(SettingsObject) {


		if (!SettingsObject || !SettingsObject.ContainerID || !RLdesign.Utils.DefineObject(SettingsObject.ContainerID)) {
			//alert("Error:\n\nNo ID or ID for non-existing element was given.\n\nCancelling creation of calendar. Please revise your script.");
			return;
		}


		// registration of calendar container and eventsetting		
		_oCalendarContainer = RLdesign.Utils.DefineObject(SettingsObject.ContainerID);
		window["RLdesign_Calendar_" + _oCalendarContainer.id] = this;
		RLdesign.Events.SetEventHandler(_oCalendarContainer, "mouseover", function() { window["RLdesign_Calendar_" + _oCalendarContainer.id]._hasFocus = true; window["RLdesign_Calendar_" + _oCalendarContainer.id].ClearCalendarTimeout(); });
		//RLdesign.Events.SetEventHandler(_oCalendarContainer, "mouseout", function() { window["RLdesign_Calendar_" + _oCalendarContainer.id]._hasFocus = false; } );
		//RLdesign.Events.SetEventHandler(_oCalendarContainer, "blur", function() { window["RLdesign_Calendar_" + _oCalendarContainer.id]._hasFocus = false; window["RLdesign_Calendar_" + _oCalendarContainer.id].SetCalendarTimeout(1000); } );
		RLdesign.Events.SetEventHandler(_oCalendarContainer, "click", function() { window["RLdesign_Calendar_" + _oCalendarContainer.id]._hasFocus = true; window["RLdesign_Calendar_" + _oCalendarContainer.id].ClearCalendarTimeout(); });
		//RLdesign.Events.SetEventHandler(document, "click", function() { window["RLdesign_Calendar_" + _oCalendarContainer.id]._hasFocus = false; window["RLdesign_Calendar_" + _oCalendarContainer.id].SetCalendarTimeout(10); } );

		// initial creation of day and month names
		this.SetDayNames(new Array("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"));
		this.SetMonthNames(new Array("january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"));


		if (SettingsObject.ShowControlsAfterTable !== undefined) this.ShowControlsAfterTable = SettingsObject.ShowControlsAfterTable;
		if (SettingsObject.TitleText !== undefined) this.TitleText = SettingsObject.TitleText;
		if (SettingsObject.WeekCaption !== undefined) this.WeekCaption = SettingsObject.WeekCaption;
		if (SettingsObject.ShowWeekNo !== undefined) this.ShowWeekNo = SettingsObject.ShowWeekNo;
		if (SettingsObject.AddFormTags !== undefined) this.AddFormTags = SettingsObject.AddFormTags;
		if (SettingsObject.CellSpacing !== undefined) this.CellSpacing = SettingsObject.CellSpacing;
		if (SettingsObject.CellPadding !== undefined) this.CellPadding = SettingsObject.CellPadding;
		if (SettingsObject.ShowOuterMonthDates !== undefined) this.ShowOuterMonthDates = SettingsObject.ShowOuterMonthDates;
		if (SettingsObject.ShowCycleMonthButtons !== undefined) this.ShowCycleMonthButtons = SettingsObject.ShowCycleMonthButtons;
		if (SettingsObject.ShowCycleYearButtons !== undefined) this.ShowCycleYearButtons = SettingsObject.ShowCycleYearButtons;
		if (SettingsObject.ShowSelectMonthControl !== undefined) this.ShowSelectMonthControl = SettingsObject.ShowSelectMonthControl;
		if (SettingsObject.ShowSelectYearControl !== undefined) this.ShowSelectYearControl = SettingsObject.ShowSelectYearControl;
		if (SettingsObject.ShowTitleBar !== undefined) this.ShowTitleBar = SettingsObject.ShowTitleBar;
		if (SettingsObject.ShowMinimizeButton !== undefined) this.ShowMinimizeButton = SettingsObject.ShowMinimizeButton;
		if (SettingsObject.ShowMaximizeButton !== undefined) this.ShowMaximizeButton = SettingsObject.ShowMaximizeButton;
		if (SettingsObject.ShowCloseButton !== undefined) this.ShowCloseButton = SettingsObject.ShowCloseButton;
		if (SettingsObject.NextMonthText !== undefined) this.NextMonthText = SettingsObject.NextMonthText;
		if (SettingsObject.NextYearText !== undefined) this.NextYearText = SettingsObject.NextYearText;
		if (SettingsObject.PreviousMonthText !== undefined) this.PreviousMonthText = SettingsObject.PreviousMonthText;
		if (SettingsObject.PreviousYearText !== undefined) this.PreviousYearText = SettingsObject.PreviousYearText;
		if (SettingsObject.ShowStaticSelectedMonth !== undefined) this.ShowStaticSelectedMonth = SettingsObject.ShowStaticSelectedMonth;
		if (SettingsObject.ShowStaticSelectedYear !== undefined) this.ShowStaticSelectedYear = SettingsObject.ShowStaticSelectedYear;
		if (SettingsObject.ShortDayLength !== undefined) this.ShortDayLength = SettingsObject.ShortDayLength;
		if (SettingsObject.ShortMonthLength !== undefined) this.ShortMonthLength = SettingsObject.ShortMonthLength;
		if (SettingsObject.RenderCycleButtonsAs !== undefined) this.RenderCycleButtonsAs = SettingsObject.RenderCycleButtonsAs;
		if (SettingsObject.MarkedDates !== undefined) this._markeddates = SettingsObject.MarkedDates;
		if (SettingsObject.AllowSelections !== undefined) this.AllowSelections = SettingsObject.AllowSelections;
		if (SettingsObject.ShowDisabledDates !== undefined) this.ShowDisabledDates = SettingsObject.ShowDisabledDates;

		if (SettingsObject.DayNames !== undefined) this.SetDayNames(SettingsObject.DayNames);
		if (SettingsObject.MonthNames !== undefined) this.SetMonthNames(SettingsObject.MonthNames);

		if (SettingsObject.DefaultDate !== undefined && SettingsObject.DefaultDate !== null) {
			_dateselected = true;
			this._setdate(SettingsObject.DefaultDate.getFullYear(), (SettingsObject.DefaultDate.getMonth()) + 1, SettingsObject.DefaultDate.getDate());
		}
		else {
			// setting todays date as default

			if (SettingsObject.SetDefaultDate != undefined && SettingsObject.SetDefaultDate == false) {
				_dateselected = false;
			}
			else {
				_dateselected = true;
			}

			var today = new Date();
			this._setdate(today.getFullYear(), today.getMonth() + 1, today.getDate());
		}
	}
	// builds the calendar
	this.BuildCalendar = function() {
		if (!_oCalendarContainer) return;
		var lastrow = false;
		var d = 0;
		var oInput = null;
		var oTable = null;
		var oTableBody = null;
		var oTableHead = null;
		var oTableRow = null;
		var oTableCell = null;
		var oDateLink = null;
		var aParams = null;
		var firstofmonth = null;
		var days = 0;
		var othermonth = null;
		var outputday = null;
		var aWeek = null;
		this.ShowWeekNo = !!(this.ShowWeekNo);
		this.CellPadding = (RLdesign.Utils.IsNumeric(this.CellPadding)) ? parseInt(this.CellPadding) : 0;
		this.CellSpacing = (RLdesign.Utils.IsNumeric(this.CellSpacing)) ? parseInt(this.CellSpacing) : 0;
		this.AddFormTags = !!(this.AddFormTags);
		this.UseControls = !!(this.UseControls);
		this.ShowControlsAfterTable = !!(this.ShowControlsAfterTable);
		this.ShowTitleBar = !!(this.ShowTitleBar);

		// make sure the root element is empty
		RLdesign.Utils.ClearChildElements(_oCalendarContainer);

		if (this.ShowTitleBar === true) {
			this._buildtitlebar();
		}

		// controls
		if (this.UseControls === true && this.ShowControlsAfterTable === false) {
			this._buildcalendarcontrols();
		}
		// end controls

		// thead
		oTableHead = RLdesign.Utils.MakeElement("thead");
		oTableRow = RLdesign.Utils.MakeElement("tr");
		if (this.ShowWeekNo) {
			aParams = new Array();
			aParams["class"] = "CalendarWeekHeader";
			oTableCell = RLdesign.Utils.MakeElement("th", aParams, this.WeekCaption);
			oTableRow.appendChild(oTableCell);
		}
		for (d = 0; d < 7; d++) {
			aParams = new Array();
			aParams["class"] = "CalendarHeader";
			oTableCell = RLdesign.Utils.MakeElement("th", aParams, _adaynamesshort[d]);
			oTableRow.appendChild(oTableCell);
		}
		oTableHead.appendChild(oTableRow);

		aParams = new Array();
		aParams["cellspacing"] = this.CellSpacing;
		aParams["cellpadding"] = this.CellPadding;
		oTable = RLdesign.Utils.MakeElement("table", aParams);
		oTable.appendChild(oTableHead);
		// end thead

		// tbody
		oTableBody = RLdesign.Utils.MakeElement("tbody");
		oTableBody.id = "RLdesign_Calendar_" + _oCalendarContainer.id + "tbody";

		oTableBody.appendChild(this._buildcalendarbody());

		oTable.appendChild(oTableBody);
		// end tbody
		_oCalendarContainer.appendChild(oTable);

		// controls
		if (this.UseControls === true && this.ShowControlsAfterTable === true) {
			this._buildcalendarcontrols();
		}
		// end controls

		this._hasbeenbuilt = true;
		_dateselected = true;
		_aCalendar = oTable = oTableBody = oTableRow = oTableCell = oTableHead = oDateLink = null; // cleanup
	}



	/* REGION: EVENTHANDLERS */

	// internal eventhandler to ensure that the calendar form doesn't accidentally submit the page
	this._handleformsubmit = function(oEvent) {
		return RLdesign.Utils.DefineReturnCode(oEvent, false);
	}
	// internal eventhandler to ensure that the calendar form isn't reset when resetting the page form
	this._handlereset = function(oEvent) {
		var today = new Date();
		window["RLdesign_Calendar_" + _oCalendarContainer.id].SetDate(today.getFullYear(), today.getMonth() + 1, today.getDate());
		//return false;
		//return RLdesign.Utils.DefineReturnCode(oEvent, false);
	}
	// internal eventhandler for user selection of a year
	// triggered when the user presses the enter-key while having focus on the year inputfield
	// changes the internally selected year and redraws the calendar to match the selected month of the new year
	this._onselectyear = function(oEvent) {
		var obj = RLdesign.Utils.DefineEventTarget(oEvent);
		var iKeyCode = RLdesign.Utils.DefineEventKeyCode(oEvent);
		var testNumeric = (iKeyCode >= 96 && iKeyCode <= 105);
		var testNumeric2 = (iKeyCode >= 48 && iKeyCode <= 57);
		var testModifiers1 = (iKeyCode === 8 || iKeyCode === 9);
		var testModifiers2 = (iKeyCode >= 16 && iKeyCode <= 18);
		var testModifiers3 = (iKeyCode >= 33 && iKeyCode <= 40);
		var testModifiers4 = (iKeyCode === 45 || iKeyCode === 46);
		var testModifiers5 = (iKeyCode === 144 || iKeyCode === 145);
		var testFButtons = (iKeyCode >= 112 && iKeyCode <= 123);


		if (iKeyCode != 13 && (testModifiers1 || testModifiers2 || testModifiers3 || testModifiers4 || testModifiers5 || testFButtons || testNumeric || testNumeric2)) return true; // if a numeric value and not enter
		//		if (iKeyCode === 13) {
		if (RLdesign.Utils.IsNumeric(obj.value) && obj.value >= 0 && obj.value <= 2500) {
			_year = parseInt(obj.value);
		}
		window["RLdesign_Calendar_" + _oCalendarContainer.id].UpdateCalendar();
		//obj.blur(); // omitted for causing an exception in firefox 1.5.0.12
		//		}
		return RLdesign.Utils.DefineReturnCode(oEvent, false);
	}
	// internal eventhandler for user selection of a year
	// triggered when the user presses the enter-key while having focus on the year inputfield
	// changes the internally selected year and redraws the calendar to match the selected month of the new year
	this._onselectyearblur = function(oEvent) {
		var obj = RLdesign.Utils.DefineEventTarget(oEvent);
		if (RLdesign.Utils.IsNumeric(obj.value) && obj.value >= 0 && obj.value <= 2500) {
			_year = parseInt(obj.value);
		}
		window["RLdesign_Calendar_" + _oCalendarContainer.id].UpdateCalendar();
	}
	// internal eventhandler for user selection of a month
	// triggered when the user changes the month in the dropdown selection box
	// changes the internally selected month and redraws the calendar to match the newly selected month
	this._onselectmonth = function(oEvent) {
		var obj = RLdesign.Utils.DefineEventTarget(oEvent);
		var selectIndex = obj.selectedIndex;
		if (selectIndex == -1 || selectIndex > obj.options.length) return true;

		var selectedmonth = obj.options[selectIndex].value;
		_month = parseInt(selectedmonth);
		//obj.blur();
		window["RLdesign_Calendar_" + _oCalendarContainer.id].UpdateCalendar();
		return true;
	}

	// internal eventhandler - wraps userdefined handler set on onChangeMonth
	this._onchangemonth = function() {
		if (typeof (window["RLdesign_Calendar_" + _oCalendarContainer.id].onChangeMonth) == "function") {
			var selectedmonth = _month;
			var selectedyear = _year;

			var oE = new Object(); // eventargs object
			oE.Month = selectedmonth + 1;
			oE.Year = selectedyear;
			oE.LiteralMonth = _amonthnameslong[_selecteddate.getMonth()];

			var returned = window["RLdesign_Calendar_" + _oCalendarContainer.id].onChangeMonth(oE); // calls userdefined eventhandler
		}
	}

	// internal eventhandler - wraps userdefined handler set on onSelectDate
	this._onselectdate = function(e) {
		var obj = RLdesign.Utils.DefineEventTarget(e);
		if (typeof (window["RLdesign_Calendar_" + _oCalendarContainer.id].onSelectDate) == "function") {
			var selectedday = obj.innerHTML * 1;
			var selectedmonth = _month;
			var selectedyear = _year;
			if (obj.className.indexOf("CalendarOtherMonth") >= 0) {
				if (selectedday > 15) {
					if (selectedmonth > 0) { selectedmonth--; }
					else {
						selectedmonth = 11;
						selectedyear--;
					}
				}
				else {
					if (selectedmonth < 11) { selectedmonth++; }
					else {
						selectedmonth = 0;
						selectedyear++;
					}
				}
			}
			_selecteddate = new Date();
			_selecteddate.setFullYear(selectedyear, selectedmonth, selectedday);
			_dateselected = true;

			var oE = new Object(); // eventargs object
			oE.SelectedDate = _selecteddate;
			oE.LiteralDay = (_selecteddate.getDay() == 0) ? _adaynameslong[6] : _adaynameslong[_selecteddate.getDay() - 1];
			oE.LiteralMonth = _amonthnameslong[_selecteddate.getMonth()];
			oE.EventTarget = obj;

			var returned = window["RLdesign_Calendar_" + _oCalendarContainer.id].onSelectDate(oE); // calls userdefined eventhandler
			if (returned !== false) { // if userdefined eventhandler didn't return false:
				window["RLdesign_Calendar_" + _oCalendarContainer.id].SetDate(selectedyear, selectedmonth + 1, selectedday);
			}
		}
		obj = null;
		return RLdesign.Utils.DefineReturnCode(e, false);
	}

	/* END REGION */






	/* REGION: INTERNAL FUNCTIONS */

	this._buildcalendararray = function() {
		var iDate = 0;
		var dToday = new Date();
		var iFirstOfMonth = new Date(_year, _month, 1);
		var thisDate = null;
		iFirstOfMonth = iFirstOfMonth.getDay();
		if (iFirstOfMonth == 0) iFirstOfMonth = 7;
		var iPrevMonthLength = this._getdayspermonth(_month - 1);
		var iThisMonthLength = this._getdayspermonth(_month);
		// which day of the previous month does the calendar start with?
		var iPrevMonthDay = (iFirstOfMonth > 1) ? iPrevMonthLength - (iFirstOfMonth - 2) : false;
		var iNextMonthDay = 1;
		var bLastrow = false;
		var aCalendar = new Array();
		var aWeek = new Array();
		for (var w = 0; w < 6 && !bLastrow; w++) {
			aCalendar[w] = new Array();
			aCalendar[w]["weeknumber"] = this.GetWeekNo(_year, _month, iDate + 1);
			aWeek = new Array();
			for (var d = 0; d < 7; d++) {
				aWeek[d] = new Object();
				if (w == 0 && d < iFirstOfMonth - 1) {
					aWeek[d]["date"] = iPrevMonthDay;
					aWeek[d]["month"] = (this.GetPrevMonth()).month;
					aWeek[d]["year"] = (this.GetPrevMonth()).year;
					iPrevMonthDay++;
				}
				else if (bLastrow && iDate == iThisMonthLength) {
					aWeek[d]["date"] = iNextMonthDay;
					aWeek[d]["month"] = (this.GetNextMonth()).month;
					aWeek[d]["year"] = (this.GetNextMonth()).year;
					iNextMonthDay++;
				}
				else {
					iDate++;
					aWeek[d]["date"] = iDate;
					aWeek[d]["month"] = _month;
					aWeek[d]["year"] = _year;
				}

				thisDate = new Date(aWeek[d]["year"], aWeek[d]["month"], aWeek[d]["date"]);

				// is this the selected date?
				if (_selecteddate != null && _dateselected == true) {
					aWeek[d]["selected"] = (aWeek[d]["year"] == _selecteddate.getFullYear() && aWeek[d]["month"] == _selecteddate.getMonth() && aWeek[d]["date"] == _selecteddate.getDate());
				}

				if (this._markeddates != null && this._markeddates.length > 0) {
					if (this._inmarkeddates(thisDate)) {
						aWeek[d]["markeddate"] = true;
					}
				}

				// is this date today?
				aWeek[d]["today"] = (aWeek[d]["year"] == dToday.getFullYear() && aWeek[d]["month"] == dToday.getMonth() && aWeek[d]["date"] == dToday.getDate());
				// no more weeks this month?
				if (iDate == iThisMonthLength) bLastrow = true;
			}
			aCalendar[w]["week"] = aWeek;
		}
		return aCalendar;
	}

	this._buildtitlebar = function() {
		this.ShowMinimizeButton = !!(this.ShowMinimizeButton);
		this.ShowMaximizeButton = !!(this.ShowMaximizeButton);
		this.ShowCloseButton = !!(this.ShowCloseButton);


		var aParams = new Array();
		aParams["class"] = "CalendarTitleBar";
		var oTitleBar = RLdesign.Utils.MakeElement("div", aParams);
		oTitleBar.id = "RLdesign_Calendar_" + _oCalendarContainer.id + "TitleBar";

		aParams = new Array();
		aParams["class"] = "CalendarTitle";
		var oTitleTextSpan = RLdesign.Utils.MakeElement("span", aParams, this.TitleText);
		oTitleBar.appendChild(oTitleTextSpan);

		var oTitleControlsSpan = null;
		if (this.ShowMinimizeButton || this.ShowMaximizeButton || this.ShowCloseButton) {
			aParams = new Array();
			aParams["class"] = "CalendarTitleControls";
			oTitleControlsSpan = RLdesign.Utils.MakeElement("span", aParams);

			if (this.ShowMinimizeButton === true) {
				aParams = new Array();
				aParams["class"] = "CalendarTitleControlMinimize";
				aParams["href"] = "#";
				var oMinimizeBtn = RLdesign.Utils.MakeElement("a", aParams);
				var oMiniSpan = RLdesign.Utils.MakeElement("span", null, "_");
				oMinimizeBtn.appendChild(oMiniSpan);
				oTitleControlsSpan.appendChild(oMinimizeBtn);
				oMiniSpan = oMinimizeBtn = null;
			}
			if (this.ShowMaximizeButton === true) {
				aParams = new Array();
				aParams["class"] = "CalendarTitleControlMaximize";
				aParams["href"] = "#";
				var oMaximizeBtn = RLdesign.Utils.MakeElement("a", aParams);
				var oMaxiSpan = RLdesign.Utils.MakeElement("span", null, "-");
				oMaximizeBtn.appendChild(oMaxiSpan);
				oTitleControlsSpan.appendChild(oMaximizeBtn);
				oMaxiSpan = oMaximizeBtn = null;
			}
			if (this.ShowCloseButton === true) {
				aParams = new Array();
				aParams["class"] = "CalendarTitleControlClose";
				aParams["href"] = "#";
				var oCloseBtn = RLdesign.Utils.MakeElement("a", aParams);
				var oCloseSpan = RLdesign.Utils.MakeElement("span", null, "x");

				RLdesign.Events.SetEventHandler(oCloseBtn, "click", window["RLdesign_Calendar_" + _oCalendarContainer.id].CloseCalendar);

				oCloseBtn.appendChild(oCloseSpan);
				oTitleControlsSpan.appendChild(oCloseBtn);
				oCloseSpan = oCloseBtn = null;
			}
		}

		if (oTitleControlsSpan != null) oTitleBar.appendChild(oTitleControlsSpan);
		_oCalendarContainer.appendChild(oTitleBar);

		oTitleTextSpan = oTitleBar = oTitleControlsSpan = null;
	}

	this._buildcalendarcontrols = function() {
		var oForm = null;
		var oButton = null;
		var oFieldset = null;
		var oSelect = null;
		var oInput = null;
		var oSpan = null;
		var oLink = null;
		var oParams = null;

		oFieldset = RLdesign.Utils.MakeElement("fieldset");

		// adds a <form> tag around the calendar, if so allowed (disable this feature when used in an asp.net environment)
		if (this.AddFormTags) {
			oForm = RLdesign.Utils.MakeElement("form");
			oForm.id = "RLdesign_Calendar_" + _oCalendarContainer.id + "ControlsForm";
			RLdesign.Events.SetEventHandler(oForm, "submit", window["RLdesign_Calendar_" + _oCalendarContainer.id]._handleformsubmit);
		}

		// previous year button
		if (this.ShowCycleYearButtons) {
			if (this.RenderCycleButtonsAs == "Link") {
				oParams = new Object();
				oParams["href"] = "javascript:void(0)";
				oLink = RLdesign.Utils.MakeElement("a", oParams);
				oLink.className = "CalendarPrevYear";
				RLdesign.Events.SetEventHandler(oLink, "click", function(e) { window["RLdesign_Calendar_" + _oCalendarContainer.id].PrevYear(e); return false; });
				oSpan = RLdesign.Utils.MakeElement("span", null, this.PreviousYearText);
				oLink.appendChild(oSpan);
				oFieldset.appendChild(oLink);
			}
			else {
				oButton = RLdesign.Utils.MakeElement("button", null, this.PreviousYearText);
				oButton.className = "CalendarPrevYear";
				RLdesign.Events.SetEventHandler(oButton, "click", window["RLdesign_Calendar_" + _oCalendarContainer.id].PrevYear);
				oFieldset.appendChild(oButton);
			}
		}

		// end

		// previous month button
		if (this.ShowCycleMonthButtons) {
			if (this.RenderCycleButtonsAs == "Link") {
				oParams = new Object();
				oParams["href"] = "javascript:void(0)";
				oLink = RLdesign.Utils.MakeElement("a", oParams);
				oLink.className = "CalendarPrevMonth";
				RLdesign.Events.SetEventHandler(oLink, "click", function(e) { window["RLdesign_Calendar_" + _oCalendarContainer.id].PrevMonth(e); return false; });
				oSpan = RLdesign.Utils.MakeElement("span", null, this.PreviousMonthText);
				oLink.appendChild(oSpan);
				oFieldset.appendChild(oLink);
			}
			else {
				oButton = RLdesign.Utils.MakeElement("button", null, this.PreviousMonthText);
				oButton.className = "CalendarPrevMonth";
				RLdesign.Events.SetEventHandler(oButton, "click", window["RLdesign_Calendar_" + _oCalendarContainer.id].PrevMonth);
				oFieldset.appendChild(oButton);
			}
		}
		// end

		// select month dropdown
		if (this.ShowSelectMonthControl) {
			oSelect = RLdesign.Utils.MakeElement("select");
			for (i = 0; i < 12; i++) {
				oSelect.options[i] = new Option(_amonthnameslong[i], i);
				if (_month == i) oSelect.options[i].selected = true;
			}
			oSelect.id = "RLdesign_Calendar_" + _oCalendarContainer.id + "MonthSelect";
			oSelect.className = "CalendarMonthSelect";
			RLdesign.Events.SetEventHandler(oSelect, "change", window["RLdesign_Calendar_" + _oCalendarContainer.id]._onselectmonth);
			RLdesign.Events.SetEventHandler(oSelect, "reset", window["RLdesign_Calendar_" + _oCalendarContainer.id]._handlereset);
			oFieldset.appendChild(oSelect);
		}
		else if (this.ShowStaticSelectedMonth) {
			oSpan = RLdesign.Utils.MakeElement("span");
			oSpan.id = "RLdesign_Calendar_" + _oCalendarContainer.id + "StaticMonth";
			oSpan.className = "CalendarStaticMonth";
			oSpan.innerHTML = _amonthnameslong[_month];
			oFieldset.appendChild(oSpan);
		}

		// select year input
		if (this.ShowSelectYearControl) {
			oInput = RLdesign.Utils.MakeElement("input");
			oInput.type = "text";
			oInput.id = "RLdesign_Calendar_" + _oCalendarContainer.id + "YearInput";
			oInput.className = "CalendarYearInput";
			oInput.value = _year;
			RLdesign.Events.SetEventHandler(oInput, "keydown", window["RLdesign_Calendar_" + _oCalendarContainer.id]._onselectyear);
			RLdesign.Events.SetEventHandler(oInput, "blur", window["RLdesign_Calendar_" + _oCalendarContainer.id]._onselectyearblur);
			RLdesign.Events.SetEventHandler(oInput, "reset", window["RLdesign_Calendar_" + _oCalendarContainer.id]._handlereset);
			oFieldset.appendChild(oInput);
		}
		else if (this.ShowStaticSelectedYear) {
			oSpan = RLdesign.Utils.MakeElement("span");
			oSpan.id = "RLdesign_Calendar_" + _oCalendarContainer.id + "StaticYear";
			oSpan.className = "CalendarStaticYear";
			oSpan.innerHTML = _year;
			oFieldset.appendChild(oSpan);
		}

		// next month button
		if (this.ShowCycleMonthButtons) {
			if (this.RenderCycleButtonsAs == "Link") {
				oParams = new Object();
				oParams["href"] = "javascript:void(0)";
				oLink = RLdesign.Utils.MakeElement("a", oParams);
				oLink.className = "CalendarNextMonth";
				RLdesign.Events.SetEventHandler(oLink, "click", function(e) { window["RLdesign_Calendar_" + _oCalendarContainer.id].NextMonth(e); return false; });
				oSpan = RLdesign.Utils.MakeElement("span", null, this.NextMonthText);
				oLink.appendChild(oSpan);
				oFieldset.appendChild(oLink);
			}
			else {
				oButton = RLdesign.Utils.MakeElement("button", null, this.NextMonthText);
				oButton.className = "CalendarNextMonth";
				RLdesign.Events.SetEventHandler(oButton, "click", window["RLdesign_Calendar_" + _oCalendarContainer.id].NextMonth);
				oFieldset.appendChild(oButton);
			}
		}
		// end

		// next year button
		if (this.ShowCycleYearButtons) {
			if (this.RenderCycleButtonsAs == "Link") {
				oParams = new Object();
				oParams["href"] = "javascript:void(0)";
				oLink = RLdesign.Utils.MakeElement("a", oParams);
				oLink.className = "CalendarNextYear";
				RLdesign.Events.SetEventHandler(oLink, "click", function(e) { window["RLdesign_Calendar_" + _oCalendarContainer.id].NextYear(e); return false; });
				oSpan = RLdesign.Utils.MakeElement("span", null, this.NextYearText);
				oLink.appendChild(oSpan);
				oFieldset.appendChild(oLink);
			}
			else {
				oButton = RLdesign.Utils.MakeElement("button", null, this.NextYearText);
				oButton.className = "CalendarNextYear";
				RLdesign.Events.SetEventHandler(oButton, "click", window["RLdesign_Calendar_" + _oCalendarContainer.id].NextYear);
				oFieldset.appendChild(oButton);
			}
		}
		//end

		if (this.AddFormTags) {
			oForm.appendChild(oFieldset);
			_oCalendarContainer.appendChild(oForm);
			RLdesign.Events.SetEventHandler(oForm, "reset", window["RLdesign_Calendar_" + _oCalendarContainer.id]._handlereset);
		}
		else {
			_oCalendarContainer.appendChild(oFieldset);
			var f = _oCalendarContainer;
			while (f.parentNode != null) {
				f = f.parentNode;
				if (f.tagName.toLowerCase() == "form") {
					break;
				}
			}
			if (f.tagName.toLowerCase() == "form") {
				RLdesign.Events.SetEventHandler(f, "reset", window["RLdesign_Calendar_" + _oCalendarContainer.id]._handlereset);
			}
			f = null;
		}
		oSpan = oInput = oForm = oButton = oFieldset = oSelect = null; // memory cleanup
	}

	this._buildcalendarbody = function() {
		if (!_oCalendarContainer) return;
		var oTableRow = null;
		var oTableCell = null;
		var oDateLink = null;
		var aParams = null;
		var aWeek = null;
		this.ShowWeekNo = !!this.ShowWeekNo;
		_aCalendar = this._buildcalendararray();
		var dToday = new Date();

		var oTmpFragment = document.createDocumentFragment();

		for (var w = 0; w < _aCalendar.length; w++) { // writes the weeks and dates on the calendar
			aWeek = _aCalendar[w]["week"];
			oTableRow = RLdesign.Utils.MakeElement("tr");
			// weeknumbers
			if (this.ShowWeekNo) {
				aParams = new Array();
				aParams["class"] = "CalendarWeekNumber";
				oTableCell = RLdesign.Utils.MakeElement("th", aParams, _aCalendar[w]["weeknumber"]);
				oTableRow.appendChild(oTableCell);
			}
			for (d = 0; d < aWeek.length; d++) {
				aParams = new Array();
				// periferary month?
				if (aWeek[d]["month"] != _month) {
					if (aParams["class"]) aParams["class"] += " CalendarOtherMonth";
					else aParams["class"] = "CalendarOtherMonth";
				}
				// weekend?
				if (d == 5 || d == 6) {
					if (aParams["class"]) aParams["class"] += " CalendarWeekend";
					else aParams["class"] = "CalendarWeekend";
				}
				// selected date?
				if (aWeek[d]["selected"]) {
					if (aParams["class"]) aParams["class"] += " CalendarSelectedDate";
					else aParams["class"] = "CalendarSelectedDate";
				}
				// marked date?
				if (aWeek[d]["markeddate"]) {
					if (aParams["class"]) aParams["class"] += " CalendarMarkedDate";
					else aParams["class"] = "CalendarMarkedDate";
				}
				// today?
				if (aWeek[d]["today"]) {
					if (aParams["class"]) aParams["class"] += " CalendarToday";
					else aParams["class"] = "CalendarToday";
				}

				//				var a = "";
				//				a += "Current date: d=" + dToday.getDate() + " m=" + dToday.getMonth() + " y=" + dToday.getFullYear() + "\r\n";
				//				a += "Selected date: d=" + aWeek[d]["date"] + " m=" + aWeek[d]["month"] + " y=" + aWeek[d]["year"] + "\r\n";
				//				a += "condition 1: " + (this.AllowSelections == "Future" && aWeek[d]["year"] < dToday.getFullYear());
				//				a += "\r\ncondition 2: " + (this.AllowSelections == "Future" && aWeek[d]["month"] < dToday.getMonth());
				//				a += "\r\ncondition 3: " + (this.AllowSelections == "Future" && aWeek[d]["year"] == dToday.getFullYear() && aWeek[d]["month"] == dToday.getMonth() && aWeek[d]["date"] < dToday.getDate());
				//				a += "\r\ncondition 4: " + (!this.ShowOuterMonthDates && aWeek[d]["month"] != _month);
				//				alert(a);

				if (
					(this.AllowSelections == "Future" && aWeek[d]["year"] < dToday.getFullYear()) ||
					(this.AllowSelections == "Future" && aWeek[d]["year"] == dToday.getFullYear() && aWeek[d]["month"] < dToday.getMonth()) ||
					(this.AllowSelections == "Future" && aWeek[d]["year"] == dToday.getFullYear() && aWeek[d]["month"] == dToday.getMonth() && aWeek[d]["date"] < dToday.getDate()) ||
					(this.AllowSelections == "Past" && aWeek[d]["year"] > dToday.getFullYear()) ||
					(this.AllowSelections == "Past" && aWeek[d]["year"] == dToday.getFullYear() && aWeek[d]["month"] > dToday.getMonth()) ||
					(this.AllowSelections == "Past" && aWeek[d]["year"] == dToday.getFullYear() && aWeek[d]["month"] == dToday.getMonth() && aWeek[d]["date"] > dToday.getDate()) ||
					(!this.ShowOuterMonthDates && aWeek[d]["month"] != _month)
				) {
					aParams["class"] += " disabled";
					oTableCell = RLdesign.Utils.MakeElement("td", aParams);
					if (this.ShowDisabledDates) {
						oDateLink = RLdesign.Utils.MakeElement("span", null, aWeek[d]["date"]);
					}
					else {
						oDateLink = RLdesign.Utils.MakeElement("span", null, " ");
					}
					oTableCell.appendChild(oDateLink);
				}
				else {
					oTableCell = RLdesign.Utils.MakeElement("td", aParams);
					aParams["href"] = "#";
					oDateLink = RLdesign.Utils.MakeElement("a", aParams, aWeek[d]["date"]);
					RLdesign.Events.SetEventHandler(oDateLink, "click", this._onselectdate);
					oTableCell.appendChild(oDateLink);
				}
				oTableRow.appendChild(oTableCell);
			}
			oTmpFragment.appendChild(oTableRow);
		}
		return oTmpFragment;
	}
	// internal function that searches the markeddates array for the given date
	this._inmarkeddates = function(mdate) {
		var r = false;
		if (this._markeddates != null && this._markeddates.length > 0) {
			var y = mdate.getFullYear();
			var m = mdate.getMonth();
			var d = mdate.getDate();

			for (var i = 0; i < this._markeddates.length; i++) {
				var tdate = this._markeddates[i];
				if (tdate.getFullYear() == y && tdate.getMonth() == m && tdate.getDate() == d) {
					r = true;
					break;
				}
			}
		}
		return r;
	}
	// internal function that sets the selected date
	this._setdate = function(y, m, d) {
		_day = (d > 0) ? d - 1 : d;
		_month = (m > 0) ? m - 1 : m;
		_year = y;
		_selecteddate = new Date();
		_selecteddate.setFullYear(_year, _month, _day + 1);
	}
	// internal function that add a marked date to the array
	this._markdate = function(mdate) {
		if (!RLdesign.Arrays.InArray(this._markeddates, mdate)) {
			this._markeddates[this._markeddates.length] = mdate;
		}
	}
	// returns no. of days in the given month (taking leap-year into consideration)
	this._getdayspermonth = function(m) {
		var d = new Date(_year, m + 1, 0);
		//				alert("_year: " + _year + ", m: " + m + ", date: " + d.getDate());
		return new Date(_year, m + 1, 0).getDate();
		/*
		if (_year % 4 == 0) _adayspermonth[1] = 29;
		else _adayspermonth[1] = 28;
		if (arguments && arguments.length > 0) { if (m < 0) m = 12 - (m * -1); }
		else { m = _month; }
		return _adayspermonth[m];
		*/
	}
	// src: http://www.thecodeproject.com/csharp/gregorianwknum.asp
	this.GetWeekNo = function(year, month, day) {
		month += 1; //use 1-12
		var a = Math.floor((14 - (month)) / 12);
		var y = year + 4800 - a;
		var m = (month) + (12 * a) - 3;
		var jd = day + Math.floor(((153 * m) + 2) / 5) + (365 * y) + Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400) - 32045; // (gregorian calendar)
		//var jd = (day+1)+Math.Round(((153*m)+2)/5)+(365+y) + 
		//                 Math.round(y/4)-32083;    // (julian calendar)

		//now calc weeknumber according to JD
		var d4 = (jd + 31741 - (jd % 7)) % 146097 % 36524 % 1461;
		var L = Math.floor(d4 / 1460);
		var d1 = ((d4 - L) % 365) + L;
		var NumberOfWeek = Math.floor(d1 / 7) + 1;
		return NumberOfWeek;
	}

	/* END REGION */






	/* REGION: PUBLIC CONTROL FUNCTIONS */

	this.UpdateCalendar = function() {
		var oTableBody = RLdesign.Utils.DefineObject("RLdesign_Calendar_" + _oCalendarContainer.id + "tbody");
		RLdesign.Utils.ClearChildElements(oTableBody);
		oTableBody.appendChild(this._buildcalendarbody());

		if (this.ShowSelectYearControl) {
			document.getElementById("RLdesign_Calendar_" + _oCalendarContainer.id + "YearInput").value = _year;
		}
		else if (this.ShowStaticSelectedYear) {
			document.getElementById("RLdesign_Calendar_" + _oCalendarContainer.id + "StaticYear").innerHTML = _year;
		}
		if (this.ShowSelectMonthControl) {
			var oMonthSelect = document.getElementById("RLdesign_Calendar_" + _oCalendarContainer.id + "MonthSelect");
			for (i = 0; i < oMonthSelect.options.length; i++) {
				oMonthSelect.options[i].selected = false;
				if (_month == i) oMonthSelect.options[i].selected = true;
			}
			oMonthSelect = null;
		}
		else if (this.ShowStaticSelectedMonth) {
			document.getElementById("RLdesign_Calendar_" + _oCalendarContainer.id + "StaticMonth").innerHTML = _amonthnameslong[_month];
		}
		oTableBody = null;
	}

	// sets literal month names (ie. in your local language)
	this.SetMonthNames = function(aM, aMs) {
		if (RLdesign.Utils.IsArray(aM) && aM.length == 12) {
			_amonthnameslong = aM;
			if (arguments.length == 2 && RLdesign.Utils.IsArray(aMs) && aMs.length == 7) {
				_amonthnamesshort = aMs;
			}
			else {
				_amonthnamesshort = new Array();
				for (var i = 0; i < _amonthnameslong.length; i++) {
					_amonthnamesshort[i] = _amonthnameslong[i].substring(0, this.ShortMonthLength);
				}
			}
		}
	}
	// sets literal day names (ie. in your local language)
	this.SetDayNames = function(aD, aDs) {
		if (RLdesign.Utils.IsArray(aD) && aD.length == 7) {
			_adaynameslong = aD;
			if (arguments.length == 2 && RLdesign.Utils.IsArray(aDs) && aDs.length == 7) {
				_adaynamesshort = aDs;
			}
			else {
				_adaynamesshort = new Array();
				for (var i = 0; i < _adaynameslong.length; i++) {
					_adaynamesshort[i] = _adaynameslong[i].substring(0, this.ShortDayLength);
				}
			}
		}
	}
	// externally called function that sets the selected date and subsequently updates the calendar
	this.SetDate = function(y, m, d) {
		this._dateselected = true;
		this._setdate(Number(y), Number(m), Number(d));
		if (this._hasbeenbuilt) {
			this.UpdateCalendar();
		}
	}
	// externally called function that marks a given date (as either a single date object or parameters year, month, date
	this.MarkDate = function() {
		var mdate = null;
		if (arguments.length == 3) {
			var y = arguments[0];
			var m = arguments[1];
			var d = arguments[2];
			var mdate = new Date();
			mdate.setFullYear(y, m - 1, d);
		}
		else if (arguments.length == 1) {
			mdate = arguments[0];
		}
		if (mdate != null) {
			this._markdate(mdate);
		}
		if (this._hasbeenbuilt) {
			this.UpdateCalendar();
		}
	}
	// advances the calendar one year
	this.NextYear = function(oEvent) {
		_year++;
		window["RLdesign_Calendar_" + _oCalendarContainer.id]._onchangemonth();
		window["RLdesign_Calendar_" + _oCalendarContainer.id].UpdateCalendar();
		return RLdesign.Utils.DefineReturnCode(oEvent, false);
	}
	// retards the calendar one year
	this.PrevYear = function(oEvent) {
		_year--;
		window["RLdesign_Calendar_" + _oCalendarContainer.id]._onchangemonth();
		window["RLdesign_Calendar_" + _oCalendarContainer.id].UpdateCalendar();
		return RLdesign.Utils.DefineReturnCode(oEvent, false);
	}
	// advances the calendar one month
	this.NextMonth = function(oEvent) {
		if (_month < 11) { _month++; }
		else {
			_month = 0;
			_year++;
		}
		window["RLdesign_Calendar_" + _oCalendarContainer.id]._onchangemonth();
		window["RLdesign_Calendar_" + _oCalendarContainer.id].UpdateCalendar();
		return RLdesign.Utils.DefineReturnCode(oEvent, false);
	}
	// retards the calendar one month
	this.PrevMonth = function(oEvent) {
		if (_month > 0) { _month--; }
		else {
			_month = 11;
			_year--;
		}
		window["RLdesign_Calendar_" + _oCalendarContainer.id]._onchangemonth();
		window["RLdesign_Calendar_" + _oCalendarContainer.id].UpdateCalendar();
		return RLdesign.Utils.DefineReturnCode(oEvent, false);
	}
	this.GetPrevMonth = function() {
		var oPrev = new Object();
		if (_month > 0) {
			oPrev.month = _month - 1;
			oPrev.year = _year;
		}
		else {
			oPrev.month = 11;
			oPrev.year = _year - 1;
		}
		return oPrev;
	}
	this.GetNextMonth = function() {
		var oNext = new Object();
		if (_month < 11) {
			oNext.month = _month + 1;
			oNext.year = _year;
		}
		else {
			oNext.month = 0;
			oNext.year = _year + 1;
		}
		return oNext;
	}

	/* END REGION */






	/* REGION: OTHER PUBLIC FUNCTIONS */

	// externally called function that returns the selected date
	this.GetDate = function() {
		return _selecteddate;
	}
	this.GetTimeoutVariable = function() {
		return "RLdesign_Calendar_" + _oCalendarContainer.id + "_Timeout";
	}
	this.ClearCalendarTimeout = function(e) {
		RLdesign.Utils.ClearTimeout(window["RLdesign_Calendar_" + _oCalendarContainer.id].GetTimeoutVariable());
	}
	this.SetCalendarTimeout = function(time) {
		if (!window["RLdesign_Calendar_" + _oCalendarContainer.id]._hasFocus) {
			var timeout = window["RLdesign_Calendar_" + _oCalendarContainer.id].GetTimeoutVariable();
			window[timeout] = window.setTimeout("window['RLdesign_Calendar_" + _oCalendarContainer.id + "'].SetDisplay(false)", time);
		}
	}

	this.GetCalendarContainer = function() {
		return _oCalendarContainer;
	}

	this.CloseCalendar = function(oEvent) {
		window["RLdesign_Calendar_" + _oCalendarContainer.id]._hasFocus = false;
		window["RLdesign_Calendar_" + _oCalendarContainer.id].SetDisplay("none");
		return RLdesign.Utils.DefineReturnCode(oEvent, false);
	}

	this.RemoveSelectedDate = function() {
		this._dateselected = false;
		this.UpdateCalendar();
	}

	// returns whether the calendar control is displayed
	this.GetDisplayStatus = function() {
		return this._displaystatus;
	}
	// sets display property of calendarcontainer
	this.SetDisplay = function(sDisplay) {
		if (!_oCalendarContainer) return;
		if (sDisplay === true) {
			this._displaystatus = true;
			sDisplay = "block";
		}
		else if (sDisplay === false) {
			this._displaystatus = false;
			sDisplay = "none";
		}
		else {
			switch (sDisplay.toString().toLowerCase()) {
				case "none":
				case "hidden":
				case "false":
					this._displaystatus = false;
					sDisplay = "none";
					break;
				case "inline":
					this._displaystatus = true;
					sDisplay = "inline";
					break;
				default:
					this._displaystatus = true;
					sDisplay = "block";
					break;
			}
		}
		_oCalendarContainer.style.display = sDisplay;
	}

	/* END REGION */

	this._init(SettingsObject);
}


RLdesign.CalendarInit = function () {
	return {
		Start: function (oCalendarSettings) {
			if (!oCalendarSettings.TextboxID || !oCalendarSettings.ContainerID) return;

			oCalendarSettings.Calendar = new RLdesign.Calendar(oCalendarSettings);

			if (oCalendarSettings.ToggleDisplay != null && oCalendarSettings.ToggleDisplay == true && oCalendarSettings.onSelectDate == null) {
				oCalendarSettings.Calendar.onSelectDate = function (e) {
					var numDate = Number(e.SelectedDate.getDate());
					var numMonth = Number(e.SelectedDate.getMonth() + 1);
					var numYear = Number(e.SelectedDate.getFullYear());
					if (numDate < 10) numDate = "0" + numDate;
					if (numMonth < 10) numMonth = "0" + numMonth;
					var strDate = numDate + "-" + numMonth + "-" + numYear;
					var oTextbox = RLdesign.Utils.DefineObject(oCalendarSettings.TextboxID);
					if (oTextbox != null && oTextbox.tagName.toLowerCase() == "input") {
						oTextbox.value = strDate;
					}
					else if (oTextbox != null && oTextbox.innerHTML != null && oTextbox.innerHTML != "") {
						oTextbox.innerHTML = strDate;
					}
					oCalendarSettings.Calendar.SetDisplay(false);
					oTextbox = null;
				};
			}
			else if (oCalendarSettings.onSelectDate == null) {
				oCalendarSettings.Calendar.onSelectDate = function (e) {
					var numDate = Number(e.SelectedDate.getDate());
					var numMonth = Number(e.SelectedDate.getMonth() + 1);
					var numYear = Number(e.SelectedDate.getFullYear());
					if (numDate < 10) numDate = "0" + numDate;
					if (numMonth < 10) numMonth = "0" + numMonth;
					var strDate = numDate + "-" + numMonth + "-" + numYear;
					var oTextbox = RLdesign.Utils.DefineObject(oCalendarSettings.TextboxID);
					if (oTextbox != null && oTextbox.tagName.toLowerCase() == "input") {
						oTextbox.value = strDate;
					}
					else if (oTextbox != null && oTextbox.innerHTML != null && oTextbox.innerHTML != "") {
						oTextbox.innerHTML = strDate;
					}
					oTextbox = null;
				};
			}
			else {
				oCalendarSettings.Calendar.onSelectDate = oCalendarSettings.onSelectDate;
			}

			oCalendarSettings.Calendar.BuildCalendar();

			if (oCalendarSettings.TextboxID != null) {
				var oTextbox = RLdesign.Utils.DefineObject(oCalendarSettings.TextboxID);

				var strTmpDate = "";

				if (oTextbox != null && oTextbox.tagName.toLowerCase() == "input") {
					RLdesign.Events.SetEventHandler(oTextbox, "keydown", function (e) {
						var iKeyCode = RLdesign.Utils.DefineEventKeyCode(e);
						if (iKeyCode == 9 || iKeyCode == 27 || iKeyCode == 13) {
							return true;
						}
						if (iKeyCode == 8 || iKeyCode == 9 || iKeyCode == 35 || iKeyCode == 36 || iKeyCode == 37 || iKeyCode == 39 || iKeyCode == 46 || iKeyCode == 16) return true;
						if ((iKeyCode >= 96 && iKeyCode <= 105) || (iKeyCode >= 48 && iKeyCode <= 57) || iKeyCode == 109) {
							var elem = RLdesign.Utils.DefineEventTarget(e);
							return true;
						}
						return RLdesign.Utils.DefineReturnCode(e, false);
					});


					if (oCalendarSettings.ToggleDisplay != null && oCalendarSettings.ToggleDisplay == true) {
						RLdesign.Events.SetEventHandler(oTextbox, "focus", function (e) {
							if (oCalendarSettings.Calendar.GetDisplayStatus() == false) {
								oCalendarSettings.Calendar.SetDisplay(true);
							}
							return RLdesign.Utils.DefineReturnCode(e, false);
						});
						oCalendarSettings.Calendar.SetDisplay(false);
					}
					else {
						oCalendarSettings.Calendar.SetDisplay(true);
					}

					strTmpDate = oTextbox.value;
				}
				else if (oTextbox != null && oTextbox.innerHTML != null && oTextbox.innerHTML != "") {
					strTmpDate = oTextbox.innerHTML;
				}

				var numDate = null;
				var numMonth = null;
				var numYear = null;
				if (strTmpDate != "" && strTmpDate.indexOf("-") > -1) {
					numDate = Number(strTmpDate.substring(0, strTmpDate.indexOf("-")));
					strTmpDate = strTmpDate.substring(strTmpDate.indexOf("-") + 1);
					if (strTmpDate.indexOf("-") > -1) {
						numMonth = Number(strTmpDate.substring(0, strTmpDate.indexOf("-")));
						strTmpDate = strTmpDate.substring(strTmpDate.indexOf("-") + 1);
					}
					numYear = Number(strTmpDate);
					strTmpDate = null;
				}
				if (numDate != null && numMonth != null && numYear != null) {
					oCalendarSettings.Calendar.SetDate(numYear, numMonth, numDate);
				}

				oTextbox = null;
			}
			else {
				oCalendarSettings.Calendar.SetDisplay(true);
			}
		}
	}
} ();
