/**
* @version: 1.4.0
* @author: Coolite Inc. http://www.coolite.com/
* @date: 19-Mar-2008
* @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved. Unauthorized use prohibited.
* @website: http://www.coolite.com/
*/

Coolite.DatePicker = function(config) {
	if (!config || config === null) {
		return null;
	}

	var defaults = {
		dateFormat: "d-MMM-yyyy",
		displayType: "TextBoxAndImage",

		rows: 1,
		columns: 1,

		northImage: "/aspnet_client/basicframe_webcontrols_basicdatepicker/1_4_0/arrow_up.gif",
		southImage: "/aspnet_client/basicframe_webcontrols_basicdatepicker/1_4_0/arrow_down.gif",
		eastImage: "/aspnet_client/basicframe_webcontrols_basicdatepicker/1_4_0/arrow_right.gif",
		westImage: "/aspnet_client/basicframe_webcontrols_basicdatepicker/1_4_0/arrow_left.gif",

		showWeekNumbers: false,
		showDaysInNextMonth: true,
		showDaysInPrevMonth: true,

		enabled: true,
		autoPostBack: false,
		postBackFunction: null,

		selectableWeekDays: true,
		selectableWeekendDays: true,
		selectablePrevMonthDays: true,
		selectableNextMonthDays: true,
		monthSelectorEnabled: true,
		yearSelectorEnabled: true,

		maximumDate: "12/31/9999",
		minimumDate: null,
		nullDate: null,
		nullDateText: "",
		visibleDate: null,

		firstDayOfWeek: Date.CultureInfo.firstDayOfWeek,
		forceSixRows: true,
		showCalendarOnTextBoxFocus: false,

		titleFormat: "MMMM yyyy",
		dayStatusBarText: "{0}",

		nextMonthText: "&gt;",
		prevMonthText: "&lt;",

		showTodayButton: true,
		showNoneButton: true,
		showDayHeader: true,
		showNextPrevMonth: true,
		showTitle: true,
		showPreviousMonthArrow: true,
		showNextMonthArrow: true,

		dayNameFormat: "FirstLetter",
		nextPrevFormat: "Image",

		buttonText: "Calendar",
		todayButtonText: "Today",
		noneButtonText: "None",
		footNoteText: "",

		upYearSelectorText: "+",
		downYearSelectorText: "-",
		upDownYearSelectorFormat: "Image",

		xOffset: 0,
		yOffset: -2,
		monthSelectorXOffset: -11,
		monthSelectorYOffset: 2,
		yearSelectorXOffset: -11,
		yearSelectorYOffset: 2,

		events: {
			dayRender: "",
			preParse: "",
			beforeCalendarOpen: "",
			beforeCalendarClose: "",
			afterCalendarOpen: "",
			beforeSelectionChanged: "",
			afterSelectionChanged: "",
			beforeVisibleMonthChanged: "",
			afterVisibleMonthChanged: ""
		},

		styles: {
			popUp: "",
			calendar: "",
			noneButton: "",
			todayButton: "",
			title: "",
			nextPrev: "",
			dayHeader: "",
			day: "",
			otherMonthDay: "",
			weekendDay: "",
			selectedDay: "",
			todayDay: "",
			footer: "",
			footNote: "",
			weekNumber: "",
			otherMonthDayWeekendDay: "",
			selectedDayTodayDay: "",
			selectedDayWeekendDay: "",
			todayDayWeekendDay: ""
		},

		cssClasses: {
			popUp: "bdpPopUp",
			calendar: "bdpCalendar",
			noneButton: "bdpClearButton",
			todayButton: "bdpTodayButton",
			title: "bdpTitle",
			nextPrev: "bdpNextPrev",
			dayHeader: "bdpDayHeader",
			day: "bdpDay",
			otherMonthDay: "bdpDay bdpOtherMonthDay",
			weekendDay: "bdpDay bdpWeekendDay",
			selectedDay: "bdpDay bdpSelectedDay",
			todayDay: "bdpDay bdpTodayDay",
			footer: "bdpFooter",
			footNote: "bdpFootNote",
			weekNumber: "bdpWeekNumber",
			otherMonthDayWeekendDay: "bdpDay bdpOtherMonthDay bdpWeekendDay",
			selectedDayTodayDay: "bdpDay bdpTodayDay bdpSelectedDay",
			selectedDayWeekendDay: "bdpDay bdpWeekendDay bdpSelectedDay",
			todayDayWeekendDay: "bdpDay bdpWeekendDay bdpTodayDay"
		},

		timePickerID: "",
		specialDatesID: ""
	};

	for (var p in defaults) {
		this[p] = defaults[p];
		if (p == "events" || p == "styles" || p == "cssClasses") {
			for (var p2 in defaults[p]) {
				this[p][p2] = defaults[p][p2];
			}
		}
	}

	for (var c in config) {
		this[c] = config[c];
		if (c == "events" || c == "styles" || c == "cssClasses") {
			for (var c2 in config[c]) {
				this[c][c2] = config[c][c2];
			}
		}
	}

	this.visibleDate = Date.parseExact(this.visibleDate + " 12:00:00", "yyyy/M/d hh:mm:ss");
	this.initVisibleDate = (this.visibleDate !== null) ? this.visibleDate.clone() : null;
	this.maximumDate = Date.parseExact(this.maximumDate + " 12:00:00", "yyyy/M/d hh:mm:ss");
	this.minimumDate = Date.parseExact(this.minimumDate + " 12:00:00", "yyyy/M/d hh:mm:ss");
	this.inputFocusValue = "";

	this.input = null;
	this.button = null;
	this.label = null;
	this.calendar = null;

	this.yearInc = null;
	this.timeout = null;
	this.interval = null;
	this._isDate = false;
	this._isApplied = false;

	this.calendarShim = null;
	this.monthSelectorShim = null;
	this.yearSelectorShim = null;
	this.monthSelector = null;
	this.yearSelector = null;

	this.generatedHTML = null;
	this.isOpen = false;
	this.clickToggle = false;

	this.applyTo = function(config) {
		if (!config || config === null) {
			return null;
		}

		if (config.inputID) {
			this.input = Coolite.Dom.get(config.inputID);
			Coolite.Event.addListener(this.input, 'keydown', this._inputKeyDown, this);
			Coolite.Event.addListener(this.input, 'focus', this._inputFocus, this, true);
			Coolite.Event.addListener(this.input, 'blur', this._inputBlur, this, true);
			Coolite.Event.addListener(this.input, 'click', this._inputClick, this, true);
		}

		if (config.buttonID && this.getEnabled()) {
			this.button = Coolite.Dom.get(config.buttonID);
			if (!$COOL.isNullOrEmpty(this.displayType) && this.displayType != "TextBox") {
				Coolite.Event.addListener(this.button, 'click', this._buttonClick, this, true);
			}
		}

		if (config.labelID) {
			this.label = Coolite.Dom.get(config.labelID);
		}

		this._isApplied = true;
		$COOL.controls.push({ type: "DatePicker", control: this });

		return this;
	};

	this.setVisibleDate = function(date) {
		this.visibleDate = (date !== null) ? date.clone().clearTime().moveToFirstDayOfMonth() : Date.today().moveToFirstDayOfMonth();
		return this;
	};

	this.getVisibleDate = function() {
		if (this.visibleDate === null) {
			this.visibleDate = Date.today().moveToFirstDayOfMonth();
		}
		return this.visibleDate;
	};

	this.show = function() {
		if (this.isOpen || !this._isApplied) {
			return this;
		}

		Coolite.Event.removeListener(this.input, 'blur', this._inputBlur);
		Coolite.Event.addListener(document, 'click', this._documentClick, this, true);
		Coolite.Event.addListener(document, 'keypress', this._documentKeyPress, this, true);

		var d = this.getSelectedDate();

		if (d !== null) {
			this.setSelectedDate(d);
			this.setVisibleDate(d);
		}

		if (!$COOL.isNullOrEmpty(this.events.beforeCalendarOpen)) {
			window[this.events.beforeCalendarOpen](this, null);
		}

		if (!this.calendar) {
			this.calendar = document.createElement("DIV");
			this.calendar.id = this.clientID + "_calendar";
			this.calendar.style.position = "absolute";
			this.calendar.style.visibility = "hidden";
			this.calendar.style.zIndex = "100002";
			Coolite.Event.addListener(this.calendar, 'click', this._calendarClick, this, true);
			Coolite.Event.addListener(this.calendar, 'mousewheel', this._scroll, this, true);
			Coolite.Event.on(this.calendar, "DOMMouseScroll", this._scroll, this, true);
			document.body.appendChild(this.calendar);
		}

		this.render(d, this.getVisibleDate());

		this.calendar.style.visibility = "visible";

		this.isOpen = true;

		if (!$COOL.isNullOrEmpty(this.events.afterCalendarOpen)) {
			window[this.events.afterCalendarOpen](this, null);
		}
		return this;
	};

	this.close = function() {

		if (!this.isOpen || !this._isApplied) {
			return this;
		}

		Coolite.Event.addListener(this.input, 'blur', this._inputBlur, this, true);
		Coolite.Event.removeListener(document, 'click', this._documentClick);
		Coolite.Event.removeListener(document, 'keypress', this._documentKeyPress);

		if (!$COOL.isNullOrEmpty(this.events.beforeCalendarClose)) {
			window[this.events.beforeCalendarClose](this, null);
		}

		if (this.calendar) {
			this.calendar.style.visibility = "hidden";
		}

		this.clearTimers();
		$COOL.hideShim(this.calendarShim);
		this.hideSelectors();
		this.isOpen = false;

		return this;
	};

	this.hideSelectors = function() {
		if (this.monthSelector) {
			this.monthSelector.style.visibility = "hidden";
		}

		if (this.yearSelector) {
			this.yearSelector.style.visibility = "hidden";
		}

		$COOL.hideShim(this.monthSelectorShim);
		$COOL.hideShim(this.yearSelectorShim);
	};

	this.clearTimers = function() {
		clearTimeout(this.timeout);
		clearInterval(this.interval);
		return this;
	};

	this.render = function(selectedDate, visibleDate) {
		this.setVisibleDate(visibleDate || Date.today());

		var count = 0, o = [];
		o.push('<table cellpadding="0" cellspacing="0" class="bdpWrapper">');
		for (var y = 0; y < this.rows; y++) {
			o.push("<tr>");
			for (var x = 0; x < this.columns; x++) {
				var prev = (y === 0 && x === 0);
				var next = (y === 0 && x == this.columns - 1);
				o.push("<td>");
				o.push(this._renderMonth((count === 0) ?
					new Date(visibleDate.getFullYear(), visibleDate.getMonth(), visibleDate.getDate(), 12, 0, 0) :
					new Date(visibleDate.addMonths(1).getFullYear(), visibleDate.getMonth(), visibleDate.getDate(), 12, 0, 0),
					new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 12, 0, 0),
					prev, next));
				o.push("</td>");
				count++;
			}
			o.push("</tr>");
		}

		o.push('<tr><td colspan="', this.columns, '">');

		if ((this.showTodayButton || this.showNoneButton)) {
			o.push("<div", $COOL.buildStyleAttributes(this.styles.footer, this.cssClasses.footer), ">");
			if (this.showTodayButton) {
				o.push('<input type="button" onclick="', this.clientID, '._setToToday().close().focus(); " value="', this.todayButtonText, "\"", $COOL.buildStyleAttributes(this.styles.todayButton, this.cssClasses.todayButton), ' title="" />');
			}
			if (this.showNoneButton) {
				o.push('<input type="button" onclick="', this.clientID, '.clear().close().focus();" value="', this.noneButtonText, '"', $COOL.buildStyleAttributes(this.styles.noneButton, this.cssClasses.noneButton), ' title="" />');
			}
			o.push("</div>");
		}

		if (!$COOL.isNullOrEmpty(this.footNoteText)) {
			o.push("<div", $COOL.buildStyleAttributes(this.styles.footNote, this.cssClasses.footNote), ">", this.footNoteText, "</div>");
		}

		o.push("</td></tr>");
		o.push("</table>");
		o = o.join('');
		this.generatedHTML = o;
		this.calendar.innerHTML = o;

		var els = Coolite.Dom.getElementsByClassName("bdpDayItem", "a", this.calendar);

		for (i = 0; i < els.length; i++) {
			Coolite.Event.addListener(els[i], 'click', this.onDayClick, this);
			//Coolite.Event.addListener(els[i], 'rightclick', this._rightdayClick, this);
		}

		var p = Coolite.Dom.getXY(this.button), popupMaxTop = p[1] - this.calendar.offsetHeight, windowHeight;

		if ($COOL.isIEWin) {
			popupMaxTop -= 1;
		}

		var popupMaxBottom = this.yOffset + p[1] + this.button.offsetHeight + this.calendar.offsetHeight;

		if (window.innerHeight) {
			windowHeight = Math.max(window.innerHeight, document.documentElement.clientHeight);
		} else {
			windowHeight = (document.documentElement.clientHeight > 0) ? document.documentElement.clientHeight : document.body.clientHeight;
		}

		windowHeight = windowHeight + Math.max(document.documentElement.scrollTop, document.body.scrollTop);
		this.calendar.style.left = (this.xOffset + p[0] + this.button.offsetWidth - this.calendar.offsetWidth) + "px";
		this.calendar.style.top = ((popupMaxBottom > windowHeight && popupMaxTop > 0) ? popupMaxTop : (this.yOffset + p[1] + this.button.offsetHeight)) + "px";
		if (parseInt(this.calendar.style.left, 10) < 0) {
			this.calendar.style.left = 0;
		}

		if (parseInt(this.calendar.style.top, 10) < 0) {
			this.calendar.style.top = 0;
		}

		this.calendarShim = $COOL.makeShim(this.calendarShim, this.calendar);
		return this;
	};

	this.onDayClick = function(e, el) {
		el.setSelectedDate(new Date(parseInt(this.getAttribute("d"), 0))).close().focus();
	};

	this._renderMonth = function(date, selectedDate, showPrevMonthArrow, showNextMonthArrow) {
		this.hideSelectors();
		var nvd = date.clone().addMonths(1), pvd = date.clone().addMonths(-1), o = [];
		if (selectedDate !== null) {
			date.set({ hour: selectedDate.getHours(), minute: selectedDate.getMinutes(), second: selectedDate.getSeconds() });
		}
		o.push('<table border="0" cellpadding="0" cellspacing="0"', $COOL.buildStyleAttributes(this.styles.popUp, this.cssClasses.popUp), '>');
		o.push("<tr><td>");

		if (this.showTitle) {
			o.push('<table border="0" cellpadding="0" cellspacing="0"', $COOL.buildStyleAttributes(this.styles.title, this.cssClasses.title), '><tr>');
			var monthAndYear = '<th>';
			var titleParts = this.titleFormat.split(" ");
			for (var i = 0; i < titleParts.length; i++) {
				if (titleParts[i].indexOf("M") != -1) {
					if (this.monthSelectorEnabled) {
						monthAndYear += '<span id="monthSelector" onclick="' + this.clientID + '._monthSelectorClick(this)" style="cursor:pointer">' + date.toString(titleParts[i]) + '</span>';
					} else {
						monthAndYear += date.toString(titleParts[i]);
					}
				}
				else if (titleParts[i].indexOf("yy") != -1) {
					if (this.yearSelectorEnabled) {
						monthAndYear += '<span id="yearSelector" onclick="' + this.clientID + '._yearSelectorClick(this)" style="cursor:pointer">' + date.toString(titleParts[i]) + '</span>';
					} else {
						monthAndYear += date.toString(titleParts[i]);
					}
				}
				else {
					monthAndYear += titleParts[i];
				}
				if (i < titleParts.length) {
					monthAndYear += ' ';
				}
			}
			monthAndYear += "</th>";
			if (this.showNextPrevMonth) {
				var prevMonth, nextMonth;
				switch (this.nextPrevFormat) {
					case "ShortMonth":
						prevMonth = pvd.toString("MMM");
						nextMonth = nvd.toString("MMM");
						break;
					case "FullMonth":
						prevMonth = pvd.toString("MMMM");
						nextMonth = nvd.toString("MMMM");
						break;
					case "Image":
						prevMonth = '<img src="' + this.westImage + '" alt="" />';
						nextMonth = '<img src="' + this.eastImage + '" alt="" />';
						break;
					case "CustomText":
						prevMonth = this.prevMonthText;
						nextMonth = this.nextMonthText;
						break;
				}
				if (showPrevMonthArrow) {
					if (this.showPreviousMonthArrow) {
						o.push('<td onclick="', this.clientID, '.showPrevMonth()" onmousedown="', this.clientID, '.clearTimers().timeout = setTimeout(\'', this.clientID, '._prevMonthMouseDown()\', 100);" onmouseup="' + this.clientID + '.clearTimers();" onmouseout="' + this.clientID + '.clearTimers();"' + $COOL.buildStyleAttributes(this.styles.nextPrev, this.cssClasses.nextPrev), '>');
						o.push(prevMonth);
					} else {
						o.push("<td>&nbsp;");
					}
					o.push("</td>");
				} else {
					o.push("<td></td>");
				}
				o.push(monthAndYear);
				if (showNextMonthArrow) {
					if (this.showNextMonthArrow) {
						o.push('<td onclick="', this.clientID, '.showNextMonth()" onmousedown="', this.clientID, '.clearTimers().timeout=setTimeout(\'', this.clientID, '._nextMonthMouseDown()\', 100);" onmouseup="' + this.clientID + '.clearTimers();" onmouseout="', this.clientID, '.clearTimers();"', $COOL.buildStyleAttributes(this.styles.nextPrev, this.cssClasses.nextPrev), '>');
						o.push(nextMonth);
					} else {
						o.push("<td>&nbsp;");
					}
					o.push("</td>");
				} else {
					o.push("<td></td>");
				}
			} else {
				o.push(monthAndYear);
			}
			o.push("</tr></table>");
		}

		/* calendar table */
		o.push('<table border="0" cellpadding="0" cellspacing="0"', $COOL.buildStyleAttributes(this.styles.calendar, this.cssClasses.calendar), '>');

		/* week number header */
		if (this.showDayHeader) {
			o.push("<thead><tr>");
			if (this.showWeekNumbers) {
				o.push('<th', $COOL.buildStyleAttributes(this.styles.dayHeader, this.cssClasses.dayHeader), '>&nbsp;</th>');
			}

			/* day of week headers */
			var dayPointer = this.firstDayOfWeek;
			for (var j = 0; j < 7; j++, dayPointer++) {
				if (dayPointer == 7) {
					dayPointer = 0;
				}
				o.push('<th', $COOL.buildStyleAttributes(this.styles.dayHeader, this.cssClasses.dayHeader), ' title="', Date.CultureInfo.dayNames[dayPointer], '">');
				switch (this.dayNameFormat) {
					case "Short":
						o.push(Date.CultureInfo.abbreviatedDayNames[dayPointer]);
						break;
					case "Full":
						o.push(Date.CultureInfo.dayNames[dayPointer]);
						break;
					case "FirstTwoLetters":
						o.push(Date.CultureInfo.shortestDayNames[dayPointer]);
						break;
					case "FirstLetter":
						o.push(Date.CultureInfo.firstLetterDayNames[dayPointer]);
						break;
				}
				o.push("</th>");
			}
			o.push("</tr></thead>");
		}

		o.push("<tbody><tr>");

		var caret = date.clone().moveToFirstDayOfMonth();
		var startingPos = caret.getDay() - this.firstDayOfWeek;

		if (startingPos < this.firstDayOfWeek) {
			startingPos += 7;
		}

		var month, prev = caret.clone().addDays(-1).getMonth(), next = caret.clone().addMonths(1).getMonth(), lastDayOfWeek;

		caret.add(startingPos * -1).days();

		for (d = 1; d <= 42; d++) {
			month = caret.getMonth();
			lastDayOfWeek = ((d % 7) === 0);

			//            if (month === 9 && d === 21) {
			//                if (month === prev) {
			//                    o.push(this._renderDay(caret.addDays(1), selectedDate, "prev"));
			//                } else if (month === next) {
			//                    o.push(this._renderDay(caret.addDays(1), selectedDate, "next"));
			//                } else {
			//                    o.push(this._renderDay(caret.addDays(1), selectedDate, "current"));
			//                }
			//            }
			//            else {

			if (month === prev) {
				o.push(this._renderDay(caret, selectedDate, "prev"));
			} else if (month === next) {
				o.push(this._renderDay(caret, selectedDate, "next"));
			} else {
				o.push(this._renderDay(caret, selectedDate, "current"));
			}

			// }
			caret.addDays(1);

			if (lastDayOfWeek) {
				if (!this.forceSixRows && caret.getMonth() != date.getMonth()) {
					break;
				}
				o.push("</tr><tr>");
			}
		}
		o.push("</tr></tr></tbody></table></td></tr></table>");
		return o.join('');
	};

	this._renderDay = function(date, selectedDate, type) {
		var o = [], dayClassName = "", isToday = Date.equals(Date.today(), date.clone().clearTime()),
        isWeekday = date.is().weekday(), styleToUse = null, styleCssClassToUse = null,
        isSelectedDay = (selectedDate instanceof Date) ? date.clone().clearTime().equals(selectedDate.clone().clearTime()) : false;
		switch (type) {
			case "current":
				if (isSelectedDay && isToday) {
					if (isWeekday) {
						styleToUse = this.styles.selectedDayTodayDay;
						styleCssClassToUse = this.cssClasses.selectedDayTodayDay;
					} else {
						styleToUse = this.styles.selectedDayWeekendDay;
						styleCssClassToUse = this.cssClasses.selectedDayWeekendDay;
					}
				} else if (isSelectedDay) {
					if (isWeekday) {
						styleToUse = this.styles.selectedDay;
						styleCssClassToUse = this.cssClasses.selectedDay;
					} else {
						styleToUse = this.styles.selectedDayWeekendDay;
						styleCssClassToUse = this.cssClasses.selectedDayWeekendDay;
					}
				} else if (isToday) {
					if (isWeekday) {
						styleToUse = this.styles.todayDay;
						styleCssClassToUse = this.cssClasses.todayDay;
					} else {
						styleToUse = this.styles.todayDayWeekendDay;
						styleCssClassToUse = this.cssClasses.todayDayWeekendDay;
					}
				} else {
					if (isWeekday) {
						styleToUse = this.styles.day;
						styleCssClassToUse = this.cssClasses.day;
					} else {
						styleToUse = this.styles.weekendDay;
						styleCssClassToUse = this.cssClasses.weekendDay;
					}
				}
				break;
			case "prev":
			case "next":
				if (isWeekday) {
					styleToUse = this.styles.otherMonthDay;
					styleCssClassToUse = this.cssClasses.otherMonthDay;
				} else {
					styleToUse = this.styles.otherMonthDayWeekendDay;
					styleCssClassToUse = this.cssClasses.otherMonthDayWeekendDay;
				}
				break;
		}

		var sx = null, specialDateSelectable = true, specialDateOnClick = "return false", specialDateText = "";
		if (this.getSpecialDates() !== null) {
			sx = this.getSpecialDates().getByDate(date);
			for (var i = 0; i < sx.length; i++) {
				styleToUse += sx[i].style;
				styleCssClassToUse += " " + sx[i].cssClass;
				specialDateSelectable = sx[i].selectable;
				if (sx[i].text.length > 0) {
					if (i !== 0) {
						if ($COOL.isIE || $COOL.isOpera || $COOL.isSafari) {
							specialDateText += "\n";
						} else {
							specialDateText += ", ";
						}
					}
					specialDateText += sx[i].text;
				}

				if (!$COOL.isNullOrEmpty(sx[i].onclick)) {
					specialDateOnClick = "window['" + sx[i].onclick + "']({date:new Date(" + sx[i].date.getTime() + ")});";
				}
			}

			if (sx.length > 0) {
				specialDateSelectable = false;
			}

			for (var j = 0; j < sx.length; j++) {
				if (sx[j].selectable === true) {
					specialDateSelectable = true;
					break;
				}
			}
		}

		if (this.showWeekNumbers && date.getDay() == this.firstDayOfWeek) {
			var week;
			if (!$COOL.isNullOrEmpty(this.events.calculateWeekNumber)) {
				week = window[this.events.calculateWeekNumber](date);
			} else {
				week = date.getWeek();
			}
			o.push("<td", $COOL.buildStyleAttributes(this.styles.weekNumber, this.cssClasses.weekNumber), ">", week, "</td>");
		}

		o.push("<td", $COOL.buildStyleAttributes(styleToUse, styleCssClassToUse));
		if (!$COOL.isNullOrEmpty(this.dayStatusBarText)) {
			var statusText = this.dayStatusBarText;
			if (statusText.indexOf("{0}") != -1) {
				statusText = statusText.replace("{0}", (this.hasTimePicker()) ? date.toString(this.dateFormat + " " + this.getTimePicker().timeFormat) : date.toString(this.dateFormat));
			}
			o.push(' title="', statusText, '"');
			o.push(" onmouseover=\"window.status='", statusText, "';return true;\"");
			o.push(" onmouseout=\"window.status='';return true;\"");
		}
		o.push(">");
		if (type == "current" || (type == "prev" && this.showDaysInPrevMonth) || (type == "next" && this.showDaysInNextMonth)) {
			if (specialDateSelectable === true && (((isWeekday && this.selectableWeekDays) || (!isWeekday && this.selectableWeekendDays)) && (type == "current" || (type == "prev" && this.selectablePrevMonthDays) || (type == "next" && this.selectableNextMonthDays)) && (!this.maximumDate || this.maximumDate >= date) && (!this.minimumDate || this.minimumDate <= date))) {
				o.push('<a onclick="' + specialDateOnClick + '" d="', date.getTime(), '" class="bdpDayItem" href="#" title="', specialDateText, '">', date.getDate(), '</a>');
			} else {
				o.push(date.getDate());
			}
		} else {
			o.push("&nbsp;");
		}
		o.push("</td>");
		if (!$COOL.isNS6 && !$COOL.isNullOrEmpty(this.events.dayRender)) {
			var objectConverterDIV = document.createElement("DIV");
			objectConverterDIV.innerHTML = "<table><tr>" + o.join('') + "</tr></table>";
			var newTD = window[this.events.dayRender](this, { date: date, cell: objectConverterDIV.firstChild.firstChild.firstChild.firstChild });
			if (newTD !== null && newTD.tagName && newTD.tagName == "TD") {
				var objectConverterTR = document.createElement("TR");
				objectConverterTR.appendChild(newTD);
				return objectConverterTR.innerHTML;
			}
		}
		return o.join('');
	};

	this.clear = function() {
		this.setSelectedDate(null);
		if (this.hasTimePicker()) {
			this.getTimePicker().clear();
		}
		return this;
	};

	this.buildYearSelectorLinks = function(baseYear, optTimeoutChange) {
		var p, m, o = [];
		this.yearSelector.innerHTML = "";
		switch (this.upDownYearSelectorFormat) {
			case "Image":
				p = '<img src="' + this.northImage + '" />';
				m = '<img src="' + this.southImage + '" />';
				break;
			case "CustomText":
				p = this.downYearSelectorText;
				m = this.upYearSelectorText;
				break;
		}

		if (!$COOL.isNullOrEmpty(optTimeoutChange) && !$COOL.isNullOrEmpty(this.yearInc)) {
			baseYear = this.yearInc + optTimeoutChange;
		}

		var bottom = parseInt(baseYear.toString().substring(0, 3) + "0", 10);

		o.push('<a href="#" onclick="', this.clientID, '.buildYearSelectorLinks(', (bottom - 10), ');" class="bdpYearSelectorImg">', p, '</a>');

		for (var i = bottom; i <= bottom + 9; i++) {
			var className = (i == this.getVisibleDate().getFullYear()) ? "bdpYearSelectorSelectedItem" : "bdpYearSelectorItem";
			o.push('<a href="#" y="', i, '" onclick="return false;" class="bdpYearItem ', className, '">', i, '</a>');
		}

		o.push('<a href="#" onclick="', this.clientID, '.buildYearSelectorLinks(', (bottom + 10), ');" class="bdpYearSelectorImg">', m, '</a>');

		var children = this.yearSelector.childNodes;
		if (children) {
			for (var j = 0; j < children.length; j++) {
				this.yearSelector.removeChild(children[j]);
			}
		}

		this.yearSelector.innerHTML = o.join('');

		Coolite.Event.addListener(this.yearSelector, 'click', Coolite.Event.stopPropagation);

		var els = Coolite.Dom.getElementsByClassName("bdpYearItem", "a", this.yearSelector);

		for (i = 0; i < els.length; i++) {
			Coolite.Event.addListener(els[i], 'click', this._yearClick, this);
		}
		this.yearInc = baseYear;
	};

	this.selectRange = function(e, start, length) {
		var s = (!start || start < 1) ? 0 : start;
		var l = (isNaN(length) || length < 0) ? 1 : length;
		if (e.createTextRange) {
			var r = e.createTextRange();
			r.moveStart("character", s);
			r.moveEnd("character", s + l - e.value.length);
			r.select();
		} else if (e.setSelectionRange) {
			e.setSelectionRange(s, s + l);
		}
		e.focus();
	};

	/*
	* Public Methods 
	*/
	this.getText = function() {
		return $COOL.getInputValue(this.input);
	};

	this.getMaximumDate = function() {
		return (this.maximumDate !== null) ? this.maximumDate : new Date(9999, 11, 1);
	};

	this.setMaximumDate = function(date) {
		this.maximumDate = date;
		return this;
	};

	this.getMinimumDate = function() {
		return (this.minimumDate !== null) ? this.minimumDate : new Date(1, 0, 1);
	};

	this.setMinimumDate = function(date) {
		this.minimumDate = date;
		return this;
	};

	this.hasTimePicker = function() {
		return (!$COOL.isNullOrEmpty(this.timePickerID) && window[this.timePickerID]);
	};

	this.getTimePicker = function() {
		return (this.hasTimePicker()) ? window[this.timePickerID] : null;
	};

	this._setToToday = function() {
		var date = Date.today();
		if (this.hasTimePicker() && !this.getTimePicker().getIsNull()) {
			var time = this.getTimePicker().getSelectedTime();
			date.set({ hour: time.getHours(), minute: time.getMinutes(), second: time.getSeconds() });
		}
		return this.setSelectedDate(date);
	};

	this.setVisibleDate = function(date) {
		this.visibleDate = (date !== null) ? date.clone().clearTime().moveToFirstDayOfMonth() : Date.today().moveToFirstDayOfMonth();
		return this;
	};

	this.getVisibleDate = function() {
		if (this.visibleDate === null) {
			this.visibleDate = Date.today().moveToFirstDayOfMonth();
		}
		return this.visibleDate;
	};

	this.showNextMonth = function() {
		this.showMonth(this.getVisibleDate().addMonths(1));
		return this;
	};

	this.showPrevMonth = function() {
		this.showMonth(this.getVisibleDate().addMonths(-1));
		return this;
	};

	this.showMonth = function(date) {
		if (!$COOL.isNullOrEmpty(this.events.beforeVisibleMonthChanged)) {
			window[this.events.beforeVisibleMonthChanged](this, { newVisibleDate: date });
		}

		this.render(this.getSelectedDate(), date);

		if (!$COOL.isNullOrEmpty(this.events.afterVisibleMonthChanged)) {
			window[this.events.afterVisibleMonthChanged](this, null);
		}

		return this;
	};

	this.setSelectedDate = function(date) {
		if (!this.getEnabled() || !this._isApplied) {
			return this;
		}

		var s = "", d = (date || null), bs = s;
		var text = this.getText();

		if (d === null || d == this.nullDate || d === 0) {
			this.setVisibleDate(this.initVisibleDate);
			s = this.nullDateText;
			bs = this.buttonText;
		} else if (d instanceof Date) {
			s = d.toString(this.dateFormat);

			if (this.hasTimePicker()) {
				var time = this.getTimePicker().getSelectedTime();
				if (time === null) {
					this.getTimePicker().setSelectedTime(d.getTimeOfDay());
				}
			}
		}

		if (text != s) {
			if (!$COOL.isNullOrEmpty(this.events.beforeSelectionChanged)) {
				window[this.events.beforeSelectionChanged](this, { oldDate: this.getSelectedDate(), newDate: d });
			}

			$COOL.setInputValue(this.input, s);
			if (this.displayType == "Button") {
				this.button.value = s;
			} else if (this.displayType == "HyperLink") {
				this.button.innerHTML = s;
			} else if (this.displayType == "Label") {
				this.button.innerHTML = s;
			}

			if (this.label) {
				this.label.innerHTML = s;
			}

			this.clearTimers();

			if (!$COOL.isNullOrEmpty(this.events.afterSelectionChanged)) {
				window[this.events.afterSelectionChanged](this, null);
			}

			if (this.autoPostBack) {
				eval(this.postBackFunction);
			}
		}
		if (this.isOpen && d !== null) {
			this.render(d, d.clone().moveToFirstDayOfMonth());
		}
		return this;
	};

	this.getSelectedDate = function() {
		if (!this._isApplied) {
			return this;
		}

		var date = null, tx = this.getText();

		if (!$COOL.isNullOrEmpty(this.events.preParse)) {
			date = window[this.events.preParse](this, { value: tx });
			if (typeof date == "string") {
				tx = date;
				date = null;
			}
		}

		if (!(date instanceof Date)) {
			date = Date.parseExact(tx + " 12:00:00", this.dateFormat + " hh:mm:ss");
			if (date === null) {
				date = Date.parse(tx + " 12:00:00");
			}
		}

		this._isDate = ($COOL.isNullOrEmpty(tx) || (tx.length > 0 && date !== null));

		if (this.hasTimePicker()) {
			var time = this.getTimePicker().getSelectedTime();
			if (time !== null) {
				if (date === null) {
					return null;
				}
				return date.set({ hour: time.getHours(), minute: time.getMinutes(), second: time.getSeconds() });
			}
		}
		return date;
	};

	this.getSelectedDateFormatted = function() {
		return (this.getIsNull()) ? "" : this.getSelectedDate().toString(this.dateFormat);
	};

	this.getSelectedDateTimeFormatted = function() {
		var text = this.getSelectedDateFormatted();
		if (this.hasTimePicker()) {
			text += " " + this.getTimePicker().getSelectedTimeFormatted();
		}
		return text;
	};

	this.getSpecialDates = function() {
		return (!$COOL.isNullOrEmpty(this.specialDatesID) && window[this.specialDatesID] && window[this.specialDatesID].dates.length > 0) ? window[this.specialDatesID] : null;
	};

	this.getEnabled = function() {
		return this.enabled;
	};

	this.setEnabled = function(enabled) {
		this.input.disabled = !enabled;
		if (this.input.parentElement) {
			this.input.parentElement.disabled = !enabled;
		}
		if (this.button) {
			this.button.disabled = !enabled;
		}
		this.enabled = enabled;
		return this;
	};

	this.getIsDate = function() {
		this.getSelectedDate();
		return this._isDate;
	};

	this.getIsNull = function() {
		return (this.getSelectedDate() === null);
	};

	this.focus = function() {
		if (this.input.style.display != "none") {
			if (!this.showCalendarOnTextBoxFocus) {
				this.input.focus();
			}
		}
		return this;
	};
};

/* Event Handlers */
Coolite.DatePicker.prototype._buttonClick = function(e, el) {
	Coolite.Event.stopEvent(e);
	el.toggle();
};

Coolite.DatePicker.prototype._inputFocus = function(e, el) {
	Coolite.Event.stopEvent(e);
	if (el.showCalendarOnTextBoxFocus) {
		el.toggle();
	} else {
		Coolite.DatePicker.closeAll();
	}
	el.resetToggler().clickToggle = false;
};

Coolite.DatePicker.prototype._inputClick = function(e, el) {
	Coolite.Event.stopEvent(e);
	if (el.showCalendarOnTextBoxFocus) {
		if (el.clickToggle) {
			el.toggle();
		} else {
			el.clickToggle = true;
		}
	} else {
		Coolite.DatePicker.closeAll();
	}
};

Coolite.DatePicker.prototype.resetToggler = function() {
	setTimeout(function() {
		this.clickToggle = true;
	}, 100);
	return this;
};

Coolite.DatePicker.prototype.toggle = function() {
	if (this.isOpen) {
		Coolite.DatePicker.closeAll();
	} else {
		Coolite.DatePicker.closeAll();
		this.show();
	}
	return this;
};

Coolite.DatePicker.prototype._inputBlur = function(ev, el) {
	if (el.getText() !== el.inputFocusValue && el.getIsDate()) {
		el.setSelectedDate(el.getSelectedDate());
	}
	Coolite.DatePicker.closeAll();
};

Coolite.DatePicker.prototype._inputKeyDown = function(ev, el) {
	var c = Coolite.Event.getCharCode(ev);
	var s = $COOL.getPositionStart(el.input);
	if (c == 8 || c == 9) {
		Coolite.DatePicker.closeAll();
		return;
	}
	if ((c == 33 || c == 34 || c == 38 || c == 40)) {
		if (el.getIsNull()) {
			el.setSelectedDate(Date.today());
		}
		switch (c) {
			case 38:
				if (ev.ctrlKey) {
					el.setSelectedDate(el.getSelectedDate().addYears(1));
				} else if (ev.altKey) {
					el.setSelectedDate(el.getSelectedDate().addMonths(1));
				} else {
					el.setSelectedDate(el.getSelectedDate().addDays(1));
				}
				el.selectRange(el.input, s, 1);
				break;
			case 40:
				if (ev.ctrlKey) {
					el.setSelectedDate(el.getSelectedDate().addYears(-1));
				} else if (ev.altKey) {
					el.setSelectedDate(el.getSelectedDate().addMonths(-1));
				} else {
					el.setSelectedDate(el.getSelectedDate().addDays(-1));
				}
				el.selectRange(el.input, s, 1);
				break;
		}
	}
};

Coolite.DatePicker.prototype._documentKeyPress = function(e, el) {
	if (!e) {
		e = winodow.event;
	}
	if (e.keyCode == 9 || e.keyCode == 13) {
		Coolite.DatePicker.closeAll();
	}
};

Coolite.DatePicker.prototype._documentClick = function(e, el) {
	if (el.getText() !== el.inputFocusValue) {
		el.setSelectedDate(el.getSelectedDate());
	}
	Coolite.DatePicker.closeAll();
};

Coolite.DatePicker.prototype._calendarClick = function(e) {
	Coolite.Event.stopEvent(e);
};

Coolite.DatePicker.prototype._scroll = function(e) {
	var delta = 0;
	if (!e) {
		e = window.event;
	}
	if (e.wheelDelta) {
		delta = e.wheelDelta / 120;
	} else if (e.detail) {
		delta = -e.detail / 3;
	}
	if (delta) {
		if (delta < 0) {
			this.showNextMonth();
		} else {
			this.showPrevMonth();
		}
	}

	if (e.preventDefault) {
		e.preventDefault();
	}
	e.returnValue = false;
};

Coolite.DatePicker.prototype._prevMonthMouseDown = function() {
	this.interval = setInterval(this.clientID + ".showPrevMonth()", 50);
};

Coolite.DatePicker.prototype._nextMonthMouseDown = function() {
	this.interval = setInterval(this.clientID + ".showNextMonth()", 50);
};

Coolite.DatePicker.prototype._monthSelectorClick = function(span) {
	this.hideSelectors();
	if (!this.monthSelector) {
		this.monthSelector = document.createElement("DIV");
		this.monthSelector.id = "basicDatePickerMonthSelector";
		this.monthSelector.className = "bdpMonthSelector";
		this.monthSelector.style.position = "absolute";
		this.monthSelector.style.zIndex = "100003";
		document.body.appendChild(this.monthSelector);
	}
	this.monthSelector.innerHTML = "";
	var o = [];
	var monthNames = Date.CultureInfo.monthNames;
	for (var i = 0; i < monthNames.length; i++) {
		var className = (i == this.getVisibleDate().getMonth()) ? "bdpMonthSelectorSelectedItem" : "bdpMonthSelectorItem";
		o.push('<a href="#" m="', i, '" onclick="return false;" class="bdpMonthItem ', className, '">', monthNames[i], '</a>');
	}
	this.monthSelector.innerHTML = o.join('');
	Coolite.Event.addListener(this.monthSelector, 'click', Coolite.Event.stopPropagation);

	var els = Coolite.Dom.getElementsByClassName("bdpMonthItem", "a", this.monthSelector);

	for (i = 0; i < els.length; i++) {
		Coolite.Event.addListener(els[i], 'click', this._monthClick, this);
	}
	var pos = Coolite.Dom.getXY(span);
	this.monthSelector.style.top = (parseInt(pos[1] + span.offsetHeight + this.monthSelectorYOffset, 10)) + "px";
	this.monthSelector.style.left = (parseInt(pos[0] + this.monthSelectorXOffset, 10)) + "px";
	this.monthSelectorShim = $COOL.makeShim(this.monthSelectorShim, this.monthSelector);
	this.monthSelector.style.visibility = "visible";
};

Coolite.DatePicker.prototype._monthClick = function(ev, el) {
	el.showMonth(el.getVisibleDate().set({ month: parseInt(this.getAttribute("m"), 10) }));
};

Coolite.DatePicker.prototype._yearSelectorClick = function(item) {
	this.hideSelectors();
	if (!this.yearSelector) {
		this.yearSelector = document.createElement("DIV");
		this.yearSelector.id = "basicDatePickerYearSelector";
		this.yearSelector.className = "bdpYearSelector";
		this.yearSelector.style.position = "absolute";
		this.yearSelector.style.zIndex = "100003";
		document.body.appendChild(this.yearSelector);
	}

	var pos = Coolite.Dom.getXY(item);
	this.buildYearSelectorLinks(this.getVisibleDate().getFullYear());
	this.yearSelector.style.top = (parseInt(pos[1] + item.offsetHeight + this.monthSelectorYOffset, 10)) + "px";
	this.yearSelector.style.left = (parseInt(pos[0] + this.monthSelectorXOffset, 10)) + "px";
	this.yearSelectorShim = $COOL.makeShim(this.yearSelectorShim, this.yearSelector);
	this.yearSelector.style.visibility = "visible";
};

Coolite.DatePicker.prototype._yearClick = function(e, el) {
	el.showMonth(el.getVisibleDate().set({ year: parseInt(this.getAttribute('y'), 10) }));
};

Coolite.DatePicker.closeAll = function() {
	for (var i = 0; i < $COOL.controls.length; i++) {
		if ($COOL.controls[i].type == "DatePicker") {
			$COOL.controls[i].control.close();
		}
	}
};

Coolite.DatePicker.showAll = function() {
	for (var i = 0; i < $COOL.controls.length; i++) {
		if ($COOL.controls[i].type == "DatePicker") {
			$COOL.controls[i].control.show();
		}
	}
};
