/*
 *
 * Wijmo Library 3.20173.126
 * http://wijmo.com/
 *
 * Copyright(c) GrapeCity, Inc.  All rights reserved.
 * 
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * licensing@wijmo.com
 * http://wijmo.com/widgets/license/
 * ----
 * Credits: Wijmo includes some MIT-licensed software, see copyright notices below.
 */
/// <reference path="../wijutil/jquery.wijmo.wijutil.ts"/>
/// <reference path="../Base/jquery.wijmo.widget.ts"/>
/// <reference path="../wijpopup/jquery.wijmo.wijpopup.ts"/>
/// <reference path="../external/declarations/globalize.d.ts"/>
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var wijmo;
(function (wijmo) {
    /*globals jQuery,window,document*/
    /*
    * Depends:
    *     jquery.ui.core.js
    *     jquery.ui.widget.js
    *     jquery.wijmo.wijtooltip.js
    */
    (function (_calendar) {
        var $ = jQuery, widgetName = "wijcalendar";

        var wijDayType = {
            /** A general day cell, denotes nothing.*/
            general: 0,
            /** A weekend day cell.*/
            weekEnd: 1,
            /** A day cell with a date blongs to other month.*/
            otherMonth: 2,
            /** A day cell with a date out of the minDate/maxDate range.*/
            outOfRange: 4,
            /** A day cell represents today.*/
            today: 8,
            /** A custom day cell, which has CSS class 'wijmo-wijcalendar-customday' associated.*/
            custom: 16,
            /** A day cell in disabled state.*/
            disabled: 32,
            /** A day cell in selected state.*/
            selected: 64,
            /** A blank day cell.*/
            gap: 128
        };

        /** @widget */
        var wijcalendar = (function (_super) {
            __extends(wijcalendar, _super);
            function wijcalendar() {
                _super.apply(this, arguments);
                this._touchRangeSelect = false;
            }
            wijcalendar.prototype._create = function () {
                var self = this, wijCSS = self.options.wijCSS;
                self.element.uniqueId();
                self._originalStyle = self.element.attr("style");

                // enable touch support:
                if (window.wijmoApplyWijTouchUtilEvents) {
                    $ = window.wijmoApplyWijTouchUtilEvents($);
                }

                // Add for parse date options for jUICE. D.H
                if ($.isFunction(window["wijmoASPNetParseOptions"])) {
                    wijmoASPNetParseOptions(this.options);
                }

                this.element.addClass("wijmo-wijcalendar  " + wijCSS.datepickerInline + " " + wijCSS.datepicker + " " + wijCSS.widget + " " + wijCSS.content + " " + wijCSS.helperClearFix + " " + wijCSS.cornerAll).attr('role', 'grid');
                this._previewWrapper(this.options.allowPreview);
                this.element.data('preview.wijcalendar', false);

                //update for visibility change
                if (self.element.is(":hidden") && self.element.wijAddVisibilityObserver) {
                    self.element.wijAddVisibilityObserver(function () {
                        self.refresh();
                        if (self.element.wijRemoveVisibilityObserver) {
                            self.element.wijRemoveVisibilityObserver();
                        }
                    }, "wijcalendar");
                }

                _super.prototype._create.call(this);
            };

            wijcalendar.prototype._init = function () {
                var self = this, o = self.options;
                if (o.popupMode) {
                    var po = {
                        autoHide: !!o.autoHide,
                        showing: undefined,
                        shown: undefined,
                        hiding: undefined,
                        hidden: undefined
                    };
                    if (o.beforePopup) {
                        po.showing = o.beforePopup;
                    }
                    if (o.afterPopup) {
                        po.shown = o.afterPopup;
                    }
                    if (o.beforeClose) {
                        po.hiding = o.beforeClose;
                    }

                    po.hidden = function (data) {
                        self.element.removeData("lastdate.wijcalendar");
                        if (o.afterClose) {
                            o.afterClose.call(data);
                        }
                    };

                    self.element.wijpopup(po).bind("wijpopupshown", function (e) {
                        self._renderInitialView();
                    });
                } else {
                    self.element.removeAttr("style");
                    if (self._originalStyle) {
                        self.element.attr("style", self._originalStyle);
                    }
                }

                self._getSelectedDates();
                self._getDisabledDates();
                self._resetWidth();
                self.refresh();

                //this.element.width(this.element.width() + 2);
                if (!o.popupMode) {
                    self._renderInitialView();
                }
            };

            wijcalendar.prototype._renderInitialView = function () {
                var self = this, viewType = self.options.initialView;
                if (($.inArray(viewType, ["month", "year", "decade"]) >= 0) && (self.options.monthCols == 1 && self.options.monthRows == 1)) {
                    if (self._myGrid === undefined) {
                        self._getMyGrid();
                    }
                    self._myGrid.gridType = viewType;
                    self._updateMyGrid(0, false);
                }
            };

            /**
            * Remove the functionality completely. This will return the element back to its pre-init state.
            */
            wijcalendar.prototype.destroy = function () {
                var wijCSS = this.options.wijCSS;

                //$.wijmo.widget.prototype.destroy.apply(this, arguments);
                _super.prototype.destroy.call(this);
                this._innerCulture = null;
                this.close();
                this._unbindEvents();
                this.element.html("");
                this.element.removeClass("wijmo-wijcalendar " + wijCSS.datepickerInline + " " + wijCSS.datepicker + " " + wijCSS.widget + " " + wijCSS.content + " " + wijCSS.helperClearFix + " " + wijCSS.cornerAll + " " + wijCSS.datepickerMulti).removeAttr('role');

                var self = this;
                $.each([
                    "preview", "disableddates", "selecteddates", "dragging",
                    "lastdate", "animating"], function (i, prefix) {
                    self.element.removeData(prefix + ".wijcalendar");
                });

                this._previewWrapper(false);
            };

            wijcalendar.prototype._setOption = function (key, value) {
                //$.wijmo.widget.prototype._setOption.apply(this, arguments);
                if (key === "disabled" && value === this.options.disabled) {
                    return;
                }

                if (key === "popupMode" && value === this.options.popupMode) {
                    return;
                }

                _super.prototype._setOption.call(this, key, value);

                switch (key) {
                    case "showWeekDays":
                    case "showWeekNumbers":
                    case "showTitle":
                    case "showOtherMonthDays":
                    case "selectionMode":
                        this.unSelectAll();
                        this._resetWidth();
                        this.refresh();
                        break;

                    case "culture":
                    case "cultureCalendar":
                        this._innerCulture = null;
                        this.refresh();
                        break;

                    case "allowPreview":
                        this._previewWrapper(value);
                        this.refresh();
                        break;

                    case "monthCols":
                        this._resetWidth();
                        this.refresh();
                        break;

                    case "monthRows":
                        if (this._myGrid) {
                            this._myGrid = undefined;
                        }
                        break;
                    case "autoHide":
                        this.element.wijpopup({ autoHide: this.options.autoHide });
                        break;

                    case "selectedDates":
                        this._getSelectedDates().setDates(value);
                        this.refresh();
                        break;

                    case "disabledDates":
                        this._getDisabledDates().setDates(value);
                        this.refresh();
                        break;
                    case "displayDate":
                    case "allowQuickPick":
                    case "navButtons":
                    case "minDate":
                    case "maxDate":
                        this.refresh();
                        break;
                    case "popupMode":
                        if (value === false) {
                            this.element.wijpopup("destroy");
                        }
                        this._unbindEvents();
                        this._init();
                        break;
                }
            };

            wijcalendar.prototype._innerDisable = function () {
                _super.prototype._innerDisable.call(this);
                this._unbindEvents();
            };

            wijcalendar.prototype._innerEnable = function () {
                _super.prototype._innerEnable.call(this);
                this.refresh();
            };

            wijcalendar.prototype._previewWrapper = function (add) {
                if (add) {
                    if (!this.element.parent().hasClass('wijmo-wijcalendar-preview-wrapper')) {
                        this.element.wrap("<div class='wijmo-wijcalendar-preview-wrapper" + " " + this.options.wijCSS.helperClearFix + "'></div>");
                    }
                } else {
                    if (this.element.parent().hasClass('wijmo-wijcalendar-preview-wrapper')) {
                        this.element.unwrap();
                    }
                }
            };

            wijcalendar.prototype._isRTL = function () {
                return !!this._getCulture(null).isRTL;
            };

            /** Refreshes the calendar.*/
            wijcalendar.prototype.refresh = function () {
                if (this._myGrid) {
                    this._myGrid = undefined;
                }
                this.element.empty().append(this._createCalendar());
                this.element[(this._isRTL() ? 'add' : 'remove') + 'Class'](this.options.wijCSS.datepickerRtl);
                if (!this.options.showTitle) {
                    this.element.addClass("wijmo-wijcalendar-notitle");
                } else {
                    this.element.removeClass("wijmo-wijcalendar-notitle");
                }
                this._bindEvents();
            };

            /** Refereshes a single date on the calendar.
            * @param {Date} date The date to be refreshed.
            */
            wijcalendar.prototype.refreshDate = function (date) {
                if (!this._monthViews) {
                    return;
                }
                if (date < this._groupStartDate || date > this._groupEndDate) {
                    return;
                }
                $.each(this._monthViews, function () {
                    this._refreshDate(date);
                });
            };

            /** Gets the valid display date.*/
            wijcalendar.prototype.getDisplayDate = function () {
                var d = this.options.displayDate ? this.options.displayDate : new Date();
                if (wijDateOps.isSameDate(d, new Date(1900, 0, 1))) {
                    d = new Date();
                }

                return d;
            };

            /** Gets the currently selected date.*/
            wijcalendar.prototype.getSelectedDate = function () {
                var dates = this.options.selectedDates;
                return (!dates || dates.length === 0) ? null : dates[0];
            };

            /** Selects the specified date.
            * @param {Date} date The date to be selected.
            */
            wijcalendar.prototype.selectDate = function (date) {
                date = new Date(date);
                if (this._getDisabledDates().contains(date)) {
                    return false;
                }
                if (date < this.options.minDate || date > this.options.maxDate) {
                    return false;
                }

                this._getSelectedDates().add(date);
                this.refreshDate(date);
                return true;
            };

            /** Clears any selection from the specified date.
            * @param {Date} date The date to be removed from the selectedDates collection.
            */
            wijcalendar.prototype.unSelectDate = function (date) {
                date = new Date(date);
                if (this._getDisabledDates().contains(date)) {
                    return false;
                }
                if (date < this.options.minDate || date > this.options.maxDate) {
                    return false;
                }

                this._getSelectedDates().remove(date);
                this.refreshDate(date);
                return true;
            };

            /** Clears any selections from dates on the calendar, removing them from the selectedDates collection.*/
            wijcalendar.prototype.unSelectAll = function () {
                var dates = this.options.selectedDates, i;
                if (dates && dates.length > 0) {
                    this._getSelectedDates().clear();
                    for (i = 0; i < dates.length; i++) {
                        this.refreshDate(dates[i]);
                    }
                }
            };

            wijcalendar.prototype._slideToDate = function (date) {
                if (wijDateOps.isSameMonth(this.getDisplayDate(), date)) {
                    return;
                }
                var visible = this.element.is(":visible");
                if (!visible) {
                    this.options.displayDate = date;
                } else {
                    if (this._trigger('beforeSlide') === false) {
                        return;
                    }

                    if (this._isSingleMonth()) {
                        this._playSlideAnimation(date);
                    } else {
                        this._playMmSlideAnimation(date);
                    }
                }
            };

            /** Determines whether the calendar is in the pop-up state.*/
            wijcalendar.prototype.isPopupShowing = function () {
                return !!this.options.popupMode ? this.element.wijpopup('isVisible') : false;
            };

            /** Pops up the calendar at specifies position.
            * @param {Object} position  A jQuery Position plugin that indicates the position in which to pop up the calendar.
            *                    Please see "http://jqueryui.com/demos/position/" for details of the parameter.
            */
            wijcalendar.prototype.popup = function (position) {
                this._myGrid = undefined;
                this.refresh();
                this.element.data('dragging.wijcalendar', false);
                if (this.element.data("wijmo-wijpopup")) {
                    this.element.wijpopup('show', position);
                }
            };

            /** Pops up the calendar at the specified X and Y coordinates in the document.
            * @param {number} x X offset.
            * @param {number} y Y offset.
            */
            wijcalendar.prototype.popupAt = function (x, y) {
                this._myGrid = undefined;
                this.refresh();
                this.element.data('dragging.wijcalendar', false);
                if (this.element.data("wijmo-wijpopup")) {
                    this.element.wijpopup('showAt', x, y);
                }
            };

            /** Closes the calendar if it is in the pop-up state.*/
            wijcalendar.prototype.close = function () {
                if (this.isPopupShowing()) {
                    this.element.wijpopup('hide');
                }
            };

            wijcalendar.prototype._getCulture = function (name) {
                if (!this._innerCulture) {
                    this._resetCulture(name);
                }
                return this._innerCulture;
            };
            wijcalendar.prototype._resetCulture = function (name) {
                var cal = $.wijGetCulture(name || this.options.culture, this.options.cultureCalendar);
                this._innerCulture = cal;
            };

            wijcalendar.prototype._getDates = function (token) {
                var name = token.toLowerCase() + ".wijcalendar", dates = this.element.data(name);
                if (dates === undefined) {
                    dates = new wijDateCollection(this, token);
                    this.element.data(name, dates);
                }
                return dates;
            };

            wijcalendar.prototype._getDisabledDates = function () {
                return this._getDates('disabledDates');
            };

            wijcalendar.prototype._getSelectedDates = function () {
                return this._getDates('selectedDates');
            };

            wijcalendar.prototype._onDayDragStart = function (e) {
                e.preventDefault();
                e.stopPropagation();
                return false;
            };

            wijcalendar.prototype._triggerSelectedDatesChanged = function (selected, dates) {
                var o = this.options, self = this;

                if (selected && !!o.selectionMode.days) {
                    self.element.data('dragging.wijcalendar', true);
                    $(document.body).one("mouseup." + self.widgetName, function () {
                        self.element.data('dragging.wijcalendar', false);
                    });
                }
                self._trigger('selectedDatesChanged', null, { dates: dates });
            };

            wijcalendar.prototype._onDayMouseDown = function (e) {
                e.preventDefault();
                e.stopPropagation();

                var o = this.options, self = this, date, args, selected = false, selDates = o.selectedDates, exist = false, dates = [];

                if (e.which !== 1) {
                    if (e.which === 0) {
                        this._onTouchstart(e);
                    }
                    return false;
                }

                if ($.browser.msie) {
                    if (parseFloat($.browser.version) >= 10) {
                        if (e.originalEvent.pointerType) {
                            if (e.originalEvent.pointerType === 2 || e.originalEvent.pointerType === "touch") {
                                this._onTouchstart(e);
                                return false;
                            }
                        } else if (e.originalEvent.originalEvent && e.originalEvent.originalEvent.pointerType) {
                            if (e.originalEvent.originalEvent.pointerType === 2 || e.originalEvent.originalEvent.pointerType === "touch") {
                                this._onTouchstart(e);
                                return false;
                            }
                        }
                    }
                }

                date = this._getCellDate(e.currentTarget);
                if (date === undefined) {
                    return false;
                }
                if (!o.selectionMode.day) {
                    return false;
                }

                args = { date: date };
                if (this._trigger("beforeSelect", null, args) === false) {
                    return false;
                }

                if (!o.selectionMode.days || (!e.metaKey && !e.shiftKey && !e.ctrlKey)) {
                    this.unSelectAll();
                }

                if (!!o.selectionMode.days) {
                    if (e.shiftKey && this.element.data("lastdate.wijcalendar")) {
                        this._selectRange(this.element.data("lastdate.wijcalendar"), date, null, true);
                        selected = true;
                    } else if (e.ctrlKey) {
                        this.element.data("lastdate.wijcalendar", date);

                        $.each(selDates, function (i, d) {
                            if (date.getFullYear() === d.getFullYear() && date.getMonth() === d.getMonth() && date.getDate() === d.getDate()) {
                                exist = true;
                                return false;
                            }
                        });

                        if (exist) {
                            this.unSelectDate(date);
                        } else {
                            this.selectDate(date);
                        }

                        selDates = o.selectedDates;
                        $.each(selDates, function (i, d) {
                            dates.push(new Date(d));
                        });

                        selected = true;
                        this._triggerSelectedDatesChanged(selected, dates);
                    }
                }

                if (!selected) {
                    this.element.data("lastdate.wijcalendar", date);
                    selected = this.selectDate(date);
                    this._triggerSelectedDatesChanged(selected, [date]);
                }
                if (selected) {
                    this._trigger('afterSelect', null, args);
                }

                return false;
            };

            wijcalendar.prototype._onMouseUp = function (e) {
                e.preventDefault();
                e.stopPropagation();

                //var self = this;
                this.element.data('dragging.wijcalendar', false);

                return false;
            };

            wijcalendar.prototype._onDayClicked = function (e) {
                var date = this._getCellDate(e.currentTarget);
                if (date === undefined) {
                    return false;
                }
                if (!this.options.selectionMode.day) {
                    return false;
                }

                if (this.isPopupShowing()) {
                    this.close();
                } else {
                    if ($(e.currentTarget).hasAllClasses(this.options.wijCSS.datepickerOtherMonth)) {
                        this._slideToDate(date);
                    }
                }

                return false;
            };

            wijcalendar.prototype._onDayMouseEnter = function (e) {
                $(e.currentTarget).attr('state', 'hover');
                this._refreshDayCell(e.currentTarget);

                if (!!this.element.data('dragging.wijcalendar')) {
                    var date = this._getCellDate(e.currentTarget);
                    if (date === undefined) {
                        return;
                    }

                    this.unSelectAll();
                    this._selectRange(this.element.data("lastdate.wijcalendar"), date, true);
                }
            };

            wijcalendar.prototype._onDayMouseLeave = function (e) {
                $(e.currentTarget).attr('state', 'normal');
                this._refreshDayCell(e.currentTarget);
            };

            wijcalendar.prototype._onTouchstart = function (e) {
                this._touchStartTime = new Date();
            };

            wijcalendar.prototype._onTouchend = function (e) {
                e.preventDefault();
                e.stopPropagation();

                var touchEndTime = new Date(), timeDiff = touchEndTime - this._touchStartTime, o = this.options, self = this, date, args, selDates = o.selectedDates, exist = false, dates = [];

                this.element.data('dragging.wijcalendar', false);

                if ($.browser.msie) {
                    if (parseFloat($.browser.version) >= 10) {
                        if (e.originalEvent.pointerType) {
                            if (e.originalEvent.pointerType !== 2 && e.originalEvent.pointerType !== "touch") {
                                return false;
                            }
                        } else if (e.originalEvent.originalEvent.pointerType) {
                            if (e.originalEvent.originalEvent.pointerType !== 2 && e.originalEvent.originalEvent.pointerType !== "touch") {
                                return false;
                            }
                        } else {
                            return false;
                        }
                    } else {
                        return false;
                    }
                } else if (e.which !== 0) {
                    return false;
                }

                date = this._getCellDate(e.currentTarget);
                if (date === undefined) {
                    return false;
                }
                if (!o.selectionMode.day) {
                    return false;
                }

                args = { date: date };
                if (this._trigger("beforeSelect", null, args) === false) {
                    return false;
                }

                $.each(selDates, function (i, d) {
                    if (date.getFullYear() === d.getFullYear() && date.getMonth() === d.getMonth() && date.getDate() === d.getDate()) {
                        exist = true;
                        return false;
                    }
                });

                if (!this._touchRangeSelect && timeDiff >= 750) {
                    this.element.data("lastdate.wijcalendar", date);
                    if (exist) {
                        if (!!o.selectionMode.days) {
                            this.unSelectDate(date);
                        }
                    } else {
                        if (!o.selectionMode.days) {
                            this.unSelectAll();
                        } else {
                            this._touchRangeSelect = true;
                        }
                        this.selectDate(date);
                    }
                    selDates = o.selectedDates;
                    $.each(selDates, function (i, d) {
                        dates.push(new Date(d));
                    });

                    if (selDates.length > 0) {
                        this._trigger('selectedDatesChanged', null, { dates: dates });
                    }
                } else {
                    if (!o.selectionMode.days) {
                        this.unSelectAll();
                        this._touchRangeSelect = false;
                    }

                    if (!this._touchRangeSelect) {
                        this.unSelectAll();
                        this.element.data("lastdate.wijcalendar", date);
                        this.selectDate(date);
                        this._trigger('selectedDatesChanged', null, { dates: [date] });
                    } else {
                        if (timeDiff >= 750) {
                            if (!!o.selectionMode.days) {
                                this.element.data("lastdate.wijcalendar", date);

                                if (exist) {
                                    this.unSelectDate(date);
                                    this._touchRangeSelect = false;
                                } else {
                                    this.selectDate(date);
                                }

                                selDates = o.selectedDates;
                                $.each(selDates, function (i, d) {
                                    dates.push(new Date(d));
                                });

                                this._trigger('selectedDatesChanged', null, { dates: dates });
                            }
                        }

                        if (timeDiff < 750 && this.element.data("lastdate.wijcalendar")) {
                            this._selectRange(this.element.data("lastdate.wijcalendar"), date, null);
                            if (this._touchRangeSelect)
                                this._touchRangeSelect = false;
                        }
                    }
                }

                this._trigger('afterSelect', null, args);

                this._touchStartTime = null;

                return false;
            };

            wijcalendar.prototype._selectRange = function (start, end, bymouse, shiftMouseDown) {
                var self = this, triggerSelectedDatesChanged = function (dates) {
                    !!shiftMouseDown ? self._triggerSelectedDatesChanged(true, dates) : self._trigger('selectedDatesChanged', null, { dates: dates });
                };
                if (start !== undefined && start !== new Date(1900, 1, 1)) {
                    var minDate = start, maxDate = end, selDates = [];
                    if (start > end) {
                        maxDate = start;
                        minDate = end;
                    }

                    while (true) {
                        if (minDate > maxDate) {
                            break;
                        }
                        this.selectDate(minDate);
                        selDates[selDates.length] = minDate;
                        minDate = wijDateOps.addDays(minDate, 1);
                    }
                    if (!bymouse) {
                        this.element.removeData("lastdate.wijcalendar");
                    }

                    triggerSelectedDatesChanged(selDates);
                } else {
                    this.selectDate(start);
                    triggerSelectedDatesChanged([start]);
                }

                return true;
            };

            wijcalendar.prototype._getCellDate = function (c) {
                var d = $(c).attr('date');
                return (d === undefined) ? d : new Date(d);
            };

            wijcalendar.prototype._getParentTable = function (c) {
                var parents = $(c).parents('table');
                return (parents.length === 0) ? undefined : parents.get(0);
            };

            wijcalendar.prototype._initMonthSelector = function (ms) {
                if ($(ms).data('cells') !== undefined) {
                    return;
                }

                var tokens = ms.id.split('_'), monthID, monthTable, cells = [], i, j, td, dt, row;
                if (tokens[tokens.length - 1] !== 'ms') {
                    throw Error["create"]('not a monthview');
                }
                monthID = (tokens.slice(0, tokens.length - 1)).join('_');
                monthTable = this._getParentTable(ms);
                if (monthTable) {
                    if (monthTable.id !== monthID) {
                        throw Error["create"]('not a monthview');
                    }
                    for (i = 0; i < monthTable.rows.length; i++) {
                        row = monthTable.rows[i];
                        for (j = 0; j < row.cells.length; j++) {
                            td = row.cells[j];
                            if (td) {
                                dt = $(td).attr('daytype');
                                if (dt !== undefined) {
                                    if ($(td).find('a').hasAllClasses(this.options.wijCSS.prioritySecondary) === false) {
                                        if (this._isSelectable(parseInt(dt, 10))) {
                                            cells[cells.length] = td;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                $(ms).data('cells', cells);
            };

            wijcalendar.prototype._onMonthSelectorClicked = function (e) {
                this._initMonthSelector(e.currentTarget);
                var cells = $(e.currentTarget).data('cells'), selDates = [], i, c, d, date;

                this.element.removeData("lastdate.wijcalendar");
                this.unSelectAll();
                for (i = 0; i < cells.length; i++) {
                    c = cells[i];
                    d = $(c).attr('date');
                    if (d !== undefined) {
                        date = new Date(d);
                        this.selectDate(date);
                        selDates[selDates.length] = date;
                    }
                }

                this._trigger('selectedDatesChanged', null, { dates: selDates });
                if (this.isPopupShowing()) {
                    this.close();
                }

                return false;
            };

            wijcalendar.prototype._onMonthSelectorMouseEnter = function (e) {
                this._initMonthSelector(e.currentTarget);
                var cells = $(e.currentTarget).data('cells'), i;

                for (i = 0; i < cells.length; i++) {
                    e.currentTarget = cells[i];
                    this._onDayMouseEnter(e);
                }
            };

            wijcalendar.prototype._onMonthSelectorMouseLeave = function (e) {
                this._initMonthSelector(e.currentTarget);
                var cells = $(e.currentTarget).data('cells'), i;

                for (i = 0; i < cells.length; i++) {
                    e.currentTarget = cells[i];
                    this._onDayMouseLeave(e);
                }
            };

            wijcalendar.prototype._initWeekDaySelector = function (wd) {
                if ($(wd).data('cells') !== undefined) {
                    return;
                }

                var tokens = wd.id.split('_'), colIndex, monthID, monthTable, cells = [], i = 0, tr, td, dt;
                if (tokens[tokens.length - 2] !== 'cs') {
                    throw Error["create"]('not a column');
                }
                colIndex = parseInt(tokens[tokens.length - 1], 10);
                monthID = (tokens.slice(0, tokens.length - 2)).join('_');
                monthTable = this._getParentTable(wd);
                if (monthTable) {
                    if (monthTable.id !== monthID) {
                        throw Error["create"]('not a column');
                    }

                    /** update for issue 29995
                    if (!this._isSingleMonth()) {
                    i++;
                    }*/
                    if (this.options.showWeekDays) {
                        i++;
                    }
                    for (; i < monthTable.rows.length; i++) {
                        tr = monthTable.rows[i];
                        if (colIndex < tr.cells.length) {
                            td = tr.cells[colIndex];
                            if (td) {
                                dt = $(td).attr('daytype');
                                if (dt !== undefined) {
                                    if ($(td).find('a').hasAllClasses(this.options.wijCSS.prioritySecondary) === false) {
                                        if (this._isSelectable(parseInt(dt, 10))) {
                                            cells[cells.length] = td;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                $(wd).data('cells', cells);
            };

            wijcalendar.prototype._onWeekDayClicked = function (e) {
                this._initWeekDaySelector(e.currentTarget);
                var cells = $(e.currentTarget).data('cells'), selDates = [], i, c, d, date;

                this.unSelectAll();
                for (i = 0; i < cells.length; i++) {
                    c = $(cells[i]);
                    d = c.attr('date');
                    if (d !== undefined) {
                        date = new Date(d);
                        this.selectDate(date);
                        selDates[selDates.length] = date;
                    }
                }

                this._trigger('selectedDatesChanged', null, { dates: selDates });
                if (this.isPopupShowing()) {
                    this.close();
                }

                return false;
            };

            wijcalendar.prototype._onWeekDayMouseEnter = function (e) {
                this._initWeekDaySelector(e.currentTarget);
                var cells = $(e.currentTarget).data('cells'), i;

                for (i = 0; i < cells.length; i++) {
                    e.currentTarget = cells[i];
                    this._onDayMouseEnter(e);
                }
            };

            wijcalendar.prototype._onWeekDayMouseLeave = function (e) {
                this._initWeekDaySelector(e.currentTarget);
                var cells = $(e.currentTarget).data('cells'), i;

                for (i = 0; i < cells.length; i++) {
                    e.currentTarget = cells[i];
                    this._onDayMouseLeave(e);
                }
            };

            wijcalendar.prototype._initWeekNumberSelector = function (wn) {
                if ($(wn).data('cells') !== undefined) {
                    return;
                }

                var tokens = wn.id.split('_'), rowIndex, monthID, monthTable, cells = [], tr, i, td, dt;
                if (tokens[tokens.length - 2] !== 'rs') {
                    throw Error["create"]('not a row');
                }
                rowIndex = parseInt(tokens[tokens.length - 1], 10);
                monthID = (tokens.slice(0, tokens.length - 2)).join('_');
                monthTable = this._getParentTable(wn);
                if (monthTable) {
                    if (monthTable.id !== monthID) {
                        throw Error["create"]('not a row');
                    }
                    tr = monthTable.rows[rowIndex];
                    if (tr) {
                        i = 0;
                        if (this.options.showWeekNumbers) {
                            i++;
                        }
                        for (; i < tr.cells.length; i++) {
                            td = tr.cells[i];
                            if (td) {
                                dt = $(td).attr('daytype');
                                if (dt !== undefined) {
                                    if ($(td).find('a').hasAllClasses(this.options.wijCSS.prioritySecondary) === false) {
                                        if (this._isSelectable(parseInt(dt, 10))) {
                                            cells[cells.length] = td;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                $(wn).data('cells', cells);
            };

            wijcalendar.prototype._onWeekNumberClicked = function (e) {
                this._initWeekNumberSelector(e.currentTarget);
                var cells = $(e.currentTarget).data('cells'), selDates = [], i, c, d, date;
                this.unSelectAll();
                for (i = 0; i < cells.length; i++) {
                    c = $(cells[i]);
                    d = c.attr('date');
                    if (d !== undefined) {
                        date = new Date(d);
                        this.selectDate(date);
                        selDates[selDates.length] = date;
                    }
                }

                this._trigger('selectedDatesChanged', null, { dates: selDates });
                if (this.isPopupShowing()) {
                    this.close();
                }

                return false;
            };

            wijcalendar.prototype._onWeekNumberMouseEnter = function (e) {
                this._initWeekNumberSelector(e.currentTarget);
                var cells = $(e.currentTarget).data('cells'), i;
                for (i = 0; i < cells.length; i++) {
                    e.currentTarget = cells[i];
                    this._onDayMouseEnter(e);
                }
            };

            wijcalendar.prototype._onWeekNumberMouseLeave = function (e) {
                this._initWeekNumberSelector(e.currentTarget);
                var cells = $(e.currentTarget).data('cells'), i;
                for (i = 0; i < cells.length; i++) {
                    e.currentTarget = cells[i];
                    this._onDayMouseLeave(e);
                }
            };

            wijcalendar.prototype._isAnimating = function () {
                return !!this.element.data('animating.wijcalendar');
            };

            wijcalendar.prototype._onPreviewMouseEnter = function (e) {
                if (!!this.element.data('previewContainer')) {
                    return;
                }
                if (this._isAnimating()) {
                    return;
                }

                var btn = $(e.currentTarget), btnId = btn.attr('id'), mainDate = this.getDisplayDate(), wijCSS = this.options.wijCSS, months = this.options.monthCols * this.options.monthRows, previewContainer = $('<div/>');
                if (btnId === undefined) {
                    return;
                }

                if (btnId.indexOf("prevPreview") >= 0) {
                    months = -months;
                }

                this.options.displayDate = wijDateOps.addMonths(mainDate, months);
                this.element.data('preview.wijcalendar', true);

                previewContainer.appendTo(document.body);
                previewContainer.hide();
                previewContainer.addClass('wijmo-wijcalendar ' + wijCSS.datepickerInline + " " + wijCSS.datepicker + " " + wijCSS.widget + " " + wijCSS.content + " " + wijCSS.helperClearFix + " " + wijCSS.cornerAll);
                previewContainer.append(this._createCalendar());

                this.options.displayDate = mainDate;
                this.element.data('preview.wijcalendar', false);
                this._createMonthViews();

                previewContainer.wijpopup({
                    showEffect: 'slide',
                    showOptions: { direction: (btnId.indexOf("prevPreview") >= 0 ? 'right' : 'left') },
                    hideEffect: 'slide',
                    hideOptions: { direction: (btnId.indexOf("prevPreview") >= 0 ? 'right' : 'left') }
                });
                previewContainer.wijpopup('show', {
                    my: (btnId.indexOf("prevPreview") >= 0 ? 'right top' : 'left top'),
                    at: (btnId.indexOf("prevPreview") >= 0 ? 'left top' : 'right top'),
                    of: btn
                });

                this.element.data('previewContainer', previewContainer);
            };

            wijcalendar.prototype._onPreviewMouseLeave = function (e) {
                var btn = $(e.currentTarget), btnId = btn.attr('id'), previewContainer = this.element.data('previewContainer'), self = this;
                if (btnId === undefined) {
                    return;
                }

                if (previewContainer) {
                    if (previewContainer.wijpopup('isAnimating')) {
                        window.setTimeout(function () {
                            self._onPreviewMouseLeave(e);
                        }, 200);
                    } else {
                        previewContainer.wijpopup('hide');
                        this.element.removeData('previewContainer');
                        $("table", previewContainer).remove();
                    }
                }
            };

            wijcalendar.prototype._resetWidth = function () {
                if (!this._myGrid) {
                    this.element.css('height', '');
                    if (this.options.monthCols > 1) {
                        this.element.css('width', 17 * this.options.monthCols + 'em');
                        this.element.addClass(this.options.wijCSS.datepickerMulti);
                    } else {
                        this.element.css('width', '');
                        this.element.removeClass(this.options.wijCSS.datepickerMulti);
                    }
                }
            };

            wijcalendar.prototype._playMmSlideAnimation = function (toDate) {
                var w = this.element.width(), h = this.element.height(), date = this.getDisplayDate(), curContent, newContent, goNext, direction = this.options.direction || 'horizontal', calendar = this, curContentAniOpts, newContentAniOpts;
                this.element.height(h);

                this.element.wrapInner("<div class='wijmo-wijcalendar-multi-aniwrapper'></div>");
                curContent = this.element.find('>:first-child').width(w).height(h);

                newContent = curContent.clone(false);
                newContent.hide();

                this.options.displayDate = toDate;
                this._createMonthViews();
                newContent.empty().append(this._createMonthGroup());
                newContent.appendTo(this.element);

                goNext = toDate > date;

                this.element.data('animating.wijcalendar', true);
                if ($.effects) {
                    curContent.effect('slide', {
                        mode: 'hide',
                        direction: direction === 'horizontal' ? (goNext ? 'left' : 'right') : (goNext ? 'up' : 'down'),
                        easing: this.options.easing || 'easeOutBack',
                        duration: this.options.duration
                    }, function () {
                        curContent.remove();
                    });

                    newContent.effect('slide', {
                        direction: direction === 'horizontal' ? (goNext ? 'right' : 'left') : (goNext ? 'down' : 'up'),
                        easing: this.options.easing || 'easeOutBack',
                        duration: this.options.duration
                    }, function () {
                        while (newContent.parent().is('.wijmo-wijcalendar-multi-aniwrapper')) {
                            newContent.parent().replaceWith(newContent);
                        }

                        newContent.replaceWith(newContent.contents());
                        calendar.element.height('');
                        calendar._bindEvents();
                        calendar.element.data('animating.wijcalendar', false);
                        calendar._trigger('afterSlide');
                    });
                } else {
                    if (direction === 'horizontal') {
                        curContentAniOpts = { width: 0 };
                        newContentAniOpts = { width: '100%' };
                    } else {
                        curContentAniOpts = { height: 0 };
                        newContentAniOpts = { height: '100%' };
                    }
                    curContent.animate(curContentAniOpts, this.options.duration, null, function () {
                        curContent.remove();
                    });
                    newContent.animate(newContentAniOpts, this.options.duration, null, function () {
                        while (newContent.parent().is('.wijmo-wijcalendar-multi-aniwrapper')) {
                            newContent.parent().replaceWith(newContent);
                        }

                        newContent.replaceWith(newContent.contents());
                        calendar.element.height('');
                        calendar._bindEvents();
                        calendar.element.data('animating.wijcalendar', false);
                        calendar._trigger('afterSlide');
                    });
                }
            };

            wijcalendar.prototype._playSlideAnimation = function (toDate) {
                if (!this._isSingleMonth()) {
                    return;
                }

                var self = this, date = this.getDisplayDate(), wijCSS = self.options.wijCSS, curTable = this.element.find('.' + wijCSS.datepickerCalendar), wrapper, slideContainer, yearStep = 1, direction = this.options.direction || 'horizontal', goNext = toDate > date, months = [], w, h, wrapperAniOpts, originalHeight = curTable[0].style.height;

                if (curTable.parent().is('.wijmo-wijcalendar-aniwrapper')) {
                    wrapper = curTable.parent();
                } else {
                    //wrapper = $.effects.createWrapper(curTable).css({ overflow: 'hidden' });
                    wrapper = $.createWrapper(curTable).css({ overflow: 'hidden' });
                    wrapper.removeClass(wijCSS.effectsWrapper);
                    wrapper.addClass('wijmo-wijcalendar-aniwrapper');
                }

                if (wrapper.parent().is('.wijmo-wijcalendar-aniwrapper')) {
                    slideContainer = wrapper.parent();
                } else {
                    //slideContainer = $.effects.createWrapper(wrapper)
                    //	.css({ overflow: 'hidden' });
                    slideContainer = $.createWrapper(wrapper).css({ overflow: 'hidden' });
                    slideContainer.removeClass(wijCSS.effectsWrapper);
                    slideContainer.addClass('wijmo-wijcalendar-aniwrapper');
                }

                if (this._myGrid) {
                    switch (this._myGrid.gridType) {
                        case "month":
                            yearStep = 1;
                            break;
                        case "year":
                            yearStep = 10;
                            break;
                        case "decade":
                            yearStep = 100;
                            break;
                    }
                }

                months[months.length] = toDate;
                w = curTable.outerWidth();
                h = curTable.outerHeight();

                if (direction === 'horizontal') {
                    curTable.width(w).css('float', goNext ? 'left' : 'right');
                    wrapper.width((months.length + 1) * w);
                    wrapper.css('left', goNext ? 0 : -months.length * w).css('position', 'absolute');
                } else {
                    wrapper.width(w);
                    wrapper.css('top', goNext ? 0 : -months.length * h).css('position', 'absolute');
                    wrapper.height((months.length + 1) * h);
                }

                $.each(months, function (index, date) {
                    if (self._myGrid === undefined) {
                        var mv = new wijMonthView(self, date), $view = self._customize(mv.getHtml(true));
                        if (direction === 'horizontal') {
                            $view.width(w).css('float', goNext ? 'left' : 'right').appendTo(wrapper);
                        } else {
                            $view.appendTo(wrapper);
                        }
                    } else {
                        if (direction === 'horizontal') {
                            $(self._myGrid.getHtml(date, true)).width(w).height(h).css('float', goNext ? 'left' : 'right').appendTo(wrapper);
                        } else {
                            $(self._myGrid.getHtml(date, true)).height(h).appendTo(wrapper);
                        }
                    }
                });

                this.options.displayDate = toDate;
                if (this._myGrid === undefined) {
                    this._createMonthViews();
                }
                this._refreshTitle();

                this.element.data('animating.wijcalendar', true);
                if ($.effects) {
                    wrapper.effect('slide', {
                        mode: 'hide',
                        direction: direction === 'horizontal' ? (goNext ? 'left' : 'right') : (goNext ? 'up' : 'down'),
                        easing: this.options.easing || 'easeOutBack',
                        distance: (direction === 'horizontal' ? w : h) * months.length,
                        duration: this.options.duration
                    }, function () {
                        curTable = wrapper.children(':last');
                        while (curTable.parent().is('.wijmo-wijcalendar-aniwrapper')) {
                            curTable.parent().replaceWith(curTable);
                        }
                        curTable.css({ 'float': '', 'width': '' });
                        self._bindEvents();
                        self.element.data('animating.wijcalendar', false);
                        if (originalHeight && $.browser.chrome) {
                            //jQuery .height() method return incorrect value in Chrome. http://stackoverflow.com/questions/3518436/jquery-height-problem-with-chrome
                            curTable[0].style.height = originalHeight;
                        }
                        self._trigger('afterSlide');
                    });
                } else {
                    if (direction === 'horizontal') {
                        wrapperAniOpts = { width: 0 };
                    } else {
                        wrapperAniOpts = { height: 0 };
                    }
                    wrapper.animate(wrapperAniOpts, this.options.duration, null, function () {
                        curTable = wrapper.children(':last');
                        while (curTable.parent().is('.wijmo-wijcalendar-aniwrapper')) {
                            curTable.parent().replaceWith(curTable);
                        }
                        curTable.css({ 'float': '', 'width': '' });
                        self._bindEvents();
                        self.element.data('animating.wijcalendar', false);
                        self._trigger('afterSlide');
                    });
                }
            };

            wijcalendar.prototype._onTitleClicked = function () {
                if (!this.options.allowQuickPick || !this._isSingleMonth()) {
                    return;
                }
                if (this._isAnimating()) {
                    return;
                }

                if (this._myGrid === undefined) {
                    this._getMyGrid();
                } else {
                    switch (this._myGrid.gridType) {
                        case "month":
                            this._myGrid.gridType = "year";
                            break;

                        case "year":
                            this._myGrid.gridType = "decade";
                            break;

                        case "decade":
                            return;
                    }
                }
                this._updateMyGrid();
            };

            wijcalendar.prototype._getMyGrid = function () {
                if (this._myGrid === undefined) {
                    this._myGrid = new wijMyGrid(this);
                }
                return this._myGrid;
            };

            wijcalendar.prototype._updateMyGrid = function (duration, annimated) {
                if (typeof duration === "undefined") { duration = 0; }
                if (typeof annimated === "undefined") { annimated = true; }
                this._refreshTitle();
                this.element.width(this.element.width()).height(this.element.height());

                var wijCSS = this.options.wijCSS, curTable = this.element.find('.' + wijCSS.datepickerCalendar), wrapper, container, nextTable, w = curTable.outerWidth(), h = curTable.outerHeight(), selIndex, row, col, toWidth, toHeight, toBounds, calendar = this, aniDur = annimated ? duration || this.options.duration || 500 : 0;

                if (curTable.parent().is('.wijmo-wijcalendar-aniwrapper')) {
                    wrapper = curTable.parent();
                } else {
                    wrapper = $.createWrapper(curTable).css({ overflow: 'hidden' }).removeClass(wijCSS.effectsWrapper).addClass('wijmo-wijcalendar-aniwrapper');
                }

                if (wrapper.parent().is('.wijmo-wijcalendar-aniwrapper')) {
                    container = wrapper.parent();
                } else {
                    container = $.createWrapper(wrapper).css({ overflow: 'hidden' }).removeClass(wijCSS.effectsWrapper).addClass('wijmo-wijcalendar-aniwrapper').width(w).height(h);
                }

                nextTable = $(this._myGrid.getHtml(true)).css({ position: 'absolute', top: 0, left: 0, opacity: 0 }).appendTo(container).height(h);

                selIndex = this._myGrid.getSelectedIndex();
                row = Math.floor(selIndex / 4);
                col = selIndex - (row * 4);

                toWidth = w / 4;
                toHeight = h / 3;

                toBounds = {
                    left: toWidth * col,
                    top: toHeight * row,
                    width: toWidth,
                    height: toHeight
                };

                curTable.width("100%").height("100%");
                wrapper.css({ border: 'solid 1px #cccccc' });

                this.element.data('animating.wijcalendar', true);

                if ($.effects) {
                    wrapper.effect('size', {
                        to: toBounds,
                        duration: aniDur
                    }, function () {
                        wrapper.remove();
                    });
                } else {
                    wrapper.animate(toBounds, aniDur, null, function () {
                        wrapper.remove();
                    });
                }

                nextTable.animate({
                    opacity: 1
                }, aniDur, null, function () {
                    nextTable.css({ position: '', top: '', left: '', filter: '' });
                    while (nextTable.parent().is('.wijmo-wijcalendar-aniwrapper')) {
                        nextTable.parent().replaceWith(nextTable);
                    }

                    calendar._bindEvents();
                    calendar.element.data('animating.wijcalendar', false);
                });
            };

            wijcalendar.prototype._onMyGridClicked = function (e) {
                if (this._myGrid === undefined) {
                    return false;
                }
                if (this._isAnimating()) {
                    return false;
                }

                var cell = $(e.currentTarget), index = parseInt(cell.attr('index'), 10), value = parseInt(cell.attr('value'), 10), wijCSS = this.options.wijCSS, curTable = this.element.find('.' + wijCSS.datepickerCalendar), wrapper, container, w, h, bounds, $content, date, mv, nextTable, calendar = this;
                if (this._myGrid.gridType !== "month") {
                    if (!index || index === 11) {
                        return false;
                    }
                }

                if (!cell.hasAllClasses(wijCSS.stateActive)) {
                    this._myGrid.select(index, value);
                }

                if (this._myGrid.gridType === "decade") {
                    this._myGrid.gridType = "year";
                } else {
                    if (this._myGrid.gridType === "year") {
                        this._myGrid.gridType = "month";
                    } else {
                        this._myGrid = undefined;
                    }
                }

                this._refreshTitle();

                w = curTable.outerWidth();
                h = curTable.outerHeight();

                if (curTable.parent().is('.wijmo-wijcalendar-aniwrapper')) {
                    container = curTable.parent();
                } else {
                    //container = $.effects.createWrapper(curTable).css({ overflow: 'hidden' })
                    container = $.createWrapper(curTable).css({ overflow: 'hidden' }).removeClass(wijCSS.effectsWrapper).addClass('wijmo-wijcalendar-aniwrapper').width(w).height(h);
                }

                bounds = $.extend({}, cell.position(), { width: cell.width(), height: cell.height() });
                if (this._myGrid === undefined) {
                    this._createMonthViews();
                    date = this.getDisplayDate();
                    mv = this._getMonthView(date);
                    $content = this._customize(mv.getHtml(true));
                } else {
                    $content = $(this._myGrid.getHtml(true));
                }

                nextTable = $content.height(h).appendTo(container);

                //wrapper = $.effects.createWrapper(nextTable).css({ overflow: 'hidden' })
                wrapper = $.createWrapper(nextTable).css({ overflow: 'hidden' }).removeClass(wijCSS.effectsWrapper).addClass('wijmo-wijcalendar-aniwrapper').css($.extend(bounds, {
                    border: 'solid 1px #cccccc',
                    position: 'absolute'
                }));

                this.element.data('animating.wijcalendar', true);
                wrapper.animate({
                    left: 0,
                    top: 0,
                    width: w,
                    height: h
                }, this.options.duration || 500, null, function () {
                    nextTable.css("width", "");
                });

                curTable.animate({
                    opacity: 0
                }, this.options.duration || 500, null, function () {
                    curTable.remove();

                    while (nextTable.parent().is('.wijmo-wijcalendar-aniwrapper')) {
                        nextTable.parent().replaceWith(nextTable);
                    }

                    if (calendar._myGrid === undefined) {
                        calendar.element.width('').height('');
                    }

                    calendar._bindEvents();
                    calendar.element.data('animating.wijcalendar', false);
                });

                return false;
            };

            wijcalendar.prototype._onMyGridMouseEnter = function (e) {
                if (this._myGrid === undefined) {
                    return;
                }

                var cell = $(e.currentTarget), index = parseInt(cell.attr('index'), 10);
                if (this._myGrid.gridType !== "month" && (index < 0 || index > 11)) {
                    return;
                }
                cell.addClass(this.options.wijCSS.stateHover);
            };

            wijcalendar.prototype._onMyGridMouseLeave = function (e) {
                if (this._myGrid === undefined) {
                    return;
                }

                var cell = $(e.currentTarget), index = parseInt(cell.attr('index'), 10);
                if (this._myGrid.gridType !== "month" && (index < 0 || index > 11)) {
                    return;
                }
                cell.removeClass(this.options.wijCSS.stateHover);
            };

            wijcalendar.prototype._unbindEvents = function () {
                var self = this, ele = self.element, wijCSS = self.options.wijCSS, selectionMode = self.options.selectionMode;

                ele.unbind(".wijcalendar");
                ele.find('div .wijmo-wijcalendar-navbutton').unbind();
                ele.find("." + wijCSS.datepickerTitle).unbind();
                ele.find(".wijmo-wijcalendar-prevpreview-button, .wijmo-wijcalendar-nextpreview-button").unbind('mouseenter.wijcalendar').unbind('mouseleave.wijcalendar');
                if (self._myGrid === undefined) {
                    ele.find(".wijmo-wijcalendar-day-selectable").unbind();
                    if ($.support.isTouchEnabled && $.support.isTouchEnabled()) {
                        if ($.browser.msie) {
                            ele.unbind("contextmenu.wijcalendar");
                        }

                        ele.find(".wijmo-wijcalendar-day-selectable").unbind("wijmouseup.wijcalendar");
                    }
                    if (!!selectionMode.month) {
                        ele.find(".wijmo-wijcalendar-monthselector").unbind();
                    }
                    if (!!selectionMode.weekDay) {
                        ele.find("." + wijCSS.datepickerWeekDay).unbind();
                    }
                    if (!!selectionMode.weekNumber) {
                        ele.find(".wijmo-wijcalendar-week-num").unbind();
                    }
                } else {
                    ele.find(".wijmo-wijcalendar-day-selectable").unbind();
                }
            };

            wijcalendar.prototype._bindEvents = function () {
                var wijCSS = this.options.wijCSS, eventPre = "";

                // fix the issue 42890, if touch is enabled, all the touch events should use wij prefix.
                // otherwise, do not use wij prefix.
                if ($.support.isTouchEnabled && $.support.isTouchEnabled()) {
                    eventPre = "wij";
                }
                if (!this.element.data('preview.wijcalendar') && !this._isDisabled()) {
                    this.element.find('div .wijmo-wijcalendar-navbutton').unbind().bind('mouseout.wijcalendar', function () {
                        var el = $(this);
                        el.removeClass(wijCSS.stateHover);
                        if (el.hasAllClasses(wijCSS.datepickerNextHover)) {
                            el.removeClass(wijCSS.datepickerNextHover);
                        } else if (el.hasAllClasses(wijCSS.datepickerPrevHover)) {
                            el.removeClass(wijCSS.datepickerPrevHover);
                        }
                    }).bind('mouseover.wijcalendar', function () {
                        var el = $(this);
                        el.addClass(wijCSS.stateHover);
                        if (el.hasAllClasses(wijCSS.datepickerNext)) {
                            el.addClass(wijCSS.datepickerNextHover);
                        } else if (el.hasAllClasses(wijCSS.datepickerPrev)) {
                            el.addClass(wijCSS.datepickerPrevHover);
                        }
                    }).bind(eventPre + 'click.wijcalendar', $.proxy(this._onNavButtonClicked, this));

                    this.element.unbind(".wijcalendar").bind(eventPre + "mouseup.wijcalendar", $.proxy(this._onMouseUp, this));

                    this.element.find("." + wijCSS.datepickerTitle).unbind().bind('mouseout.wijcalendar', function () {
                        $(this).removeClass(wijCSS.stateHover);
                    }).bind('mouseover.wijcalendar', function () {
                        $(this).addClass(wijCSS.stateHover);
                    }).bind(eventPre + 'click.wijcalendar', $.proxy(this._onTitleClicked, this));

                    this.element.find(".wijmo-wijcalendar-prevpreview-button, " + ".wijmo-wijcalendar-nextpreview-button").unbind('mouseenter.wijcalendar').unbind('mouseleave.wijcalendar').bind({
                        "mouseenter.wijcalendar": $.proxy(this._onPreviewMouseEnter, this),
                        "mouseleave.wijcalendar": $.proxy(this._onPreviewMouseLeave, this)
                    });

                    if (this._myGrid === undefined) {
                        this.element.find(".wijmo-wijcalendar-day-selectable").unbind().bind(eventPre + "click.wijcalendar", $.proxy(this._onDayClicked, this)).bind("mouseenter.wijcalendar", $.proxy(this._onDayMouseEnter, this)).bind("mouseleave.wijcalendar", $.proxy(this._onDayMouseLeave, this)).bind(eventPre + "mousedown.wijcalendar", $.proxy(this._onDayMouseDown, this)).bind("dragstart.wijcalendar", $.proxy(this._onDayDragStart, this));
                        if ($.support.isTouchEnabled && $.support.isTouchEnabled()) {
                            if ($.browser.msie) {
                                this.element.unbind("contextmenu.wijcalendar").bind({ "contextmenu.wijcalendar": function () {
                                        return false;
                                    } });
                            }

                            this.element.find(".wijmo-wijcalendar-day-selectable").unbind(eventPre + "mouseup.wijcalendar").bind(eventPre + "mouseup.wijcalendar", $.proxy(this._onTouchend, this));
                        }
                        if (!!this.options.selectionMode.month) {
                            this.element.find(".wijmo-wijcalendar-monthselector").unbind().bind(eventPre + "click.wijcalendar", $.proxy(this._onMonthSelectorClicked, this)).bind({
                                "mouseenter.wijcalendar": $.proxy(this._onMonthSelectorMouseEnter, this),
                                "mouseleave.wijcalendar": $.proxy(this._onMonthSelectorMouseLeave, this)
                            });
                        }
                        if (!!this.options.selectionMode.weekDay) {
                            this.element.find("." + wijCSS.datepickerWeekDay).unbind().bind(eventPre + "click.wijcalendar", $.proxy(this._onWeekDayClicked, this)).bind({
                                "mouseenter.wijcalendar": $.proxy(this._onWeekDayMouseEnter, this),
                                "mouseleave.wijcalendar": $.proxy(this._onWeekDayMouseLeave, this)
                            });
                        }
                        if (!!this.options.selectionMode.weekNumber) {
                            this.element.find(".wijmo-wijcalendar-week-num").unbind().bind(eventPre + "click.wijcalendar", $.proxy(this._onWeekNumberClicked, this)).bind({
                                "mouseenter.wijcalendar": $.proxy(this._onWeekNumberMouseEnter, this),
                                "mouseleave.wijcalendar": $.proxy(this._onWeekNumberMouseLeave, this)
                            });
                        }
                    } else {
                        this.element.find(".wijmo-wijcalendar-day-selectable").unbind().bind(eventPre + "click.wijcalendar", $.proxy(this._onMyGridClicked, this)).bind({
                            "mouseenter.wijcalendar": $.proxy(this._onMyGridMouseEnter, this),
                            "mouseleave.wijcalendar": $.proxy(this._onMyGridMouseLeave, this)
                        });
                    }
                }
            };

            wijcalendar.prototype._isSelectable = function (dayType) {
                var o = this.options;
                return (o.showOtherMonthDays && (dayType & wijDayType.otherMonth)) || !(dayType & (wijDayType.outOfRange | wijDayType.disabled | wijDayType.otherMonth));
            };

            wijcalendar.prototype._getCellClassName = function (dayType, date, previewMode) {
                var o = this.options, cssCell = '', cssText = o.wijCSS.stateDefault, allowSelDay = (!!o.selectionMode.day || !!o.selectionMode.days);

                previewMode = previewMode || false;
                if (!previewMode && !this._isDisabled() && allowSelDay && this._isSelectable(dayType)) {
                    cssCell += " wijmo-wijcalendar-day-selectable";
                }

                if ((dayType & wijDayType.weekEnd)) {
                    cssCell += ' ' + o.wijCSS.datepickerWeekEnd;
                }
                if ((dayType & wijDayType.otherMonth)) {
                    cssCell += ' ' + o.wijCSS.datepickerOtherMonth;
                    cssText += ' ' + o.wijCSS.prioritySecondary;
                }
                if ((dayType & wijDayType.outOfRange)) {
                    cssCell += ' wijmo-wijcalendar-outofrangeday';
                    cssText += ' ' + o.wijCSS.prioritySecondary;
                }
                if ((dayType & wijDayType.gap)) {
                    cssCell += ' wijmo-wijcalendar-gap';
                } else {
                    if ((dayType & wijDayType.disabled)) {
                        cssCell += ' ' + o.wijCSS.datepickerUnselectable;
                        cssText += ' ' + o.wijCSS.stateDisabled;
                    }
                    if ((dayType & wijDayType.today)) {
                        cssCell += ' ' + o.wijCSS.datepickerDaysCellOver + ' ' + o.wijCSS.datepickerToday;
                        cssText += ' ' + o.wijCSS.stateHighlight;
                    }
                    if ((dayType & wijDayType.selected) && ((dayType & (wijDayType.outOfRange | wijDayType.disabled)) === 0)) {
                        cssCell += ' ' + o.wijCSS.datepickerCurrentDay;
                        cssText += ' ' + o.wijCSS.stateActive;
                    }
                    if ((dayType & wijDayType.gap)) {
                        cssCell += ' wijmo-wijcalendar-gap';
                    }
                    if ((dayType & wijDayType.custom)) {
                        cssCell += ' wijmo-wijcalendar-customday';
                    }
                }

                return { cssCell: cssCell, cssText: cssText };
            };

            wijcalendar.prototype._onNavButtonClicked = function (e) {
                if (this._isAnimating()) {
                    return false;
                }

                var step = 1, btnId = $(e.currentTarget).attr('id'), date = this.getDisplayDate(), nextDate = date;
                if (this._myGrid === undefined) {
                    step = btnId.indexOf('quick') >= 0 ? this.options.quickNavStep : 1;
                    step = btnId.indexOf('next') >= 0 ? step : -step;
                    step = step * this.options.monthRows * this.options.monthCols;
                    nextDate = wijDateOps.addMonths(date, step);
                } else {
                    step = btnId.indexOf('next') >= 0 ? 1 : -1;
                    switch (this._myGrid.gridType) {
                        case "month":
                            nextDate = wijDateOps.addYears(date, step);
                            break;
                        case "year":
                            nextDate = wijDateOps.addYears(date, step * 10);
                            break;
                        case "decade":
                            nextDate = wijDateOps.addYears(date, step * 100);
                            break;
                    }
                }

                this._slideToDate(nextDate);
                return false;
            };

            wijcalendar.prototype._getMonthGroupHtml = function () {
                var date = this.getDisplayDate(), mv, width, hw, wijCSS = this.options.wijCSS, r, c;
                if (this._isSingleMonth()) {
                    mv = this._getMonthView(date);
                    mv.showPreview = this.options.allowPreview && !this.element.data('preview.wijcalendar');
                    return mv.getHtml();
                }

                width = 100 / this.options.monthCols + '%';
                hw = new htmlTextWriter();
                for (r = 0; r < this.options.monthRows; r++) {
                    for (c = 0; c < this.options.monthCols; c++) {
                        hw.writeBeginTag('div');
                        hw.writeAttribute('class', wijCSS.datepickerGroup + (c === 0 ? ' ' + wijCSS.datepickerGroupFirst : '') + (c === this.options.monthCols - 1 ? ' ' + wijCSS.datepickerGroupLast : ''));
                        hw.writeAttribute('style', 'width:' + width);
                        hw.writeTagRightChar();
                        mv = this._getMonthView(date);
                        mv.showPreview = false;
                        hw.write(mv.getHtml());
                        hw.writeEndTag('div');
                        date = wijDateOps.addMonths(date, 1);
                    }

                    hw.writeBeginTag('div');
                    hw.writeAttribute('class', wijCSS.datepickerRowBreak);
                    hw.writeTagRightChar();
                    hw.writeEndTag('div');
                }

                return hw.toString();
            };

            wijcalendar.prototype._getCalendarHtml = function () {
                this._createMonthViews();
                var hw = new htmlTextWriter();
                hw.write(this._getMonthGroupHtml());
                return hw.toString();
            };

            wijcalendar.prototype._customizeDayCell = function ($dayCell) {
                if ($dayCell.attr("state") === undefined) {
                    $dayCell.attr("state", 'normal');
                }
                if ($dayCell.attr("daytype") === undefined) {
                    return;
                }
                if ($dayCell.attr("date") === undefined) {
                    return;
                }

                var dayType = parseInt($dayCell.attr("daytype"), 10), date = new Date($dayCell.attr("date")), hover = $dayCell.attr("state") === 'hover';

                this.options.customizeDate($dayCell, date, dayType, hover);
            };

            wijcalendar.prototype._customize = function (html) {
                var o = this.options, self = this, $h = $(html);
                if (!$.isFunction(o.customizeDate)) {
                    return $h;
                }

                $.each($h.find('.wijmo-wijcalendar-day-selectable'), function (index, dayCell) {
                    self._customizeDayCell($(dayCell));
                });

                return $h;
            };

            wijcalendar.prototype._createCalendar = function () {
                //for jquery change to 1.9 $.parseHTML
                return this._customize($($.parseHTML(this._getCalendarHtml())));
            };

            wijcalendar.prototype._createMonthGroup = function () {
                return this._customize($(this._getMonthGroupHtml()));
            };

            wijcalendar.prototype._getMonthID = function (date) {
                return date.getFullYear() + '_' + (date.getMonth() + 1);
            };

            wijcalendar.prototype._createMonthViews = function () {
                this._monthViews = {};
                var monthID = '', date = this.getDisplayDate(), row, col, mv, count;
                for (row = 0; row < this.options.monthRows; row++) {
                    for (col = 0; col < this.options.monthCols; col++) {
                        monthID = this._getMonthID(date);
                        this._monthViews[monthID] = new wijMonthView(this, date);

                        if (row === 0) {
                            if (col === 0) {
                                this._monthViews[monthID].isFirst = true;
                            }

                            if (col === this.options.monthCols - 1) {
                                this._monthViews[monthID].isLast = true;
                            }
                        }
                        date = wijDateOps.addMonths(date, 1);
                    }
                }
                date = this.getDisplayDate();
                monthID = this._getMonthID(date);
                mv = this._monthViews[monthID];
                if (mv) {
                    this._groupStartDate = mv.getStartDate();
                }
                count = this.options.monthRows * this.options.monthCols;
                if (count > 1) {
                    date = wijDateOps.addMonths(date, count - 1);
                    monthID = this._getMonthID(date);
                    mv = this._monthViews[monthID];
                }
                if (mv) {
                    this._groupEndDate = mv.getEndDate();
                }
            };

            wijcalendar.prototype._getMonthView = function (date) {
                var monthID = this._getMonthID(date);
                return this._monthViews[monthID];
            };

            wijcalendar.prototype._getId = function () {
                return this.element.attr("id");
            };

            wijcalendar.prototype._getChildElement = function (id) {
                var child = this.element.find('[id*=\'' + id + '\']');
                return child.length === 0 ? undefined : child;
            };

            wijcalendar.prototype._refreshDayCell = function (dayCell) {
                var $dc = $(dayCell), o = this.options, dayType, date, hover, txt;
                if ($dc.attr("state") === undefined) {
                    $dc.attr("state", 'normal');
                }
                if ($dc.attr("daytype") === undefined) {
                    return;
                }
                if ($dc.attr("date") === undefined) {
                    return;
                }

                dayType = parseInt($dc.attr("daytype"), 10);
                date = new Date($dc.attr("date"));
                hover = $dc.attr("state") === 'hover';

                $dc.attr('class', this._getCellClassName(dayType, date, undefined).cssCell);
                $dc.removeAttribute('aria-selected');
                if (dayType & wijDayType.selected) {
                    $dc.attribute('aria-selected', true);
                }

                if ($.isFunction(o.customizeDate)) {
                    if (this._customizeDayCell($dc)) {
                        return;
                    }
                }

                txt = $dc.find('a');
                if (txt.length > 0) {
                    txt.toggleClass(this.options.wijCSS.stateHover, hover);
                    txt.toggleClass(this.options.wijCSS.stateActive, ((dayType & wijDayType.selected) !== 0));
                }
            };

            wijcalendar.prototype._isSingleMonth = function () {
                return this.options.monthCols * this.options.monthRows === 1;
            };

            wijcalendar.prototype._splitString = function (s, sep, count) {
                if (count === undefined) {
                    return s.split(sep);
                }
                var ret = [], arr = s.split(sep), i;
                for (i = 0; i < arr.length; i++) {
                    if (i >= count) {
                        ret[count - 1] = ret[count - 1] + sep + arr[i];
                    } else {
                        ret.push(arr[i]);
                    }
                }
                return ret;
            };

            wijcalendar.prototype._getNavButtonHtml = function (id, cls, imgClass, tooltip) {
                var hw = new htmlTextWriter();
                hw.writeBeginTag('a');
                hw.writeAttribute('id', id);
                hw.writeAttribute('class', cls);
                hw.writeAttribute('role', 'button');
                hw.writeAttribute('href', '#');
                if (tooltip) {
                    hw.writeAttribute('title', tooltip);
                    hw.writeAttribute('aria-label', tooltip);
                }
                hw.writeTagRightChar();
                hw.writeBeginTag('span');
                hw.writeAttribute('class', imgClass);
                hw.writeTagRightChar();
                if (tooltip) {
                    hw.write(tooltip);
                }
                hw.writeEndTag('span');
                hw.writeEndTag('a');
                return hw.toString();
            };

            // 2013-1-6
            wijcalendar.prototype._getTitleText = function (monthDate) {
                if (this._myGrid !== undefined) {
                    return this._myGrid.getTitle();
                } else {
                    var d = monthDate || this.getDisplayDate(), f = this.options.titleFormat || 'MMMM yyyy';

                    if ($.isFunction(this.options.title)) {
                        return this.options.title(d, f) || this._formatDate(f, d);
                    }

                    return this._formatDate(f, d);
                }
            };

            wijcalendar.prototype._refreshTitle = function () {
                this.element.find('.' + this.options.wijCSS.datepickerTitle).html(this._getTitleText(undefined));
            };

            wijcalendar.prototype._fillTitle = function (hw, date) {
                var wijCSS = this.options.wijCSS;
                hw.writeBeginTag('div');
                hw.writeAttribute('class', wijCSS.datepickerTitle + ' wijmo-wijcalendar-title' + ' ' + wijCSS.stateDefault + ' ' + wijCSS.cornerAll);
                hw.writeTagRightChar();
                hw.write(this._getTitleText(date));
                hw.writeEndTag('div');
            };

            wijcalendar.prototype._getHeaderHtml = function (monthDate, prevButtons, nextButtons) {
                var previewMode = !!this.element.data('preview.wijcalendar'), wijCSS = this.options.wijCSS, buttons = previewMode ? 'none' : (this._isSingleMonth() ? this.options.navButtons : 'default'), isRTL = this.element.is('.' + wijCSS.datepickerRtl), hw = new htmlTextWriter();
                if (buttons === 'quick') {
                    hw.writeBeginTag('div');
                    hw.writeAttribute('class', wijCSS.header + ' wijmo-wijcalendar-header ' + wijCSS.helperClearFix + ' ' + wijCSS.cornerAll);
                    hw.writeAttribute('role', 'heading');
                    hw.writeTagRightChar();
                    if (!!prevButtons) {
                        hw.write(this._getNavButtonHtml(this._getId() + 'quickprev', 'wijmo-wijcalendar-navbutton ' + wijCSS.datepickerPrev + ' ' + wijCSS.cornerAll, wijCSS.icon + ' ui-icon-seek-' + (isRTL ? 'next' : 'prev') + ' ' + (isRTL ? this.options.wijCSS.iconArrowRight : this.options.wijCSS.iconArrowLeft), this.options.quickPrevTooltip.replace('#', this.options.quickNavStep)));
                    }
                    hw.writeBeginTag('div');
                    hw.writeAttribute('class', wijCSS.datepickerHeader + ' wijmo-wijcalendar-header-inner');
                    hw.writeTagRightChar();
                    if (!!prevButtons) {
                        hw.write(this._getNavButtonHtml(this._getId() + 'prev', 'wijmo-wijcalendar-navbutton ' + wijCSS.datepickerPrev + ' ' + wijCSS.cornerAll, wijCSS.icon + ' ui-icon-circle-triangle-' + (isRTL ? 'e' : 'w') + " " + (isRTL ? this.options.wijCSS.iconArrowRight : this.options.wijCSS.iconArrowLeft), this.options.prevTooltip));
                    }
                    this._fillTitle(hw, monthDate);
                    if (!!nextButtons) {
                        hw.write(this._getNavButtonHtml(this._getId() + 'next', 'wijmo-wijcalendar-navbutton ' + wijCSS.datepickerNext + ' ' + wijCSS.cornerAll, wijCSS.icon + ' ui-icon-circle-triangle-' + (isRTL ? 'w' : 'e') + " " + (isRTL ? this.options.wijCSS.iconArrowLeft : this.options.wijCSS.iconArrowRight), this.options.nextTooltip));
                    }
                    hw.writeEndTag('div');
                    if (!!nextButtons) {
                        hw.write(this._getNavButtonHtml(this._getId() + 'quicknext', 'wijmo-wijcalendar-navbutton ' + wijCSS.datepickerNext + ' ' + wijCSS.cornerAll, wijCSS.icon + ' ui-icon-seek-' + (isRTL ? 'prev' : 'next') + " " + (isRTL ? this.options.wijCSS.iconArrowLeft : this.options.wijCSS.iconArrowRight), this.options.quickNextTooltip.replace('#', this.options.quickNavStep)));
                    }
                    hw.writeEndTag('div');
                } else {
                    hw.writeBeginTag('div');
                    hw.writeAttribute('class', wijCSS.datepickerHeader + ' ' + wijCSS.header + ' ' + wijCSS.datepickerHeader + ' ' + wijCSS.helperClearFix + ' ' + wijCSS.cornerAll);
                    hw.writeAttribute('role', 'heading');
                    hw.writeTagRightChar();

                    if (buttons !== 'none' && !!prevButtons) {
                        hw.write(this._getNavButtonHtml(this._getId() + 'prev', 'wijmo-wijcalendar-navbutton ' + wijCSS.datepickerPrev + ' ' + wijCSS.cornerAll, wijCSS.icon + ' ui-icon-circle-triangle-' + (isRTL ? 'e' : 'w') + ' ' + (isRTL ? this.options.wijCSS.iconArrowRight : this.options.wijCSS.iconArrowLeft), this.options.prevTooltip));
                    }
                    this._fillTitle(hw, monthDate);

                    if (buttons !== 'none' && !!nextButtons) {
                        hw.write(this._getNavButtonHtml(this._getId() + 'next', 'wijmo-wijcalendar-navbutton ' + wijCSS.datepickerNext + ' ' + wijCSS.cornerAll, wijCSS.icon + ' ui-icon-circle-triangle-' + (isRTL ? 'w' : 'e') + ' ' + (isRTL ? this.options.wijCSS.iconArrowLeft : this.options.wijCSS.iconArrowRight), this.options.nextTooltip));
                    }
                    hw.writeEndTag('div');
                }

                return hw.toString();
            };

            wijcalendar.prototype._formatDate = function (format, date) {
                if (!wijDateOps.getTicks(date)) {
                    return '&nbsp;';
                }

                return Globalize.format(date, format, this._getCulture(null));
            };
            return wijcalendar;
        })(wijmo.wijmoWidget);
        _calendar.wijcalendar = wijcalendar;

        var wijcalendar_options = (function () {
            function wijcalendar_options() {
                /** Selector option for auto self initialization. This option is internal.
                * @ignore
                */
                this.initSelector = ":jqmData(role='wijcalendar')";
                //All CSS classes used in widgets that use jQuery UI CSS Framework
                /** @ignore*/
                this.wijCSS = {
                    datepickerInline: "ui-datepicker-inline",
                    datepicker: "ui-datepicker",
                    datepickerCalendar: "ui-datepicker-calendar",
                    datepickerMulti: "ui-datepicker-multi",
                    datepickerRtl: "datepicker-rtl",
                    datepickerOtherMonth: "ui-datepicker-other-month",
                    prioritySecondary: "ui-priority-secondary",
                    effectsWrapper: "ui-effects-wrapper",
                    datepickerNextHover: "ui-datepicker-next-hover",
                    datepickerPrevHover: "ui-datepicker-prev-hover",
                    datepickerNext: "ui-datepicker-next",
                    datepickerPrev: "ui-datepicker-prev",
                    datepickerTitle: "ui-datepicker-title",
                    datepickerWeekDay: "ui-datepicker-week-day",
                    datepickerWeekEnd: "ui-datepicker-week-end",
                    datepickerUnselectable: "ui-datepicker-unselectable",
                    datepickerDaysCellOver: "ui-datepicker-days-cell-over",
                    datepickerToday: "ui-datepicker-today",
                    stateHighlight: "ui-state-highlight",
                    datepickerCurrentDay: "ui-datepicker-current-day",
                    datepickerGroup: "ui-datepicker-group",
                    datepickerGroupFirst: "ui-datepicker-group-first",
                    datepickerGroupLast: "ui-datepicker-group-last",
                    datepickerRowBreak: "ui-datepicker-row-break",
                    datepickerHeader: "ui-datepicker-header",
                    uiIconGripDottedVertical: "ui-icon-grip-dotted-vertical",
                    uiDatepickerWeekCol: "ui-datepicker-week-col"
                };
                /** @ignore*/
                this.wijMobileCSS = {
                    header: "ui-header ui-bar-a",
                    content: "ui-body ui-body-b",
                    stateDefault: "ui-btn ui-btn-b",
                    stateHighlight: "ui-btn-down-e"
                };
                /** Assigns the string value of the culture ID that appears on the calendar
                *   for the weekday and title names.
                */
                this.culture = '';
                /** Assigns the string value of the culture calendar that appears on the calendar.
                *   This option must work with culture option.
                */
                this.cultureCalendar = '';
                /** Gets or sets the format for the title text in the month view.
                * @remarks
                *   The default value of this option is "yyyy".
                */
                this.monthViewTitleFormat = 'yyyy';
                /** Gets or sets the number of calendar months in horizontal direction.
                * @remarks
                *        By setting the monthCols property, calendar months will be added horizontally to the widget.
                *        The default value of this option is "1", which displays one calendar month at a time.
                */
                this.monthCols = 1;
                /** Gets or sets the number of calendar months in vertical direction.
                * @remarks
                *        By setting the monthRows property, calendar months will be added vertically to the widget.
                *        The default value of this option is "1", which displays one calendar month at a time.
                */
                this.monthRows = 1;
                /** Gets or sets the format for the title text.*/
                this.titleFormat = "MMMM yyyy";
                /** A boolean property that determines whether to display the calendar's title.*/
                this.showTitle = true;
                /** Gets or sets the display date for the first month view.
                *   You can specify the date via a Date object.
                * @type {Date}
                */
                this.displayDate = undefined;
                /** Gets or sets the number of day rows that appear in the calendar.
                *  This is useful if you want to view more or less calendar days on the calendar month.
                */
                this.dayRows = 6;
                /** Gets or sets the number of day columns that appear in the calendar.
                *  This is useful if you want to view more or less calendar days on the calendar month.
                */
                this.dayCols = 7;
                /** An option that determines the initial view type of the calendar.
                * @remarks
                *     Possible values are: "day", "month" , "year" or "decade".
                *     The default value of this option is "day".
                *     The initialView option only takes effect when initializing the wijcalendar.
                */
                this.initialView = 'day';
                /** Gets or sets the format for the week day.
                * @remarks
                *          Possible values are: "short", "full", "firstLetter" or "abbreviated".
                */
                this.weekDayFormat = "short";
                /** A boolean property that determines whether to display week days.*/
                this.showWeekDays = true;
                /** Determines whether to display week numbers.
                * @remarks
                *      When enabled, the week numbers appear vertically on the left side of the calendar.
                *      The week numbers represent a week number for each week in the calendar month.
                *      In the calendar year there are a total of 52 weeks so the weeknumbers will range from 1 to 52.
                */
                this.showWeekNumbers = false;
                /** Defines different rules for determining the first week of the year.
                * @example
                *  $("#calendar1").wijcalendar(
                *            { calendarWeekRule: 'firstFourDayWeek'}
                *   );
                * @remarks
                *          Possible values are: "firstDay", "firstFullWeek" or "firstFourDayWeek"
                */
                this.calendarWeekRule = "firstDay";
                /** Determines the minimum date to display. You can specify the minDate via a Date object.
                * @type {Date}
                * @remarks The default value is new Date(1900, 0, 1).
                */
                this.minDate = new Date(1900, 0, 1);
                /** Determines the maximum date to display. You can specify the maxDate via a Date object.
                * @type {Date}
                * @remarks The default value is new Date(1900, 0, 1).
                */
                this.maxDate = new Date(2099, 11, 31);
                /** Determines whether to display the days of the next and/or previous month.
                */
                this.showOtherMonthDays = true;
                /** Determines whether to add zeroes to days with only one digit
                * @remarks
                *      for example, "1" would become "01" if this property were set to "true"
                */
                this.showDayPadding = false;
                /** Gets or sets the date selection mode on the calendar control that
                *  specifies whether the user can select a single day, a week, or an entire month.
                * @remarks
                *      Possible fields in hash are: day, days, weekDay, weekNumber, month.
                * @example
                *  $("#calendar1").wijcalendar(
                *             { selectionMode: {day:true, weekDay:true}}
                *   );
                */
                this.selectionMode = { day: true, days: true };
                /** Allows users to preview the next and previous months by
                *  hovering over the previousPreview and nextPreview buttons.
                */
                this.allowPreview = false;
                /** Determines whether users can change the view to month/year/decade
                *  while clicking on the calendar title.
                */
                this.allowQuickPick = true;
                /** Gets or sets the format for the ToolTip. */
                this.toolTipFormat = "dddd, MMMM dd, yyyy";
                /** Gets or sets the text for the 'previous' button's ToolTip. */
                this.prevTooltip = "Previous";
                /** Gets or sets the text for the 'next' button's ToolTip. */
                this.nextTooltip = "Next";
                /** Gets or sets the "quick previous" button's ToolTip.*/
                this.quickPrevTooltip = "Quick Previous";
                /** Gets or sets the "quick next" button's ToolTip.*/
                this.quickNextTooltip = "Quick Next";
                /** Gets or sets the "previous preview" button's ToolTip.*/
                this.prevPreviewTooltip = "";
                /** Gets or sets the "next preview" button's ToolTip. */
                this.nextPreviewTooltip = "";
                /** Determines the display type of navigation buttons.
                * @remarks
                *      Possible values are: "default", "quick" or "none"
                */
                this.navButtons = 'default';
                /** Determines the increase/decrease steps when clicking the quick navigation button. */
                this.quickNavStep = 12;
                /** Determines the month slide direction.
                * @remarks
                *     Possible values are: horizontal or vertical
                */
                this.direction = 'horizontal';
                /** Gets or sets the animation duration in milliseconds.  */
                this.duration = 250;
                /** Sets the type of animation easing effect that users experience
                *   when they click the previous or next buttons on the wijcalendar.
                * @remarks
                *     For example, if the easing is set to "easeInBounce" the calendar
                *      bounces back and forth several times and then slides to the previous
                *      or next calendar month.
                *      You can create custom easing animations using jQuery UI Easings.
                */
                this.easing = 'easeInQuad';
                /** A boolean property that determines whether the wijcalendar widget
                *  is a pop-up calendar.
                * @remarks
                *     this is useful, for example,
                *           if you're integrating the calendar with an input control to create a date picker.
                */
                this.popupMode = false;
                /** A boolean property that determines whether to autohide
                *   the calendar in pop-up mode when clicking outside of the calendar.
                */
                this.autoHide = true;
                /** Function used for customizing the content, style and attributes of a day cell.
                * @type {function}
                * @remarks the function include following parameter:
                *  $daycell:jQuery jQuery object that represents table cell of the date to be customized.
                *  date: Date Date of the cell.
                *  hover: boolean Whether mouse is over the day cell.
                *  preview: object Whether rendering in preview container.
                *  returns true if day cell content has been changed
                *  and default cell content will not be applied.
                */
                this.customizeDate = null;
                /** A callback function used to customizing the title text on month view.
                * @type {function}
                * @remarks the function include following parameter:
                * date: Date The display date of the month.
                * format: string The title format. Determined by the options.titleFormat.
                */
                this.title = null;
                /** The beforeSlide event handler.
                * A function called before the calendar view slides to another month.
                * Cancellable.
                * @event
                */
                this.beforeSlide = null;
                /** The afterSlide event handler.
                * A function called after the calendar view slided to another month.
                * Cancellable.
                * @event
                */
                this.afterSlide = null;
                /** The beforeSelect event handler.
                * A function called before user selects a day by mouse. Cancellable.
                * @event
                * @param {Object} e The jQuery.Event object.
                * @param {ISelectedDate} args The data with this event.
                * @example
                * $("#calendar1").wijcalendar({
                *       beforeSelect: function (e, data)
                *       {
                *              var selDates = $("#calendar1").wijcalendar('option', 'selectedDates'),
                *              selected = false,
                *              list;
                *              $.each(selDates, function (i, d)
                *                  {
                *                    if (data.date.getFullYear() === d.getFullYear() &&
                *                        data.date.getMonth() === d.getMonth() &&                        data.date.getDate() === d.getDate())
                *                                   {
                *                            selected = true;
                *                            return false;
                *                         }
                *              );
                *
                *           if (selected)
                *           {
                *                    $("#calendar1").wijcalendar('unSelectDate', data.date);
                *                       } else
                *                                  {
                *                            $("#calendar1").wijcalendar('selectDate', data.date);
                *                       }
                *
                *            list = $("#msg").empty();
                *                        selDates = $("#calendar1").wijcalendar('option', 'selectedDates');
                *                       $.each(selDates, function (i, d)
                *                       {
                *                 var li = $("<li/>");
                *                 li.text(d.getFullYear() + "/" + (d.getMonth() + 1) + "/" + d.getDate());
                *                list.append(li);
                *             });
                *
                *                return false;
                *             }
                *                    });
                */
                this.beforeSelect = null;
                /** The afterSelect event handler.
                * A function called after user selects a day by mouse.
                * @event
                * @param {Object} e The jQuery.Event object.
                * @param {ISelectedDate} args The data with this event.
                */
                this.afterSelect = null;
                /** The selectedDatesChanged event handler.
                * A function called after the selectedDates collection changed.
                * @event
                * @dataKey {Date} dates The array with all selected date object.
                * @example
                *  $("#calendar").wijcalendar(
                *   {
                *   popupMode: true,
                *   selectedDatesChanged: function () {
                *   var selDate = $(this).wijcalendar("getSelectedDate");
                *   var selectDate = new Date(selDate);
                *   if (!!selDate) $("#popdate").val(selectDate.getMonth() + 1 + "/" + selectDate.getDate() + "/" + selectDate.getFullYear());
                *   }
                */
                this.selectedDatesChanged = null;
            }
            return wijcalendar_options;
        })();
        ;
        wijcalendar.prototype.options = $.extend(true, {}, wijmo.wijmoWidget.prototype.options, new wijcalendar_options());
        $.wijmo.registerWidget("wijcalendar", wijcalendar.prototype);

        

        /** @ignore */
        var htmlTextWriter = (function () {
            function htmlTextWriter() {
                this._html = [];
            }
            htmlTextWriter.prototype.writeTagLeftChar = function () {
                this._html[this._html.length] = '<';
            };

            htmlTextWriter.prototype.writeTagRightChar = function () {
                this._html[this._html.length] = '>';
            };

            htmlTextWriter.prototype.write = function (text) {
                this._html[this._html.length] = ' ' + text + ' ';
            };

            htmlTextWriter.prototype.writeBeginTag = function (tagName) {
                this._html[this._html.length] = '<' + tagName;
            };

            htmlTextWriter.prototype.writeEndTag = function (tagName) {
                this._html[this._html.length] = '</' + tagName + '>';
            };

            htmlTextWriter.prototype.writeFullBeginTag = function (tagName) {
                this._html[this._html.length] = '<' + tagName + '>';
            };

            htmlTextWriter.prototype.writeSelfClosingTagEnd = function () {
                this._html[this._html.length] = '/>';
            };

            htmlTextWriter.prototype.writeAttribute = function (name, value) {
                if (value === undefined || value === null) {
                    return;
                }
                this._html[this._html.length] = ' ' + name + '=\"';
                this._html[this._html.length] = value;
                this._html[this._html.length] = '\"';
            };

            htmlTextWriter.prototype.clean = function () {
                this._html = [];
            };

            htmlTextWriter.prototype.toString = function () {
                return this._html.join('');
            };
            return htmlTextWriter;
        })();
        _calendar.htmlTextWriter = htmlTextWriter;

        var wijDateOps = {
            addDays: function (date, days) {
                var dt = new Date(date.getFullYear(), date.getMonth(), date.getDate() + days);
                if (days) {
                    if (dt.getDate() === date.getDate()) {
                        dt = new Date(date.getFullYear(), date.getMonth(), date.getDate());
                        dt.setTime(dt.getTime() + (days * 24 * 3600 * 1000));
                    }
                }
                return dt;
            },
            addMonths: function (date, months) {
                return new Date(date.getFullYear(), date.getMonth() + months, 1);
            },
            addYears: function (date, years) {
                return this.addMonths(date, years * 12);
            },
            getDate: function (date) {
                return new Date(date.getFullYear(), date.getMonth(), date.getDate());
            },
            getTicks: function (date) {
                return date.valueOf();
            },
            isSameDate: function (date1, date2) {
                return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate();
            },
            isSameMonth: function (date1, date2) {
                return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth();
            },
            getDaysInMonth: function (date) {
                return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
            },
            getWeekStartDate: function (date, firstDayOfWeek) {
                return new Date(date.getFullYear(), date.getMonth(), date.getDate() - ((date.getDay() - firstDayOfWeek + 7) % 7));
            },
            getDayOfYear: function (date) {
                var start = new Date(date.getFullYear(), 0, 1), distance = this.getTicks(date) - this.getTicks(start), days = distance / (24 * 60 * 60 * 1000);
                return Math.floor(days) + 1;
            },
            getFirstDayWeekOfYear: function (date, firstDayOfWeek) {
                var days = this.getDayOfYear(date) - 1, offset = date.getDay() - (days % 7), weeks;
                offset = ((offset - firstDayOfWeek) + 14) % 7;
                weeks = ((days + offset) / 7);
                return Math.floor(weeks) + 1;
            },
            getDayOfWeek: function (date, firstDayOfWeek) {
                return ((date.getDay() - firstDayOfWeek + 7) % 7);
            },
            getWeekOfYearFullDays: function (time, rule, firstDayOfWeek, fullDays) {
                var days = this.getDayOfYear(time) - 1, offset = (time.getDay()) - (days % 7);
                offset = ((firstDayOfWeek - offset) + 14) % 7;
                if ((offset) && (offset >= fullDays)) {
                    offset -= 7;
                }
                offset = days - offset;
                if (offset >= 0) {
                    return (Math.floor(offset / 7) + 1);
                }
                return this.getWeekOfYearFullDays(this.addDays(time, -(days + 1)), rule, firstDayOfWeek, fullDays);
            },
            getWeekOfYear: function (date, rule, firstDayOfWeek) {
                switch (rule) {
                    case "firstDay":
                        return this.getFirstDayWeekOfYear(date, firstDayOfWeek);
                    case "firstFullWeek":
                        return this.getWeekOfYearFullDays(date, rule, firstDayOfWeek, 7);
                    case "firstFourDayWeek":
                        return this.getWeekOfYearFullDays(date, rule, firstDayOfWeek, 4);
                }
                return this.getFirstDayWeekOfYear(date, firstDayOfWeek);
            },
            getDateToken: function (date) {
                return date.getFullYear() + '_' + date.getMonth() + '_' + date.getDate();
            }
        };

        /** @ignore */
        var wijMonthView = (function () {
            function wijMonthView(calendar, displayDate) {
                this.calendar = calendar;

                if (displayDate === undefined || wijDateOps.isSameDate(displayDate, new Date(1900, 0, 1))) {
                    displayDate = new Date();
                }

                this.displayDate = displayDate;
                this.id = this.calendar._getId() + '_' + this.calendar._getMonthID(displayDate);
                this.isFirst = false;
                this.isLast = false;
                this.showPreview = false;
                this.culture = this.calendar._getCulture();
                this._calcDates(this.displayDate);
            }
            wijMonthView.prototype._calcDates = function (date) {
                var daysInMonth = wijDateOps.getDaysInMonth(date);
                this._startDateInMonth = new Date(date.getFullYear(), date.getMonth(), 1);
                this._endDateInMonth = wijDateOps.addDays(this._startDateInMonth, daysInMonth - 1);
                this._startDate = wijDateOps.getWeekStartDate(this._startDateInMonth, this.culture.calendar.firstDay);
                this._endDate = wijDateOps.addDays(this._startDate, this.calendar.options.dayRows * this.calendar.options.dayCols - 1);
            };

            wijMonthView.prototype._isFirstMonth = function () {
                var date = this.calendar.getDisplayDate();
                return wijDateOps.isSameMonth(this._startDateInMonth, date);
            };

            wijMonthView.prototype._isLastMonth = function () {
                var date = this.calendar.getDisplayDate();
                date = new Date(date.getFullYear(), date.getMonth(), 1);
                date = wijDateOps.addMonths(date, this.calendar.options.monthCols * this.calendar.options.monthRows - 1);
                return wijDateOps.isSameMonth(this._startDateInMonth, date);
            };

            wijMonthView.prototype.getStartDate = function () {
                return this._startDate;
            };

            wijMonthView.prototype.getEndDate = function () {
                return this._endDate;
            };

            wijMonthView.prototype._getMonthDate = function () {
                if (this._startDateInMonth === undefined) {
                    //this._calcDates(this.getDisplayDate());
                }
                return this._startDateInMonth;
            };

            wijMonthView.prototype._setMonthDate = function (date) {
                this._calcDates(date);
            };

            wijMonthView.prototype._getWeekDayText = function (day, format) {
                format = format || "short";
                var days = this.culture.calendar.days, text = '';
                switch (format) {
                    case "full":
                        text = days.names[day];
                        break;
                    case "firstLetter":
                        text = days.names[day].substring(0, 1);
                        break;
                    case "abbreviated":
                        text = days.namesAbbr[day];
                        break;
                    default:
                        text = days.namesShort[day];
                        break;
                }
                return text;
            };

            wijMonthView.prototype._getRowCount = function () {
                var o = this.calendar.options;
                return o.showWeekDays ? o.dayRows + 1 : o.dayRows;
            };

            wijMonthView.prototype._getColCount = function () {
                var o = this.calendar.options;
                return o.showWeekNumbers ? o.dayCols + 1 : o.dayCols;
            };

            wijMonthView.prototype._getDayType = function (date) {
                var o = this.calendar.options, dayType = wijDayType.general, dow = date.getDay(), weekEnd = dow === 6 || dow === 0, outOfRange = date < o.minDate || date > o.maxDate, otherMonth = date < this._startDateInMonth || date > this._endDateInMonth, isDisabled = outOfRange || this.calendar._getDisabledDates().contains(date), isSelected = this.calendar._getSelectedDates().contains(date), today = new Date(), isToday = wijDateOps.isSameDate(date, today), isCustom = false;
                if (weekEnd) {
                    dayType |= wijDayType.weekEnd;
                }
                if (isToday) {
                    dayType |= wijDayType.today;
                }
                if (isDisabled) {
                    dayType |= wijDayType.disabled;
                }
                if (otherMonth) {
                    dayType |= wijDayType.otherMonth;
                }
                if (outOfRange) {
                    dayType |= wijDayType.outOfRange;
                }
                if (isSelected) {
                    dayType |= wijDayType.selected;
                }
                if (isCustom) {
                    dayType |= wijDayType.custom;
                }
                if (otherMonth && !o.showOtherMonthDays) {
                    dayType |= wijDayType.gap;
                }
                return dayType;
            };

            wijMonthView.prototype._refreshDate = function (date) {
                if (date < this._startDate || date > this._endDate) {
                    return;
                }
                var o = this.calendar.options, dUTC = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()), startUTC = Date.UTC(this._startDate.getFullYear(), this._startDate.getMonth(), this._startDate.getDate()), offset = (Math.floor(Math.abs(dUTC - startUTC) / (24 * 60 * 60 * 1000))), row = Math.floor(offset / this.calendar.options.dayCols), col = Math.floor(offset % this.calendar.options.dayCols), tbl, r, dayCell, dayType;
                if (o.showWeekNumbers) {
                    col++;
                }
                if (o.showWeekDays) {
                    row++;
                }

                tbl = $("#" + this.id, this.calendar.element).get(0);
                if (tbl) {
                    if (row < tbl.rows.length) {
                        r = tbl.rows[row];
                        if (col < r.cells.length) {
                            dayCell = r.cells[col];
                            dayType = this._getDayType(date);
                            $(dayCell).attr('daytype', dayType.toString());
                            this.calendar._refreshDayCell(dayCell);
                        }
                    }
                }
            };

            wijMonthView.prototype._fillDayCell = function (hw, date, previewMode) {
                var o = this.calendar.options, custom = null, text = date.getDate().toString(), tooltip = this.calendar._formatDate(o.toolTipFormat || "dddd, MMMM dd, yyyy", date), dayType = this._getDayType(date), selectable = this.calendar._isSelectable(dayType), css = this.calendar._getCellClassName(dayType, date, previewMode);

                text = (o.showDayPadding && text.length === 1) ? '0' + text : text;

                hw.writeBeginTag('td');
                hw.writeAttribute('daytype', (dayType).toString());
                if (selectable) {
                    hw.writeAttribute('title', tooltip);
                    hw.writeAttribute('aria-label', tooltip);
                }
                hw.writeAttribute('date', date.toDateString());
                hw.writeAttribute('class', css.cssCell);
                hw.writeAttribute('role', 'gridcell');
                if (!selectable) {
                    hw.writeAttribute('aria-disabled', 'true');
                }
                hw.writeTagRightChar();

                if ((dayType & wijDayType.gap)) {
                    hw.write('&#160;');
                } else {
                    if (custom && custom.content) {
                        hw.write(custom.content);
                    } else {
                        hw.writeBeginTag('a');
                        hw.writeAttribute('class', css.cssText);
                        hw.writeAttribute('href', '#');
                        hw.writeAttribute('onclick', 'return false;');
                        hw.writeTagRightChar();
                        hw.write(text);
                        hw.writeEndTag('a');
                    }
                }

                hw.writeEndTag('td');
            };

            wijMonthView.prototype.getHtml = function (tableOnly) {
                tableOnly = !!tableOnly;
                var o = this.calendar.options, wijCSS = o.wijCSS, previewMode = !!this.calendar.element.data('preview.wijcalendar'), hw = new htmlTextWriter(), i, j, dayOfWeek, weekStartDate, weekEnd, colIndex, txt, fullTxt, date, wnDate, rowIndex, weekNumber;

                if (!tableOnly && o.showTitle) {
                    hw.write(this.calendar._getHeaderHtml(this._startDateInMonth, this.isFirst, this.isLast));
                }

                if (!tableOnly && !previewMode && this.showPreview) {
                    hw.writeBeginTag('div');
                    hw.writeAttribute('class', 'wijmo-wijcalendar-prevpreview-button');
                    hw.writeAttribute('role', 'button');
                    hw.writeAttribute('aria-haspopup', 'true');
                    hw.writeAttribute('id', this.calendar._getId() + 'prevPreview');
                    hw.writeTagRightChar();
                    hw.writeBeginTag('a');
                    hw.writeAttribute('class', wijCSS.icon + ' ' + wijCSS.uiIconGripDottedVertical);
                    hw.writeAttribute('href', '#');
                    hw.writeAttribute('title', o.prevPreviewTooltip);
                    hw.writeAttribute('aria-label', o.prevPreviewTooltip);
                    hw.writeAttribute('onclick', 'return false;');
                    hw.writeTagRightChar();
                    hw.write('&#160;');
                    hw.writeEndTag('a');
                    hw.writeEndTag('div');
                }

                hw.writeBeginTag('table');
                hw.writeAttribute('id', this.id);
                hw.writeAttribute('class', wijCSS.datepickerCalendar + ' wijmo-wijcalendar-table');
                hw.writeAttribute('role', 'grid');
                hw.writeAttribute('summary', this.calendar._getTitleText(this._startDateInMonth));
                hw.writeAttribute('onselectstart', 'return false;');
                hw.writeTagRightChar();
                if (o.showWeekDays) {
                    hw.writeFullBeginTag('thead');
                    hw.writeBeginTag('tr');
                    hw.writeAttribute('role', 'row');
                    hw.writeTagRightChar();
                    if (o.showWeekNumbers) {
                        hw.writeBeginTag('th');
                        hw.writeAttribute('id', this.id + '_ms');
                        hw.writeAttribute('class', wijCSS.uiDatepickerWeekCol + ' wijmo-wijcalendar-monthselector' + (!!o.selectionMode.month ? ' wijmo-wijcalendar-selectable' : ''));
                        hw.writeAttribute('role', 'columnheader');
                        hw.writeTagRightChar();

                        if (!!o.selectionMode.month && !previewMode) {
                            hw.writeBeginTag('a');
                            hw.writeAttribute('class', wijCSS.icon + ' ' + wijCSS.iconArrowRightDown);
                            hw.writeAttribute('style', 'display: block;');
                            hw.writeSelfClosingTagEnd();
                        } else {
                            hw.write(o.weekString ? o.weekString : "Wk");
                        }

                        hw.writeEndTag('th');
                    }

                    dayOfWeek = this._startDate.getDay();
                    weekStartDate = this._startDate;
                    for (i = 0; i < o.dayCols; i++) {
                        weekEnd = dayOfWeek === 6 || dayOfWeek === 0;
                        colIndex = i + ((o.showWeekNumbers) ? 1 : 0);
                        txt = this._getWeekDayText(dayOfWeek, o.weekDayFormat);
                        fullTxt = this._getWeekDayText(dayOfWeek, "full");
                        hw.writeBeginTag('th');
                        hw.writeAttribute('id', this.id + '_cs_' + colIndex);
                        hw.writeAttribute('class', wijCSS.datepickerWeekDay + (weekEnd ? ' ' + wijCSS.datepickerWeekEnd : '') + (!!o.selectionMode.weekDay ? ' wijmo-wijcalendar-selectable' : ''));
                        hw.writeAttribute('role', 'columnheader');
                        hw.writeTagRightChar();

                        hw.writeBeginTag('span');
                        hw.writeAttribute('title', fullTxt);
                        hw.writeAttribute('aria-label', fullTxt);
                        hw.writeTagRightChar();
                        hw.write(txt);
                        hw.writeEndTag('span');

                        hw.writeEndTag('th');
                        dayOfWeek = ((dayOfWeek + 1) % 7);
                        weekStartDate = wijDateOps.addDays(weekStartDate, 1);
                    }
                    hw.writeEndTag('tr');
                    hw.writeEndTag('thead');
                }

                hw.writeFullBeginTag('tbody');
                date = this._startDate;
                wnDate = this._startDateInMonth;
                for (i = 0; i < o.dayRows; i++) {
                    hw.writeBeginTag('tr');
                    hw.writeAttribute('role', 'row');
                    hw.writeTagRightChar();
                    if (o.showWeekNumbers) {
                        rowIndex = i + ((o.showWeekDays) ? 1 : 0);
                        hw.writeBeginTag('td');
                        hw.writeAttribute('id', this.id + '_rs_' + rowIndex);
                        hw.writeAttribute('class', wijCSS.uiDatepickerWeekCol + ' wijmo-wijcalendar-week-num' + (!!o.selectionMode.weekNumber ? ' wijmo-wijcalendar-selectable' : ''));
                        hw.writeAttribute('role', 'rowheader');
                        hw.writeTagRightChar();
                        weekNumber = wijDateOps.getWeekOfYear(wnDate, o.calendarWeekRule, this.culture.calendar.firstDay);
                        hw.write(weekNumber);
                        hw.writeEndTag('td');
                        wnDate = wijDateOps.addDays(wnDate, o.dayCols);
                    }
                    for (j = 0; j < o.dayCols; j++) {
                        this._fillDayCell(hw, date, previewMode);
                        date = wijDateOps.addDays(date, 1);
                    }
                    hw.writeEndTag('tr');
                }
                hw.writeEndTag('tbody');
                hw.writeEndTag('table');

                if (!tableOnly && !previewMode && this.showPreview) {
                    hw.writeBeginTag('div');
                    hw.writeAttribute('class', 'wijmo-wijcalendar-nextpreview-button');
                    hw.writeAttribute('role', 'button');
                    hw.writeAttribute('aria-haspopup', 'true');
                    hw.writeAttribute('id', this.calendar._getId() + 'nextPreview');
                    hw.writeTagRightChar();
                    hw.writeBeginTag('a');
                    hw.writeAttribute('class', wijCSS.icon + ' ' + wijCSS.uiIconGripDottedVertical);
                    hw.writeAttribute('href', '#');
                    hw.writeAttribute('title', o.nextPreviewTooltip);
                    hw.writeAttribute('aria-label', o.nextPreviewTooltip);
                    hw.writeAttribute('onclick', 'return false;');
                    hw.writeTagRightChar();
                    hw.write('&#160;');
                    hw.writeEndTag('a');
                    hw.writeEndTag('div');
                }

                return hw.toString();
            };
            return wijMonthView;
        })();
        _calendar.wijMonthView = wijMonthView;

        /** @ignore */
        var wijDateCollection = (function () {
            function wijDateCollection(calendar, optionName) {
                this._calendar = calendar;
                this._optionName = optionName ? optionName : 'selectedDates';
                this._normalize();
            }
            wijDateCollection.prototype.getDates = function () {
                if (this._calendar.options[this._optionName] === undefined) {
                    this._calendar.options[this._optionName] = [];
                }

                return this._calendar.options[this._optionName];
            };

            wijDateCollection.prototype.setDates = function (dates) {
                this._calendar.options[this._optionName] = dates;
                this._normalize();
            };

            wijDateCollection.prototype.getCount = function () {
                return this.getDates().length;
            };

            wijDateCollection.prototype.clear = function () {
                this.setDates([]);
            };

            wijDateCollection.prototype.add = function (date) {
                this.addRange(date, date);
            };

            wijDateCollection.prototype.remove = function (date) {
                this.removeRange(date, date);
            };

            wijDateCollection.prototype.indexOf = function (date) {
                if (!this.getCount()) {
                    return -1;
                }
                return this._findRangeBound(date, true, false);
            };

            wijDateCollection.prototype.contains = function (date) {
                return this.indexOf(date) !== -1;
            };

            wijDateCollection.prototype.removeRange = function (start, end) {
                if (!this.getCount()) {
                    return;
                }
                var startIndex = this._findRangeBound(start, false, true), endIndex = this._findRangeBound(end, false, false), dates, startSlice, endSlice;
                if (startIndex < 0 || endIndex < 0) {
                    return;
                }
                if (startIndex > endIndex) {
                    return;
                }
                dates = this.getDates();
                if (dates[endIndex] > end) {
                    return;
                }
                startSlice = (!startIndex) ? [] : dates.slice(0, startIndex);
                endSlice = endIndex >= (dates.length - 1) ? [] : dates.slice(endIndex + 1);
                this.setDates(startSlice.concat(endSlice));
            };

            wijDateCollection.prototype.addRange = function (start, end) {
                this.removeRange(start, end);
                var dates = this.getDates(), insertIndex = this._findRangeBound(start, false, true), startSlice = (!insertIndex) ? [] : dates.slice(0, insertIndex), endSlice = dates.slice(insertIndex), midSlice = [], curDate;
                start = wijDateOps.getDate(start);
                end = wijDateOps.getDate(end);
                for (curDate = start; curDate <= end; curDate = wijDateOps.addDays(curDate, 1)) {
                    midSlice[midSlice.length] = curDate;
                }
                this.setDates(startSlice.concat(midSlice.concat(endSlice)));
            };

            wijDateCollection.prototype._findRangeBound = function (date, exact, isStart) {
                var dates = this.getDates(), low = 0, hi = dates.length, index;
                while (low < hi) {
                    index = (low + hi) >> 1;
                    if (wijDateOps.isSameDate(date, dates[index])) {
                        return index;
                    }
                    if (date < dates[index]) {
                        hi = index;
                    } else {
                        low = index + 1;
                    }
                }
                if (exact) {
                    return -1;
                }
                return (isStart) ? low : hi;
            };

            wijDateCollection.prototype._parseDate = function (date) {
                var strDate;
                if (!date) {
                    date = new Date();
                } else {
                    if (typeof date === 'string') {
                        strDate = date;
                    }
                }

                if (strDate) {
                    strDate = strDate.replace(/-/g, '/');

                    try  {
                        date = new Date(strDate);
                        if (isNaN(date)) {
                            date = new Date();
                        }
                    } catch (e) {
                        date = new Date();
                    }
                }

                return date;
            };

            wijDateCollection.prototype._normalize = function () {
                //Normalize the array
                var dates = this._calendar.options[this._optionName], self = this, newDates;
                if ($.isArray(dates)) {
                    newDates = $.map(dates, function (d, i) {
                        return self._parseDate(d);
                    });

                    this._calendar.options[this._optionName] = newDates.sort(function (a, b) {
                        return a.getTime() - b.getTime();
                    });
                }
            };
            return wijDateCollection;
        })();
        _calendar.wijDateCollection = wijDateCollection;

        /** @ignore */
        var wijMyGrid = (function () {
            function wijMyGrid(calendar) {
                this.gridType = "month";
                this.calendar = calendar;
                if (calendar) {
                    this.culture = calendar._getCulture();
                }
            }
            wijMyGrid.prototype._getLastDate = function (year, month) {
                var date;
                month++;
                date = new Date(year, month, 1);
                return (new Date(date.getTime() - 1000 * 60 * 60 * 24)).getDate();
            };

            wijMyGrid.prototype.select = function (index, value) {
                var date = this.calendar.getDisplayDate(), currentMonthLastDate, targetMonthLastDate;
                switch (this.gridType) {
                    case "month":
                        currentMonthLastDate = this._getLastDate(date.getFullYear(), date.getMonth());
                        targetMonthLastDate = this._getLastDate(date.getFullYear(), value);
                        if (currentMonthLastDate > targetMonthLastDate) {
                            date.setDate(targetMonthLastDate);
                        }
                        date.setMonth(value);
                        break;
                    case "year":
                        date.setFullYear(value);
                        break;
                    case "decade":
                        date.setFullYear(value);
                        break;
                }

                this.calendar.options.displayDate = date;
            };

            wijMyGrid.prototype.getSelectedIndex = function () {
                var date = this.calendar.getDisplayDate(), year = date.getFullYear(), startYear = Math.floor(year / 10) * 10 - 1, startDecade = Math.floor(year / 100) * 100 - 10;
                switch (this.gridType) {
                    case "month":
                        return date.getMonth();
                    case "year":
                        return year - startYear;
                    case "decade":
                        return Math.floor((year - startDecade) / 10);
                }
                return 0;
            };

            wijMyGrid.prototype.getTitle = function () {
                var self = this, date = self.calendar.getDisplayDate(), year = date.getFullYear(), startYear = Math.floor(year / 10) * 10 - 1, startDecade = Math.floor(year / 100) * 100 - 10;
                switch (self.gridType) {
                    case "month":
                        return self.calendar._formatDate(self.calendar.options.monthViewTitleFormat || "yyyy", date);
                    case "year":
                        return (startYear + 1) + " - " + (startYear + 10);
                    case "decade":
                        return (startDecade + 10) + " - " + (startDecade + 109);
                }
                return '';
            };

            wijMyGrid.prototype.getHtml = function (date, tableOnly) {
                if (date === undefined) {
                    date = this.calendar.getDisplayDate();
                } else {
                    if (typeof (date) === 'boolean') {
                        tableOnly = date;
                        date = this.calendar.getDisplayDate();
                    }
                }

                tableOnly = !!tableOnly;
                var o = this.calendar.options, rows = 3, cols = 4, hw = new htmlTextWriter(), wijCSS = o.wijCSS, height, year, startMonth, startYear, startDecade, ms, i, j, index, selected, outofRange, cellText, v, cls;
                if (o.showTitle && !tableOnly) {
                    hw.write(this.calendar._getHeaderHtml(null, true, true));
                }

                height = 100 / rows + '%';
                height = '30%';
                hw.writeBeginTag('table');
                hw.writeAttribute('class', wijCSS.datepickerCalendar + ' wijmo-wijcalendar-mygrid');
                hw.writeAttribute('role', 'grid');
                hw.writeAttribute('onselectstart', 'return false;');
                hw.writeTagRightChar();
                year = date.getFullYear();
                startMonth = date.getFullYear() * 12;
                startYear = Math.floor(year / 10) * 10 - 1;
                startDecade = Math.floor(year / 100) * 100 - 10;
                ms = this.culture.calendar.months;

                for (i = 0; i < rows; i++) {
                    hw.writeBeginTag('tr');
                    hw.writeAttribute('role', 'row');
                    hw.writeAttribute('height', height);
                    hw.writeTagRightChar();
                    for (j = 0; j < cols; j++) {
                        index = i * 4 + j;
                        selected = false;
                        outofRange = false;
                        cellText = '';
                        v = null;
                        switch (this.gridType) {
                            case "month":
                                if (date.getMonth() === index) {
                                    selected = true;
                                }
                                v = index;
                                cellText = ms.namesAbbr[index];
                                outofRange = ((startMonth + index) < (o.minDate.getFullYear() * 12 + o.minDate.getMonth())) || ((startMonth + index) > (o.maxDate.getFullYear() * 12 + o.maxDate.getMonth()));
                                break;
                            case "year":
                                if (index === 0 || index === 11) {
                                    outofRange = true;
                                }
                                v = startYear + index;
                                if (v < o.minDate.getFullYear() || v > o.maxDate.getFullYear()) {
                                    outofRange = true;
                                } else {
                                    selected = (year === v);
                                }
                                cellText = v.toString();
                                break;
                            case "decade":
                                if (index === 0 || index === 11) {
                                    outofRange = true;
                                }
                                v = startDecade + index * 10;
                                if (v + 10 < o.minDate.getFullYear() || v > o.maxDate.getFullYear()) {
                                    outofRange = true;
                                } else {
                                    selected = (year >= v && year < (v + 10));
                                }
                                cellText = v.toString() + '-<br/>' + (v + 9).toString();
                                break;
                        }

                        cls = wijCSS.datepickerWeekDay;
                        if (outofRange) {
                            cls = cls + ' ' + wijCSS.datepickerOtherMonth + ' ' + wijCSS.prioritySecondary + ' ' + wijCSS.datepickerUnselectable;
                        } else {
                            if (!this.calendar._isDisabled()) {
                                cls += " wijmo-wijcalendar-day-selectable";
                            }
                        }

                        cls += " " + wijCSS.stateDefault + (outofRange ? ' ' + wijCSS.stateDisabled : '') + (selected ? ' ' + wijCSS.stateActive + ' ' + wijCSS.stateHighlight : '');

                        hw.writeBeginTag('td');
                        hw.writeAttribute('class', cls);
                        hw.writeAttribute('role', 'gridcell');

                        //hw.writeAttribute('width', width);
                        hw.writeAttribute('index', index.toString());
                        hw.writeAttribute('value', v.toString());
                        hw.writeAttribute('other', outofRange.toString());
                        hw.writeTagRightChar();

                        hw.writeBeginTag('a');
                        if (outofRange) {
                            hw.writeAttribute('onclick', 'return false;');
                        }
                        hw.writeAttribute('href', '#');
                        hw.writeTagRightChar();
                        hw.write(cellText);
                        hw.writeEndTag('a');
                        hw.writeEndTag('td');
                    }
                    hw.writeEndTag('tr');
                }
                hw.writeEndTag('table');
                return hw.toString();
            };
            return wijMyGrid;
        })();
        _calendar.wijMyGrid = wijMyGrid;
    })(wijmo.calendar || (wijmo.calendar = {}));
    var calendar = wijmo.calendar;
})(wijmo || (wijmo = {}));

