/*
* Kendo UI Web v2012.1.322 (http://kendoui.com)
* Copyright 2012 Telerik AD. All rights reserved.
*
* Kendo UI Web commercial licenses may be obtained at http://kendoui.com/web-license
* If you do not own a commercial license, this file shall be governed by the
* GNU General Public License (GPL) version 3.
* For GPL requirements, please review: http://www.gnu.org/copyleft/gpl.html
*/
(function($, undefined) {
    /**
     * @name kendo.ui.Calendar.Description
     *
     * @section
     * <p>
     *  The <b>Calendar</b> renders a graphical calendar that supports
     *  navigation and selection. It supports custom templates for its
     *  "month" view, configurable options for a minimum and maximum date,
     *  start view and the depth of the navigation.
     * </p>
     * <h3>Getting Started</h3>
     *
     * @exampleTitle Create a div element
     * @example
     * <div id="calendar"></div>
     *
     * @exampleTitle Initialize the Calendar via a jQuery ID selector
     * @example
     * $(document).ready(function(){
     *  $("#calendar").kendoCalendar();
     * });
     *
     * @section
     * <p>
     *  When a <b>Calendar</b> is initialized, it will automatically be
     *  displayed near the location of the used HTML element.
     * </p>
     * <h3>Configuring Calendar Behaviors</h3>
     * <p>
     *  The <b>Calendar</b> provides many configuration options that can be
     *  easily set during initialization. Among the properties that can be
     *  controlled:
     * </p>
     * <ul>
     *  <li>Selected date</li>
     *  <li>Minimum and/or maximum date</li>
     *  <li>Start view</li>
     *  <li>
     *   Define the navigation depth (last view to which end user can
     *   navigate)
     *  </li>
     *  <li>Day template</li>
     *  <li>Footer template</li>
     * </ul>
     *
     * @exampleTitle Create Calendar with selected date and a defined minimum
     * and maximum date
     * @example
     * $("#calendar").kendoCalendar({
     *  value: new Date(),
     *  min: new Date(1950, 0, 1),
     *  max: new Date(2049, 11, 31)
     * });
     *
     * @section
     * <p>
     *  The <b>Calendar</b> will not navigate before than the minimum
     *  date specified. It will also not navigate ahead the maximum date
     *  specified.
     * </p>
     * @section
     * <h3>Define start view and navigation depth</h3>
     * <p>
     *  The first rendered view can be defined with "start" option.
     *  Navigation depth can be controlled with "depth" option. Predefined
     *  views are:
     *  <ul>
     *   <li>"month" - shows the days from the month</li>
     *   <li>"year" - shows the months of the year</li>
     *   <li>"decade" - shows the years from the decade</li>
     *   <li>"century" - shows the decades from the century</li>
     *  </ul>
     * </p>
     *
     * @exampleTitle Create a Calendar, which allows a user to select a month
     * @example
     * $("#calendar").kendoCalendar({
     *  start: "year",
     *  depth: "year"
     * });
     *
     * @section
     * <h3>Customize day template</h3>
     * <p>
     *  The <b>Calendar</b> allows to customize content of the rendered day
     *  in the "month" view.
     * </p>
     *
     * @exampleTitle Create a Calendar with custom template
     * @example
     * $("#calendar").kendoCalendar({
     *  month: {
     *   content: '<div class="custom"><#=data.value#></div>'
     *  }
     * });
     *
     * @section
     * <p>
     *  This templates wraps the "value" in a div HTML element. Here is an
     *  example of the object passed to the template function:
     * </p>
     *
     * @exampleTitle Structure of the data object passed to the template
     * @example
     * data = {
     *  date: date, // Date object corresponding to the current cell
     *  title: kendo.toString(date, "D"),
     *  value: date.getDate(),
     *  dateString: "2011/0/1" // formatted date using yyyy/MM/dd format and month is zero-based
     * };
     *
     * @section
     * <h3>Accessing an Existing Calendar</h3>
     * <p>
     *  You can reference an existing <b>Calendar</b> instance via
     *  <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>.
     *  Once a reference has been established, you can use the API to control
     *  its behavior.
     * </p>
     *
     * @exampleTitle Accessing an existing Calendar instance
     * @example
     * var calendar = $("#calendar").data("kendoCalendar");
     *
     */
    var kendo = window.kendo,
        ui = kendo.ui,
        Widget = ui.Widget,
        parse = kendo.parseDate,
        template = kendo.template,
        touch = kendo.support.touch,
        transitions = kendo.support.transitions,
        transitionOrigin = transitions ? transitions.css + "transform-origin" : "",
        cellTemplate = template('<td#=data.cssClass#><a class="k-link" href="\\#" data-#=data.ns#value="#=data.dateString#">#=data.value#</a></td>', { useWithBlock: false }),
        emptyCellTemplate = template("<td>&nbsp;</td>", { useWithBlock: false }),
        isIE8 = $.browser.msie && parseInt($.browser.version, 10) < 9,
        CLICK = touch ? "touchend" : "click",
        MIN = "min",
        LEFT = "left",
        SLIDE = "slide",
        MONTH = "month",
        CENTURY = "century",
        CHANGE = "change",
        NAVIGATE = "navigate",
        VALUE = "value",
        HOVER = "k-state-hover",
        DISABLED = "k-state-disabled",
        OTHERMONTH = "k-other-month",
        OTHERMONTHCLASS = ' class="' + OTHERMONTH + '"',
        TODAY = "k-nav-today",
        CELLSELECTOR = "td:has(.k-link)",
        MOUSEENTER = touch ? "touchstart" : "mouseenter",
        MOUSELEAVE = touch ? "touchend" : "mouseleave",
        MS_PER_MINUTE = 60000,
        MS_PER_DAY = 86400000,
        PREVARROW = "_prevArrow",
        NEXTARROW = "_nextArrow",
        proxy = $.proxy,
        extend = $.extend,
        DATE = Date,
        views = {
            month: 0,
            year: 1,
            decade: 2,
            century: 3
        };

    var Calendar = Widget.extend(/** @lends kendo.ui.Calendar.prototype */{
        /**
          * @constructs
          * @extends kendo.ui.Widget
          * @param {DomElement} element DOM element
          * @param {Object} options Configuration options.
          * @option {Date} [value] <null> Specifies the selected date.
          * _example
          * // set the selected date to Jan 1st. 2012
          * $("#calendar").kendoCalendar({
          *     value: new Date(2012, 0, 1)
          * });
          * _exampleTitle To set after initialization
          * _example
          * // get a reference to the Kendo UI calendar widget
          * var calendar = $("#calendar").data("kendoCalendar");
          * // set the selected date on the calendar to Jan 1st, 2012
          * calendar.value(new Date(2012, 0, 1));
          * @option {Date} [min] <Date(1900, 0, 1)> Specifies the minimum date, which the calendar can show.
          * _example
          * // set the min date to Jan 1st, 2011
          * $("#calendar").kendoCalendar({
          *     min = new Date(2011, 0, 1)
          * });
          * _exampleTitle To set after initialization
          * _example
          * // get a reference to the Kendo UI calendar widget
          * var calendar = $("#calendar").data("kendoCalendar");
          * // set the min date to Jan 1st, 2011
          * calendar.min(new Date(2011, 0, 1));
          * @option {Date} [max] <Date(2099, 11, 31)> Specifies the maximum date, which the calendar can show.
          * _example
          * $("#calendar").kendoCalendar({
          *     max = new Date(2013, 0, 1);
          * });
          * _exampleTitle To set after initialization
          * _example
          * // get a reference to the Kendo UI calendar widget
          * var calendar = $("#calendar").data("kendoCalendar");
          * // set the max date to Jan 1st, 2013
          * calendar.max(new Date(2013, 0, 1));
          * @option {String} [footer] <> Specifies the content of the footer. If false, the footer will not be rendered.
          * _example
          * // change the footer text from the default current date
          * $("#calendar").kendoCalendar({
          *     footer = "My Custom Footer"
          * });
          * _exampleTitle Hide the footer
          * _example
          * $("#calendar").kendoCalendar({
          *     footer = false;
          * });
          * @option {String} [format] <MM/dd/yyyy> Specifies the format, which is used to parse value set with value() method.
          * _example
          * $("#calendar").kendoCalendar({
          *     format: "yyyy/MM/dd"
          * });
          * @option {String} [start] <month> Specifies the start view.
          * _example
          * $("#calendar").kendoCalendar({
          *     start: "year"
          * });
          * @option {String} [depth] Specifies the navigation depth.
          * _example
          * $("#calendar").kendoCalendar({
          *     depth: "year"
          * });
          * @option {Function} [footer] <> Template to be used for rendering the footer. If false, the footer will not be rendered.
          * _example
          *
          *  //calendar intialization
          *  &lt;script&gt;
          *      $("#calendar").kendoCalendar({
          *          footer: kendo.template("Today - #=kendo.toString(data, 'd') #")
          *      });
          *  &lt;/script&gt;
          * @option {Object} [month] <> Templates for the cells rendered in the "month" view.
          * @option {Function} [month.content] <> Template to be used for rendering the cells in the "month" view, which are in range.
          * _example
          *  //template
          *  &lt;script id="cellTemplate" type="text/x-kendo-tmpl"&gt;
          *      &lt;div class="${ data.value < 10 ? exhibition : party }"&gt;
          *      &lt;/div&gt;
          *      ${ data.value }
          *  &lt;/script&gt;
          *
          *  //calendar intialization
          *  &lt;script&gt;
          *      $("#calendar").kendoCalendar({
          *          month: {
          *             content:  kendo.template($("#cellTemplate").html()),
          *          }
          *      });
          *  &lt;/script&gt;
          * @option {Function} [month.empty] <> Template to be used for rendering the cells in the "month" view, which are not in the min/max range.
          */
        init: function(element, options) {
            var that = this, value;

            Widget.fn.init.call(that, element, options);

            element = that.wrapper = that.element;
            options = that.options;

            element.addClass("k-widget k-calendar");

            that._templates();

            that._header();

            that._footer(that.footer);

            element
                .delegate(CELLSELECTOR, MOUSEENTER, mouseenter)
                .delegate(CELLSELECTOR, MOUSELEAVE, mouseleave)
                .delegate(CELLSELECTOR, CLICK, proxy(that._click, that));

            value = options.value;
            validate(options);

            that._index = views[options.start];
            that._current = new DATE(restrictValue(value, options.min, options.max));

            that.value(value);

            kendo.notify(that);
        },

        options: {
            name: "Calendar",
            value: null,
            min: new DATE(1900, 0, 1),
            max: new DATE(2099, 11, 31),
            footer : '#= kendo.toString(data,"D") #',
            format : "",
            month : {},
            start: MONTH,
            depth: MONTH,
            animation: {
                horizontal: {
                    effects: SLIDE,
                    duration: 500,
                    divisor: 2
                },
                vertical: {
                    effects: "zoomIn",
                    duration: 400
                }
            }
        },

        events: [
            /**
            * Fires when the selected date is changed
            * @name kendo.ui.Calendar#change
            * @event
            * @param {Event} e
            * @example
            * $("#calendar").kendoCalendar({
            *     change: function(e) {
            *         // handle event
            *     });
            * });
            * @exampleTitle To set after initialization
            * @example
            * // get a reference to the Kendo UI calendar widget
            * var calendar = $("#calendar").data("kendoCalendar");
            * // bind to the change event
            * calendar.bind("change", function(e) {
            *      // handle event
            * });
            */
            /**
            * Fires when navigate
            * @name kendo.ui.Calendar#navigate
            * @event
            * @param {Event} e
            * @example
            * $("#calendar").kendoCalendar({
            *     navigate: function(e) {
            *          // handle event
            *     }
            * });
            * @exampleTitle To set after initialization
            * @example
            * // get a reference to the Kendo UI calendar widget
            * var calendar = $("#calendar").data("kendoCalendar");
            * // bind to the change event
            * calendar.bind("navigate", function(e) {
            *      // handle event
            * });
            */
            CHANGE,
            NAVIGATE
        ],

        /**
        * Gets/Sets the min value of the calendar.
        * @param {Date|String} value The min date to set.
        * @returns {Date} The min value of the calendar.
        * @example
        * // get a reference to the calendar widget
        * var calendar = $("#calendar").data("kendoCalendar");
        *
        * // get the min value of the calendar.
        * var min = calendar.min();
        *
        * // set the min value of the calendar.
        * calendar.min(new Date(1900, 0, 1));
        */
        min: function(value) {
            return this._option(MIN, value);
        },

        /**
        * Gets/Sets the max value of the calendar.
        * @param {Date | String} value The max date to set.
        * @returns {Date} The max value of the calendar.
        * @example
        * // get a reference to the calendar widget
        * var calendar = $("#calendar").data("kendoCalendar");
        *
        * // get the max value of the calendar.
        * var max = calendar.max();
        *
        * // set the max value of the calendar.
        * calendar.max(new Date(2100, 0, 1));
        */
        max: function(value) {
            return this._option("max", value);
        },

        /**
        * Navigates to the past
        * @example
        * // get a reference to the calendar widget
        * var calendar = $("#calendar").data("kendoCalendar");
        * // navigate to past
        * calendar.navigateToPast();
        */
        navigateToPast: function() {
            this._navigate(PREVARROW, -1);
        },

        /**
        * Navigates to the future
        * @example
        * // get a reference to the calendar widget
        * var calendar = $("#calendar").data("kendoCalendar");
        * // navigate to future
        * calendar.navigateToFuture();
        */
        navigateToFuture: function() {
            this._navigate(NEXTARROW, 1);
        },

        /**
        * Navigates to the upper view
        * @example
        * // get a reference to the calendar widget
        * var calendar = $("#calendar").data("kendoCalendar");
        * // navigate up
        * calendar.navigateUp();
        */
        navigateUp: function() {
            var that = this,
                index = that._index;

            if (that._title.hasClass(DISABLED)) {
                return;
            }

            that.navigate(that._current, ++index);
        },

        /**
        * Navigates to the lower view
        * @param {Date} value Desired date
        * @example
        * // get a reference to the calendar widget
        * var calendar = $("#calendar").data("kendoCalendar");
        * // navigate down
        * calendar.navigateDown(value);
        */
        navigateDown: function(value) {
            var that = this,
            index = that._index,
            depth = that.options.depth;

            if (!value) {
                return;
            }

            if (index === views[depth]) {
                if (+that._value != +value) {
                    that.value(value);
                    that.trigger(CHANGE);
                }
                return;
            }

            that.navigate(value, --index);
        },

        /**
        * Navigates to view
        * @param {Date} value Desired date
        * @param {String} view Desired view
        * @example
        * // get a reference to the calendar widget
        * var calendar = $("#calendar").data("kendoCalendar");
        * // navigate to the desired date
        * calendar.navigate(value, view);
        */
        navigate: function(value, view) {
            view = isNaN(view) ? views[view] : view;

            var that = this,
                options = that.options,
                min = options.min,
                max = options.max,
                title = that._title,
                from = that._table,
                selectedValue = that._value,
                currentValue = that._current,
                future = value && +value > +currentValue,
                vertical = view !== undefined && view !== that._index,
                to, currentView, compare;

            //do not navigate if the view is still animating
            if (from && from.parent().data("animating")) {
                return;
            }

            if (!value) {
                value = currentValue;
            } else {
                that._current = value = new DATE(restrictValue(value, min, max))
            }

            if (view === undefined) {
                view = that._index;
            } else {
                that._index = view;
            }

            that._view = currentView = calendar.views[view];
            compare = currentView.compare;

            title.toggleClass(DISABLED, view === views[CENTURY])
            that[PREVARROW].toggleClass(DISABLED, compare(value, min) < 1);
            that[NEXTARROW].toggleClass(DISABLED, compare(value, max) > -1);

            if (!from || that._changeView) {
                title.html(currentView.title(value));

                that._table = to = $(currentView.content(extend({
                    min: min,
                    max: max,
                    date: value
                }, that[currentView.name])));

                makeUnselectable(to);

                that._animate({
                    from: from,
                    to: to,
                    vertical: vertical,
                    future: future
                });

                that.trigger(NAVIGATE);
            }

            if (view === views[options.depth] && selectedValue) {
                that._class("k-state-selected", currentView.toDateString(selectedValue));
            }

            that._changeView = true;
        },

        /**
        * Gets/Sets the value of the calendar.
        * @param {Date|String} value The date to set.
        * @returns {Date} The value of the calendar.
        * @example
        * // get a reference to the calendar widget
        * var calendar = $("#calendar").data("kendoCalendar");
        *
        * // get the value of the calendar.
        * var value = calendar.value();
        *
        * // set the value of the calendar.
        * calendar.value(new Date());
        */
        value: function(value) {
            var that = this,
            view = that._view,
            options = that.options,
            min = options.min,
            max = options.max;

            if (value === undefined) {
                return that._value;
            }

            value = parse(value, options.format);

            if (value !== null) {
                value = new DATE(value);

                if (!isInRange(value, min, max)) {
                    value = null;
                }
            }

            that._value = value;
            that._changeView = !value || view && view.compare(value, that._current) !== 0;

            that.navigate(value);
        },

        _animate: function(options) {
            var that = this,
                from = options.from,
                to = options.to;

            if (!from) {
                to.insertAfter(that.element[0].firstChild);
            } else if (!from.is(":visible") || that.options.animation === false) {
                to.insertAfter(from);
                from.remove();
            } else {
                that[options.vertical ? "_vertical" : "_horizontal"](from, to, options.future);
            }
        },

        _horizontal: function(from, to, future) {
            var that = this,
                horizontal = that.options.animation.horizontal,
                effects = horizontal.effects,
                viewWidth = from.outerWidth();

                if (effects && effects.indexOf(SLIDE) != -1) {
                    from.add(to).css({ width: viewWidth });

                    from.wrap("<div/>");

                    from.parent()
                    .css({
                        position: "relative",
                        width: viewWidth * 2,
                        "float": LEFT,
                        left: future ? 0 : -viewWidth
                    });

                    to[future ? "insertAfter" : "insertBefore"](from);

                    extend(horizontal, {
                        effects: SLIDE + ":" + (future ? LEFT : "right"),
                        complete: function() {
                            from.remove();
                            to.unwrap();
                        }
                    });

                    from.parent().kendoStop(true, true).kendoAnimate(horizontal);
                }
        },

        _vertical: function(from, to) {
            var that = this,
                vertical = that.options.animation.vertical,
                effects = vertical.effects,
                viewWidth = from.outerWidth(),
                cell, position;

            if (effects && effects.indexOf("zoom") != -1) {
                to.css({
                    position: "absolute",
                    top: from.prev().outerHeight(),
                    left: 0
                }).insertBefore(from);

                if (transitionOrigin) {
                    cell = that._cellByDate(that._view.toDateString(that._current));
                    position = cell.position();
                    position = (position.left + parseInt(cell.width() / 2)) + "px" + " " + (position.top + parseInt(cell.height() / 2) + "px");
                    to.css(transitionOrigin, position);
                }

                from.kendoStop(true, true).kendoAnimate({
                    effects: "fadeOut",
                    duration: 600,
                    complete: function() {
                        from.remove();
                        to.css({
                            position: "static",
                            top: 0,
                            left: 0
                        });
                    }
                });

                to.kendoStop(true, true).kendoAnimate(vertical);
            }
        },

        _cellByDate: function(value) {
            return this._table.find("td:not(." + OTHERMONTH + ")")
                       .filter(function() {
                           return $(this.firstChild).attr(kendo.attr(VALUE)) === value;
                       });
        },

        _class: function(className, value) {
            this._table.find("td:not(." + OTHERMONTH + ")")
                .removeClass(className)
                .filter(function() {
                   return $(this.firstChild).attr(kendo.attr(VALUE)) === value;
                })
                .addClass(className);
        },

        _click: function(e) {
            var that = this,
                options = that.options,
                currentValue = that._current,
                link = $(e.currentTarget.firstChild),
                value = link.attr(kendo.attr(VALUE)).split("/");

            //Safari cannot create corretly date from "1/1/2090"
            value = new DATE(value[0], value[1], value[2]);

            e.preventDefault();

            if (link.parent().hasClass(OTHERMONTH)) {
                currentValue = value;
            } else {
                that._view.setDate(currentValue, value);
            }

            that.navigateDown(restrictValue(currentValue, options.min, options.max));
        },

        _focus: function(value) {
            var that = this,
                view = that._view;

            if (view.compare(value, that._current) !== 0) {
                that.navigate(value);
            } else {
                that._current = value;
            }

            that._class("k-state-focused", view.toDateString(value));
        },

        _footer: function(template) {
            var that = this,
                element = that.element,
                today = new DATE(),
                footer = element.find(".k-footer"),
                link;

            if (!template) {
                that._toggle(false);
                footer.hide();
                return;
            }

            if (!footer[0]) {
                footer = $('<div class="k-footer"><a href="#" class="k-link k-nav-today"></a></div>').appendTo(element);
            }

            that._today = footer.show()
                                .find(".k-link")
                                .html(template(today))
                                .attr("title", kendo.toString(today, "D"));

            that._toggle();
        },

        _header: function() {
            var that = this,
            element = that.element,
            links;

            if (!element.find(".k-header")[0]) {
                element.html('<div class="k-header">'
                           + '<a href="#" class="k-link k-nav-prev"><span class="k-icon k-arrow-prev"></span></a>'
                           + '<a href="#" class="k-link k-nav-fast"></a>'
                           + '<a href="#" class="k-link k-nav-next"><span class="k-icon k-arrow-next"></span></a>'
                           + '</div>');
            }

            links = element.find(".k-link")
                           .bind(MOUSEENTER, mouseenter)
                           .bind(MOUSELEAVE, mouseleave)
                           .click(false);

            that._title = links.eq(1).bind(CLICK, proxy(that.navigateUp, that));
            that[PREVARROW] = links.eq(0).bind(CLICK, proxy(that.navigateToPast, that));
            that[NEXTARROW] = links.eq(2).bind(CLICK, proxy(that.navigateToFuture, that));
        },

        _navigate: function(arrow, modifier) {
            var that = this,
                index = that._index + 1,
                currentValue = new DATE(that._current);

            arrow = that[arrow];

            if (!arrow.hasClass(DISABLED)) {
                if (index > 3) {
                    currentValue.setFullYear(currentValue.getFullYear() + 100 * modifier);
                } else {
                    calendar.views[index].setDate(currentValue, modifier);
                }

                that.navigate(currentValue);
            }
        },

        _option: function(option, value) {
            var that = this,
                options = that.options,
                selectedValue = +that._value,
                bigger, navigate;

            if (value === undefined) {
                return options[option];
            }

            value = parse(value, options.format);

            if (!value) {
                return;
            }

            options[option] = new DATE(value);

            navigate = that._view.compare(value, that._current);

            if (option === MIN) {
                bigger = +value > selectedValue;
                navigate = navigate > -1
            } else {
                bigger = selectedValue > +value;
                navigate = navigate < 1;
            }

            if (bigger) {
                that.value(null);
            } else if (navigate) {
                that.navigate();
            }

            that._toggle();
        },

        _toggle: function(toggle) {
            var that = this,
                options = that.options,
                link = that._today;

            if (toggle === undefined) {
                toggle = isInRange(new DATE(), options.min, options.max);
            }

            if (link) {
                link.unbind(CLICK)

                if (toggle) {
                    link.addClass(TODAY)
                        .removeClass(DISABLED)
                        .bind(CLICK, proxy(that._todayClick, that));
                } else {
                    link.removeClass(TODAY)
                        .addClass(DISABLED)
                        .bind(CLICK, prevent);
                }
            }
        },

        _todayClick: function(e) {
            var that = this,
                depth = views[that.options.depth],
                today = new DATE();

            e.preventDefault();

            if (that._view.compare(that._current, today) === 0 && that._index == depth) {
                that._changeView = false;
            }

            that._value = today;
            that.navigate(today, depth);

            that.trigger(CHANGE);
        },

        _templates: function() {
            var that = this,
                options = that.options,
                footer = options.footer,
                month = options.month,
                content = month.content,
                empty = month.empty;

            that.month = {
                content: template('<td#=data.cssClass#><a class="k-link" href="\\#" ' + kendo.attr("value") + '="#=data.dateString#" title="#=data.title#">' + (content || "#=data.value#") + '</a></td>', { useWithBlock: !!content }),
                empty: template("<td>" + (empty || "&nbsp;") + "</td>", { useWithBlock: !!empty })
            };

            if (footer) {
                that.footer = template(footer, { useWithBlock: false });
            }
        }
    });

    ui.plugin(Calendar);

    var calendar = {
        firstDayOfMonth: function (date) {
            return new DATE(
                date.getFullYear(),
                date.getMonth(),
                1
            );
        },

        firstVisibleDay: function (date) {
            var firstDay = kendo.culture().calendar.firstDay,
            firstVisibleDay = new DATE(date.getFullYear(), date.getMonth(), 0, date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());

            while (firstVisibleDay.getDay() != firstDay) {
                calendar.setTime(firstVisibleDay, -1 * MS_PER_DAY)
            }

            return firstVisibleDay;
        },

        setTime: function (date, time) {
            var tzOffsetBefore = date.getTimezoneOffset(),
            resultDATE = new DATE(date.getTime() + time),
            tzOffsetDiff = resultDATE.getTimezoneOffset() - tzOffsetBefore;

            date.setTime(resultDATE.getTime() + tzOffsetDiff * MS_PER_MINUTE);
        },
        views: [{
            name: MONTH,
            title: function(date) {
                return kendo.culture().calendar.months.names[date.getMonth()] + " " + date.getFullYear();
            },
            content: function(options) {
                var that = this,
                idx = 0,
                min = options.min,
                max = options.max,
                date = options.date,
                currentCalendar = kendo.culture().calendar,
                firstDayIdx = currentCalendar.firstDay,
                days = currentCalendar.days,
                names = shiftArray(days.names, firstDayIdx),
                abbr = shiftArray(days.namesAbbr, firstDayIdx),
                short = shiftArray(days.namesShort, firstDayIdx),
                start = calendar.firstVisibleDay(date),
                firstDayOfMonth = that.first(date),
                lastDayOfMonth = that.last(date),
                toDateString = that.toDateString,
                today = new DATE(),
                html = '<table class="k-content" cellspacing="0"><thead><tr>';

                for (; idx < 7; idx++) {
                    html += '<th abbr="' + abbr[idx] + '" scope="col" title="' + names[idx] + '">' + short[idx] + '</th>';
                }

                today = +new DATE(today.getFullYear(), today.getMonth(), today.getDate());

                return view({
                    cells: 42,
                    perRow: 7,
                    html: html += "</tr></thead><tbody><tr>",
                    start: new DATE(start.getFullYear(), start.getMonth(), start.getDate()),
                    min: new DATE(min.getFullYear(), min.getMonth(), min.getDate()),
                    max: new DATE(max.getFullYear(), max.getMonth(), max.getDate()),
                    content: options.content,
                    empty: options.empty,
                    setter: that.setDate,
                    build: function(date, idx) {
                        var cssClass = [],
                        day = date.getDay();

                        if (date < firstDayOfMonth || date > lastDayOfMonth) {
                            cssClass.push(OTHERMONTH);
                        }

                        if (+date === today) {
                            cssClass.push("k-today");
                        }

                        if (day === 0 || day === 6) {
                            cssClass.push("k-weekend");
                        }

                        return {
                            date: date,
                            ns: kendo.ns,
                            title: kendo.toString(date, "D"),
                            value: date.getDate(),
                            dateString: toDateString(date),
                            cssClass: cssClass[0] ? ' class="' + cssClass.join(" ") + '"' : ""
                        };
                    }
                });
            },
            first: function(date) {
                return calendar.firstDayOfMonth(date);
            },
            last: function(date) {
                return new DATE(date.getFullYear(), date.getMonth() + 1, 0);
            },
            compare: function(date1, date2) {
                var result,
                month1 = date1.getMonth(),
                year1 = date1.getFullYear(),
                month2 = date2.getMonth(),
                year2 = date2.getFullYear();

                if (year1 > year2) {
                    result = 1;
                } else if (year1 < year2) {
                    result = -1;
                } else {
                    result = month1 == month2 ? 0 : month1 > month2 ? 1 : -1;
                }

                return result;
            },
            setDate: function(date, value) {
                if (value instanceof DATE) {
                    date.setFullYear(value.getFullYear(), value.getMonth(), value.getDate());
                } else {
                    calendar.setTime(date, value * MS_PER_DAY);
                }
            },
            toDateString: function(date) {
                return date.getFullYear() + "/" + date.getMonth() + "/" + date.getDate();
            }
        },
        {
            name: "year",
            title: function(date) {
                return date.getFullYear();
            },
            content: function(options) {
                var namesAbbr = kendo.culture().calendar.months.namesAbbr,
                toDateString = this.toDateString,
                min = options.min,
                max = options.max;

                return view({
                    min: new DATE(min.getFullYear(), min.getMonth(), 1),
                    max: new DATE(max.getFullYear(), max.getMonth(), 1),
                    start: new DATE(options.date.getFullYear(), 0, 1),
                    setter: this.setDate,
                    build: function(date) {
                        return {
                            value: namesAbbr[date.getMonth()],
                            ns: kendo.ns,
                            dateString: toDateString(date),
                            cssClass: ""
                        };
                    }
                });
            },
            first: function(date) {
                return new DATE(date.getFullYear(), 0, date.getDate());
            },
            last: function(date) {
                return new DATE(date.getFullYear(), 11, date.getDate());
            },
            compare: function(date1, date2){
                return compare(date1, date2);
            },
            setDate: function(date, value) {
                if (value instanceof DATE) {
                    date.setFullYear(value.getFullYear(),
                    value.getMonth(),
                    date.getDate());
                } else {
                    var month = date.getMonth() + value;

                    date.setMonth(month);

                    if (month > 11) {
                        month -= 12;
                    }

                    if (month > 0 && date.getMonth() != month) {
                        date.setDate(0);
                    }
                }
            },
            toDateString: function(date) {
                return date.getFullYear() + "/" + date.getMonth() + "/1";
            }
        },
        {
            name: "decade",
            title: function(date) {
                var start = date.getFullYear();

                start = start - start % 10;

                return start + "-" + (start + 9);
            },
            content: function(options) {
                var year = options.date.getFullYear(),
                toDateString = this.toDateString;

                return view({
                    start: new DATE(year - year % 10 - 1, 0, 1),
                    min: new DATE(options.min.getFullYear(), 0, 1),
                    max: new DATE(options.max.getFullYear(), 0, 1),
                    setter: this.setDate,
                    build: function(date, idx) {
                        return {
                            value: date.getFullYear(),
                            ns: kendo.ns,
                            dateString: toDateString(date),
                            cssClass: idx == 0 || idx == 11 ? OTHERMONTHCLASS : ""
                        };
                    }
                });
            },
            first: function(date) {
                var year = date.getFullYear();
                return new DATE(year - year % 10, date.getMonth(), date.getDate());
            },
            last: function(date) {
                var year = date.getFullYear();
                return new DATE(year - year % 10 + 9, date.getMonth(), date.getDate());
            },
            compare: function(date1, date2) {
                return compare(date1, date2, 10);
            },
            setDate: function(date, value) {
                setDate(date, value, 1);
            },
            toDateString: function(date) {
                return date.getFullYear() + "/0/1";
            }
        },
        {
            name: CENTURY,
            title: function(date) {
                var start = date.getFullYear();

                start = start - start % 100;

                return start + "-" + (start + 99);
            },
            content: function(options) {
                var year = options.date.getFullYear(),
                minYear = options.min.getFullYear(),
                maxYear = options.max.getFullYear(),
                toDateString = this.toDateString;

                minYear = minYear - minYear % 10;
                maxYear = maxYear - maxYear % 10;

                if (maxYear - minYear < 10) {
                    maxYear = minYear + 9;
                }

                return view({
                    start: new DATE(year - year % 100 - 10, 0, 1),
                    min: new DATE(minYear, 0, 1),
                    max: new DATE(maxYear, 0, 1),
                    setter: this.setDate,
                    build: function(date, idx) {
                        var year = date.getFullYear();
                        return {
                            value: year + " - " + (year + 9),
                            ns: kendo.ns,
                            dateString: toDateString(date),
                            cssClass: idx == 0 || idx == 11 ? OTHERMONTHCLASS : ""
                        };
                    }
                });
            },
            first: function(date) {
                var year = date.getFullYear();
                return new DATE(year - year % 100, date.getMonth(), date.getDate());
            },
            last: function(date) {
                var year = date.getFullYear();
                return new DATE(year - year % 100 + 99, date.getMonth(), date.getDate());
            },
            compare: function(date1, date2) {
                return compare(date1, date2, 100);
            },
            setDate: function(date, value) {
                setDate(date, value, 10);
            },
            toDateString: function(date) {
                var year = date.getFullYear();
                return (year - year % 10) + "/0/1";
            }
        }]
    }

    function view(options) {
        var idx = 0,
            data,
            view = options.view,
            min = options.min,
            max = options.max,
            start = options.start,
            setter = options.setter,
            build = options.build,
            length = options.cells || 12,
            cellsPerRow = options.perRow || 4,
            toDateString = options.toDateString,
            content = options.content || cellTemplate,
            empty = options.empty || emptyCellTemplate,
            html = options.html || '<table class="k-content k-meta-view" cellspacing="0"><tbody><tr>';

        for(; idx < length; idx++) {
            if (idx > 0 && idx % cellsPerRow == 0) {
                html += "</tr><tr>";
            }

            data = build(start, idx);

            html += isInRange(start, min, max) ? content(data) : empty(data);

            setter(start, 1);
        }

        return html + "</tr></tbody></table>";
    }

    function compare(date1, date2, modifier) {
        var year1 = date1.getFullYear(),
            start  = date2.getFullYear(),
            end = start,
            result = 0;

        if (modifier) {
            start = start - start % modifier;
            end = start - start % modifier + modifier - 1;
        }

        if (year1 > end) {
            result = 1;
        } else if (year1 < start) {
            result = -1;
        }

        return result;
    }

    function restrictValue (value, min, max) {
        var today = new DATE();

        today = new DATE(today.getFullYear(), today.getMonth(), today.getDate());

        if (value) {
            today = new DATE(value);
        }

        if (min > today) {
            today = new DATE(min);
        } else if (max < today) {
            today = new DATE(max);
        }
        return today;
    }

    function isInRange(date, min, max) {
        return +date >= +min && +date <= +max;
    }

    function shiftArray(array, idx) {
        return array.slice(idx).concat(array.slice(0, idx));
    }

    function setDate(date, value, multiplier) {
        value = value instanceof DATE ? value.getFullYear() : date.getFullYear() + multiplier * value;
        date.setFullYear(value);
    }

    function mouseenter() {
        $(this).addClass(HOVER);
    }

    function mouseleave() {
        $(this).removeClass(HOVER);
    }

    function prevent (e) {
        e.preventDefault();
    }

    function validate(options) {
        var start = views[options.start],
            depth = views[options.depth],
            format = options.format || kendo.culture().calendar.patterns.d;

        if (isNaN(start)) {
            start = 0;
            options.start = MONTH;
        }

        if (depth === undefined || depth > start) {
            options.depth = MONTH;
        }

        if (format.slice(0,3) === "{0:") {
            format = format.slice(3, format.length - 1);
        }

        options.format = format;
    }

    function makeUnselectable(element) {
        if (isIE8) {
            element.find("*").attr("unselectable", "on");
        }
    }

    calendar.makeUnselectable =  makeUnselectable;
    calendar.restrictValue = restrictValue;
    calendar.isInRange = isInRange;
    calendar.validate = validate;
    calendar.viewsEnum = views;

    kendo.calendar = calendar;
})(jQuery);
