/**
* @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, "yyyy/M/d");
    this.initVisibleDate = (this.visibleDate !== null) ? this.visibleDate.clone() : null;
    this.maximumDate = Date.parseExact(this.maximumDate, "yyyy/M/d");
    this.minimumDate = Date.parseExact(this.minimumDate, "yyyy/M/d");
    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) ? visibleDate : visibleDate.addMonths(1), selectedDate, 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, this.dateFormat);
            if (date === null) {
                date = Date.parse(tx);
            }
        }

        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();
        }
    }
};