/*
 * jQuery UI Datepicker @VERSION
 *
 * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Datepicker
 *
 * Depends:
 *    jquery.ui.core.js
 */

(function(jQuery) { // hide the namespace

jQuery.extend(jQuery.ui, { datepicker: { version: "@VERSION" } });

var PROP_NAME = 'datepicker';
var dpuuid = new Date().getTime();

/* Date picker manager.
   Use the singleton instance of this class, jQuery.datepicker, to interact with the date picker.
   Settings for (groups of) date pickers are maintained in an instance object,
   allowing multiple different settings on the same page. */

function Datepicker() {
    this.debug = false; // Change this to true to start debugging
    this._curInst = null; // The current instance in use
    this._keyEvent = false; // If the last event was a key event
    this._disabledInputs = []; // List of date picker inputs that have been disabled
    this._datepickerShowing = false; // True if the popup picker is showing , false if not
    this._inDialog = false; // True if showing within a "dialog", false if not
    this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division
    this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class
    this._appendClass = 'ui-datepicker-append'; // The name of the append marker class
    this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class
    this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class
    this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class
    this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class
    this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class
    this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class
    this.regional = []; // Available regional settings, indexed by language code
    this.regional[''] = { // Default regional settings
        clearText: 'Очистить', clearStatus: '',
        closeText: 'Закрыть', closeStatus: '',
        prevText: '&lt;Пред',  prevStatus: '',
        nextText: 'След&gt;', nextStatus: '',
        currentText: 'Сегодня', currentStatus: '',
        monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь',
        'Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
        monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн',
        'Июл','Авг','Сен','Окт','Ноя','Дек'],
        monthStatus: '', yearStatus: '',
        weekHeader: 'Не', weekStatus: '',
        dayNames: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'],
        dayNamesShort: ['вс','пн','вт','ср','чт','пт','сб'],
        dayNamesMin: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'],
        dayStatus: 'DD', dateStatus: 'D, M d',
        dateFormat: 'dd.mm.yy', firstDay: 1, 
        isRTL: false, // True if right-to-left language, false if left-to-right
        showMonthAfterYear: false, // True if the year select precedes month, false for month then year
        yearSuffix: '' // Additional text to append to the year in the month headers
    }; 
   

    this._defaults = { // Global defaults for all the date picker instances
        showOn: 'focus', // 'focus' for popup on focus,
            // 'button' for trigger button, or 'both' for either
        showAnim: 'show', // Name of jQuery animation for popup
        showOptions: {}, // Options for enhanced animations
        defaultDate: null, // Used when field is blank: actual date,
            // +/-number for offset from today, null for today
        appendText: '', // Display text following the input box, e.g. showing the format
        buttonText: '...', // Text for trigger button
        buttonImage: '', // URL for trigger button image
        buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
        hideIfNoPrevNext: false, // True to hide next/previous month links
            // if not applicable, false to just disable them
        navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
        gotoCurrent: false, // True if today link goes back to current selection instead
        changeMonth: false, // True if month can be selected directly, false if only prev/next
        changeYear: false, // True if year can be selected directly, false if only prev/next
        yearRange: 'c-10:c+10', // Range of years to display in drop-down,
            // either relative to today's year (-nn:+nn), relative to currently displayed year
            // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
        showOtherMonths: false, // True to show dates in other months, false to leave blank
        selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
        showWeek: false, // True to show week of the year, false to not show it
        calculateWeek: this.iso8601Week, // How to calculate the week of the year,
            // takes a Date and returns the number of the week for it
        shortYearCutoff: '+10', // Short year values < this are in the current century,
            // > this are in the previous century,
            // string value starting with '+' for current year + value
        minDate: null, // The earliest selectable date, or null for no limit
        maxDate: null, // The latest selectable date, or null for no limit
        duration: '_default', // Duration of display/closure
        beforeShowDay: null, // Function that takes a date and returns an array with
            // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '',
            // [2] = cell title (optional), e.g. jQuery.datepicker.noWeekends
        beforeShow: null, // Function that takes an input field and
            // returns a set of custom settings for the date picker
        onSelect: null, // Define a callback function when a date is selected
        onChangeMonthYear: null, // Define a callback function when the month or year is changed
        onClose: null, // Define a callback function when the datepicker is closed
        numberOfMonths: 1, // Number of months to show at a time
        showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
        stepMonths: 1, // Number of months to step back/forward
        stepBigMonths: 12, // Number of months to step back/forward for the big links
        altField: '', // Selector for an alternate field to store selected dates into
        altFormat: '', // The date format to use for the alternate field
        constrainInput: true, // The input is constrained by the current date format
        showButtonPanel: false, // True to show button panel, false to not show it
        autoSize: false // True to size the input for the date format, false to leave as is
    };
    jQuery.extend(this._defaults, this.regional['']);
    this.dpDiv = jQuery('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all ui-helper-hidden-accessible"></div>');
}

jQuery.extend(Datepicker.prototype, {
    /* Class name added to elements to indicate already configured with a date picker. */
    markerClassName: 'hasDatepicker',

    /* Debug logging (if enabled). */
    log: function () {
        if (this.debug)
            console.log.apply('', arguments);
    },
    
    // TODO rename to "widget" when switching to widget factory
    _widgetDatepicker: function() {
        return this.dpDiv;
    },

    /* Override the default settings for all instances of the date picker.
       @param  settings  object - the new settings to use as defaults (anonymous object)
       @return the manager object */
    setDefaults: function(settings) {
        extendRemove(this._defaults, settings || {});
        return this;
    },

    /* Attach the date picker to a jQuery selection.
       @param  target    element - the target input field or division or span
       @param  settings  object - the new settings to use for this date picker instance (anonymous) */
    _attachDatepicker: function(target, settings) {
        // check for settings on the control itself - in namespace 'date:'
        var inlineSettings = null;
        for (var attrName in this._defaults) {
            var attrValue = target.getAttribute('date:' + attrName);
            if (attrValue) {
                inlineSettings = inlineSettings || {};
                try {
                    inlineSettings[attrName] = eval(attrValue);
                } catch (err) {
                    inlineSettings[attrName] = attrValue;
                }
            }
        }
        var nodeName = target.nodeName.toLowerCase();
        var inline = (nodeName == 'div' || nodeName == 'span');
        if (!target.id)
            target.id = 'dp' + (++this.uuid);
        var inst = this._newInst(jQuery(target), inline);
        inst.settings = jQuery.extend({}, settings || {}, inlineSettings || {});
        if (nodeName == 'input') {
            this._connectDatepicker(target, inst);
        } else if (inline) {
            this._inlineDatepicker(target, inst);
        }
    },

    /* Create a new instance object. */
    _newInst: function(target, inline) {
        var id = target[0].id.replace(/([^A-Za-z0-9_])/g, '\\\\jQuery1'); // escape jQuery meta chars
        return {id: id, input: target, // associated target
            selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
            drawMonth: 0, drawYear: 0, // month being drawn
            inline: inline, // is datepicker inline or not
            dpDiv: (!inline ? this.dpDiv : // presentation div
            jQuery('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))};
    },

    /* Attach the date picker to an input field. */
    _connectDatepicker: function(target, inst) {
        var input = jQuery(target);
        inst.append = jQuery([]);
        inst.trigger = jQuery([]);
        if (input.hasClass(this.markerClassName))
            return;
        this._attachments(input, inst);
        input.addClass(this.markerClassName).keydown(this._doKeyDown).
            keypress(this._doKeyPress).keyup(this._doKeyUp).
            bind("setData.datepicker", function(event, key, value) {
                inst.settings[key] = value;
            }).bind("getData.datepicker", function(event, key) {
                return this._get(inst, key);
            });
        this._autoSize(inst);
        jQuery.data(target, PROP_NAME, inst);
    },

    /* Make attachments based on settings. */
    _attachments: function(input, inst) {
        var appendText = this._get(inst, 'appendText');
        var isRTL = this._get(inst, 'isRTL');
        if (inst.append)
            inst.append.remove();
        if (appendText) {
            inst.append = jQuery('<span class="' + this._appendClass + '">' + appendText + '</span>');
            input[isRTL ? 'before' : 'after'](inst.append);
        }
        input.unbind('focus', this._showDatepicker);
        if (inst.trigger)
            inst.trigger.remove();
        var showOn = this._get(inst, 'showOn');
        if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
            input.focus(this._showDatepicker);
        if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
            var buttonText = this._get(inst, 'buttonText');
            var buttonImage = this._get(inst, 'buttonImage');
            inst.trigger = jQuery(this._get(inst, 'buttonImageOnly') ?
                jQuery('<img/>').addClass(this._triggerClass).
                    attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
                jQuery('<button type="button"></button>').addClass(this._triggerClass).
                    html(buttonImage == '' ? buttonText : jQuery('<img/>').attr(
                    { src:buttonImage, alt:buttonText, title:buttonText })));
            input[isRTL ? 'before' : 'after'](inst.trigger);
            inst.trigger.click(function() {
                if (jQuery.datepicker._datepickerShowing && jQuery.datepicker._lastInput == input[0])
                    jQuery.datepicker._hideDatepicker();
                else
                    jQuery.datepicker._showDatepicker(input[0]);
                return false;
            });
        }
    },

    /* Apply the maximum length for the date format. */
    _autoSize: function(inst) {
        if (this._get(inst, 'autoSize') && !inst.inline) {
            var date = new Date(2009, 12 - 1, 20); // Ensure double digits
            var dateFormat = this._get(inst, 'dateFormat');
            if (dateFormat.match(/[DM]/)) {
                var findMax = function(names) {
                    var max = 0;
                    var maxI = 0;
                    for (var i = 0; i < names.length; i++) {
                        if (names[i].length > max) {
                            max = names[i].length;
                            maxI = i;
                        }
                    }
                    return maxI;
                };
                date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
                    'monthNames' : 'monthNamesShort'))));
                date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
                    'dayNames' : 'dayNamesShort'))) + 20 - date.getDay());
            }
            inst.input.attr('size', this._formatDate(inst, date).length);
        }
    },

    /* Attach an inline date picker to a div. */
    _inlineDatepicker: function(target, inst) {
        var divSpan = jQuery(target);
        if (divSpan.hasClass(this.markerClassName))
            return;
        divSpan.addClass(this.markerClassName).append(inst.dpDiv).
            bind("setData.datepicker", function(event, key, value){
                inst.settings[key] = value;
            }).bind("getData.datepicker", function(event, key){
                return this._get(inst, key);
            });
        jQuery.data(target, PROP_NAME, inst);
        this._setDate(inst, this._getDefaultDate(inst), true);
        this._updateDatepicker(inst);
        this._updateAlternate(inst);
    },

    /* Pop-up the date picker in a "dialog" box.
       @param  input     element - ignored
       @param  date      string or Date - the initial date to display
       @param  onSelect  function - the function to call when a date is selected
       @param  settings  object - update the dialog date picker instance's settings (anonymous object)
       @param  pos       int[2] - coordinates for the dialog's position within the screen or
                         event - with x/y coordinates or
                         leave empty for default (screen centre)
       @return the manager object */
    _dialogDatepicker: function(input, date, onSelect, settings, pos) {
        var inst = this._dialogInst; // internal instance
        if (!inst) {
            var id = 'dp' + (++this.uuid);
            this._dialogInput = jQuery('<input type="text" id="' + id +
                '" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');
            this._dialogInput.keydown(this._doKeyDown);
            jQuery('body').append(this._dialogInput);
            inst = this._dialogInst = this._newInst(this._dialogInput, false);
            inst.settings = {};
            jQuery.data(this._dialogInput[0], PROP_NAME, inst);
        }
        extendRemove(inst.settings, settings || {});
        date = (date && date.constructor == Date ? this._formatDate(inst, date) : date);
        this._dialogInput.val(date);

        this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
        if (!this._pos) {
            var browserWidth = document.documentElement.clientWidth;
            var browserHeight = document.documentElement.clientHeight;
            var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
            var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
            this._pos = // should use actual width/height below
                [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
        }

        // move input on screen for focus, but hidden behind dialog
        this._dialogInput.css('left', (this._pos[0] + 20) + 'px').css('top', this._pos[1] + 'px');
        inst.settings.onSelect = onSelect;
        this._inDialog = true;
        this.dpDiv.addClass(this._dialogClass);
        this._showDatepicker(this._dialogInput[0]);
        if (jQuery.blockUI)
            jQuery.blockUI(this.dpDiv);
        jQuery.data(this._dialogInput[0], PROP_NAME, inst);
        return this;
    },

    /* Detach a datepicker from its control.
       @param  target    element - the target input field or division or span */
    _destroyDatepicker: function(target) {
        var jQuerytarget = jQuery(target);
        var inst = jQuery.data(target, PROP_NAME);
        if (!jQuerytarget.hasClass(this.markerClassName)) {
            return;
        }
        var nodeName = target.nodeName.toLowerCase();
        jQuery.removeData(target, PROP_NAME);
        if (nodeName == 'input') {
            inst.append.remove();
            inst.trigger.remove();
            jQuerytarget.removeClass(this.markerClassName).
                unbind('focus', this._showDatepicker).
                unbind('keydown', this._doKeyDown).
                unbind('keypress', this._doKeyPress).
                unbind('keyup', this._doKeyUp);
        } else if (nodeName == 'div' || nodeName == 'span')
            jQuerytarget.removeClass(this.markerClassName).empty();
    },

    /* Enable the date picker to a jQuery selection.
       @param  target    element - the target input field or division or span */
    _enableDatepicker: function(target) {
        var jQuerytarget = jQuery(target);
        var inst = jQuery.data(target, PROP_NAME);
        if (!jQuerytarget.hasClass(this.markerClassName)) {
            return;
        }
        var nodeName = target.nodeName.toLowerCase();
        if (nodeName == 'input') {
            target.disabled = false;
            inst.trigger.filter('button').
                each(function() { this.disabled = false; }).end().
                filter('img').css({opacity: '1.0', cursor: ''});
        }
        else if (nodeName == 'div' || nodeName == 'span') {
            var inline = jQuerytarget.children('.' + this._inlineClass);
            inline.children().removeClass('ui-state-disabled');
        }
        this._disabledInputs = jQuery.map(this._disabledInputs,
            function(value) { return (value == target ? null : value); }); // delete entry
    },

    /* Disable the date picker to a jQuery selection.
       @param  target    element - the target input field or division or span */
    _disableDatepicker: function(target) {
        var jQuerytarget = jQuery(target);
        var inst = jQuery.data(target, PROP_NAME);
        if (!jQuerytarget.hasClass(this.markerClassName)) {
            return;
        }
        var nodeName = target.nodeName.toLowerCase();
        if (nodeName == 'input') {
            target.disabled = true;
            inst.trigger.filter('button').
                each(function() { this.disabled = true; }).end().
                filter('img').css({opacity: '0.5', cursor: 'default'});
        }
        else if (nodeName == 'div' || nodeName == 'span') {
            var inline = jQuerytarget.children('.' + this._inlineClass);
            inline.children().addClass('ui-state-disabled');
        }
        this._disabledInputs = jQuery.map(this._disabledInputs,
            function(value) { return (value == target ? null : value); }); // delete entry
        this._disabledInputs[this._disabledInputs.length] = target;
    },

    /* Is the first field in a jQuery collection disabled as a datepicker?
       @param  target    element - the target input field or division or span
       @return boolean - true if disabled, false if enabled */
    _isDisabledDatepicker: function(target) {
        if (!target) {
            return false;
        }
        for (var i = 0; i < this._disabledInputs.length; i++) {
            if (this._disabledInputs[i] == target)
                return true;
        }
        return false;
    },

    /* Retrieve the instance data for the target control.
       @param  target  element - the target input field or division or span
       @return  object - the associated instance data
       @throws  error if a jQuery problem getting data */
    _getInst: function(target) {
        try {
            return jQuery.data(target, PROP_NAME);
        }
        catch (err) {
            throw 'Missing instance data for this datepicker';
        }
    },

    /* Update or retrieve the settings for a date picker attached to an input field or division.
       @param  target  element - the target input field or division or span
       @param  name    object - the new settings to update or
                       string - the name of the setting to change or retrieve,
                       when retrieving also 'all' for all instance settings or
                       'defaults' for all global defaults
       @param  value   any - the new value for the setting
                       (omit if above is an object or to retrieve a value) */
    _optionDatepicker: function(target, name, value) {
        var inst = this._getInst(target);
        if (arguments.length == 2 && typeof name == 'string') {
            return (name == 'defaults' ? jQuery.extend({}, jQuery.datepicker._defaults) :
                (inst ? (name == 'all' ? jQuery.extend({}, inst.settings) :
                this._get(inst, name)) : null));
        }
        var settings = name || {};
        if (typeof name == 'string') {
            settings = {};
            settings[name] = value;
        }
        if (inst) {
            if (this._curInst == inst) {
                this._hideDatepicker();
            }
            var date = this._getDateDatepicker(target, true);
            extendRemove(inst.settings, settings);
            this._attachments(jQuery(target), inst);
            this._autoSize(inst);
            this._setDateDatepicker(target, date);
            this._updateDatepicker(inst);
        }
    },

    // change method deprecated
    _changeDatepicker: function(target, name, value) {
        this._optionDatepicker(target, name, value);
    },

    /* Redraw the date picker attached to an input field or division.
       @param  target  element - the target input field or division or span */
    _refreshDatepicker: function(target) {
        var inst = this._getInst(target);
        if (inst) {
            this._updateDatepicker(inst);
        }
    },

    /* Set the dates for a jQuery selection.
       @param  target   element - the target input field or division or span
       @param  date     Date - the new date */
    _setDateDatepicker: function(target, date) {
        var inst = this._getInst(target);
        if (inst) {
            this._setDate(inst, date);
            this._updateDatepicker(inst);
            this._updateAlternate(inst);
        }
    },

    /* Get the date(s) for the first entry in a jQuery selection.
       @param  target     element - the target input field or division or span
       @param  noDefault  boolean - true if no default date is to be used
       @return Date - the current date */
    _getDateDatepicker: function(target, noDefault) {
        var inst = this._getInst(target);
        if (inst && !inst.inline)
            this._setDateFromField(inst, noDefault);
        return (inst ? this._getDate(inst) : null);
    },

    /* Handle keystrokes. */
    _doKeyDown: function(event) {
        var inst = jQuery.datepicker._getInst(event.target);
        var handled = true;
        var isRTL = inst.dpDiv.is('.ui-datepicker-rtl');
        inst._keyEvent = true;
        if (jQuery.datepicker._datepickerShowing)
            switch (event.keyCode) {
                case 9: jQuery.datepicker._hideDatepicker();
                        handled = false;
                        break; // hide on tab out
                case 13: var sel = jQuery('td.' + jQuery.datepicker._dayOverClass, inst.dpDiv).
                            add(jQuery('td.' + jQuery.datepicker._currentClass, inst.dpDiv));
                        if (sel[0])
                            jQuery.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
                        else
                            jQuery.datepicker._hideDatepicker();
                        return false; // don't submit the form
                        break; // select the value on enter
                case 27: jQuery.datepicker._hideDatepicker();
                        break; // hide on escape
                case 33: jQuery.datepicker._adjustDate(event.target, (event.ctrlKey ?
                            -jQuery.datepicker._get(inst, 'stepBigMonths') :
                            -jQuery.datepicker._get(inst, 'stepMonths')), 'M');
                        break; // previous month/year on page up/+ ctrl
                case 34: jQuery.datepicker._adjustDate(event.target, (event.ctrlKey ?
                            +jQuery.datepicker._get(inst, 'stepBigMonths') :
                            +jQuery.datepicker._get(inst, 'stepMonths')), 'M');
                        break; // next month/year on page down/+ ctrl
                case 35: if (event.ctrlKey || event.metaKey) jQuery.datepicker._clearDate(event.target);
                        handled = event.ctrlKey || event.metaKey;
                        break; // clear on ctrl or command +end
                case 36: if (event.ctrlKey || event.metaKey) jQuery.datepicker._gotoToday(event.target);
                        handled = event.ctrlKey || event.metaKey;
                        break; // current on ctrl or command +home
                case 37: if (event.ctrlKey || event.metaKey) jQuery.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D');
                        handled = event.ctrlKey || event.metaKey;
                        // -1 day on ctrl or command +left
                        if (event.originalEvent.altKey) jQuery.datepicker._adjustDate(event.target, (event.ctrlKey ?
                                    -jQuery.datepicker._get(inst, 'stepBigMonths') :
                                    -jQuery.datepicker._get(inst, 'stepMonths')), 'M');
                        // next month/year on alt +left on Mac
                        break;
                case 38: if (event.ctrlKey || event.metaKey) jQuery.datepicker._adjustDate(event.target, -7, 'D');
                        handled = event.ctrlKey || event.metaKey;
                        break; // -1 week on ctrl or command +up
                case 39: if (event.ctrlKey || event.metaKey) jQuery.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D');
                        handled = event.ctrlKey || event.metaKey;
                        // +1 day on ctrl or command +right
                        if (event.originalEvent.altKey) jQuery.datepicker._adjustDate(event.target, (event.ctrlKey ?
                                    +jQuery.datepicker._get(inst, 'stepBigMonths') :
                                    +jQuery.datepicker._get(inst, 'stepMonths')), 'M');
                        // next month/year on alt +right
                        break;
                case 40: if (event.ctrlKey || event.metaKey) jQuery.datepicker._adjustDate(event.target, +7, 'D');
                        handled = event.ctrlKey || event.metaKey;
                        break; // +1 week on ctrl or command +down
                default: handled = false;
            }
        else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home
            jQuery.datepicker._showDatepicker(this);
        else {
            handled = false;
        }
        if (handled) {
            event.preventDefault();
            event.stopPropagation();
        }
    },

    /* Filter entered characters - based on date format. */
    _doKeyPress: function(event) {
        var inst = jQuery.datepicker._getInst(event.target);
        if (jQuery.datepicker._get(inst, 'constrainInput')) {
            var chars = jQuery.datepicker._possibleChars(jQuery.datepicker._get(inst, 'dateFormat'));
            var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
            return event.ctrlKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
        }
    },

    /* Synchronise manual entry and field/alternate field. */
    _doKeyUp: function(event) {
        var inst = jQuery.datepicker._getInst(event.target);
        if (inst.input.val() != inst.lastVal) {
            try {
                var date = jQuery.datepicker.parseDate(jQuery.datepicker._get(inst, 'dateFormat'),
                    (inst.input ? inst.input.val() : null),
                    jQuery.datepicker._getFormatConfig(inst));
                if (date) { // only if valid
                    jQuery.datepicker._setDateFromField(inst);
                    jQuery.datepicker._updateAlternate(inst);
                    jQuery.datepicker._updateDatepicker(inst);
                }
            }
            catch (event) {
                jQuery.datepicker.log(event);
            }
        }
        return true;
    },

    /* Pop-up the date picker for a given input field.
       @param  input  element - the input field attached to the date picker or
                      event - if triggered by focus */
    _showDatepicker: function(input) {
        input = input.target || input;
        if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
            input = jQuery('input', input.parentNode)[0];
        if (jQuery.datepicker._isDisabledDatepicker(input) || jQuery.datepicker._lastInput == input) // already here
            return;
        var inst = jQuery.datepicker._getInst(input);
        if (jQuery.datepicker._curInst && jQuery.datepicker._curInst != inst) {
            jQuery.datepicker._curInst.dpDiv.stop(true, true);
        }
        var beforeShow = jQuery.datepicker._get(inst, 'beforeShow');
        extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
        inst.lastVal = null;
        jQuery.datepicker._lastInput = input;
        jQuery.datepicker._setDateFromField(inst);
        if (jQuery.datepicker._inDialog) // hide cursor
            input.value = '';
        if (!jQuery.datepicker._pos) { // position below input
            jQuery.datepicker._pos = jQuery.datepicker._findPos(input);
            jQuery.datepicker._pos[1] += input.offsetHeight; // add the height
        }
        var isFixed = false;
        jQuery(input).parents().each(function() {
            isFixed |= jQuery(this).css('position') == 'fixed';
            return !isFixed;
        });
        if (isFixed && jQuery.browser.opera) { // correction for Opera when fixed and scrolled
            jQuery.datepicker._pos[0] -= document.documentElement.scrollLeft;
            jQuery.datepicker._pos[1] -= document.documentElement.scrollTop;
        }
        var offset = {left: jQuery.datepicker._pos[0], top: jQuery.datepicker._pos[1]};
        jQuery.datepicker._pos = null;
        // determine sizing offscreen
        inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
        jQuery.datepicker._updateDatepicker(inst);
        // fix width for dynamic number of date pickers
        // and adjust position before showing
        offset = jQuery.datepicker._checkOffset(inst, offset, isFixed);
        inst.dpDiv.css({position: (jQuery.datepicker._inDialog && jQuery.blockUI ?
            'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
            left: offset.left + 'px', top: offset.top + 'px'});
        if (!inst.inline) {
            var showAnim = jQuery.datepicker._get(inst, 'showAnim');
            var duration = jQuery.datepicker._get(inst, 'duration');
            var postProcess = function() {
                jQuery.datepicker._datepickerShowing = true;
                var borders = jQuery.datepicker._getBorders(inst.dpDiv);
                inst.dpDiv.find('iframe.ui-datepicker-cover'). // IE6- only
                    css({left: -borders[0], top: -borders[1],
                        width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()});
            };
//            inst.dpDiv.zIndex(jQuery(input).zIndex()+1);
            if (jQuery.effects && jQuery.effects[showAnim])
                inst.dpDiv.show(showAnim, jQuery.datepicker._get(inst, 'showOptions'), duration, postProcess);
            else
                inst.dpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess);
            if (!showAnim || !duration)
                postProcess();
            if (inst.input.is(':visible') && !inst.input.is(':disabled'))
                inst.input.focus();
            jQuery.datepicker._curInst = inst;
        }
    },

    /* Generate the date picker content. */
    _updateDatepicker: function(inst) {
        var self = this;
        var borders = jQuery.datepicker._getBorders(inst.dpDiv);
        inst.dpDiv.empty().append(this._generateHTML(inst))
            .find('iframe.ui-datepicker-cover') // IE6- only
                .css({left: -borders[0], top: -borders[1],
                    width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()})
            .end()
            .find('button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a')
                .bind('mouseout', function(){
                    jQuery(this).removeClass('ui-state-hover');
                    if(this.className.indexOf('ui-datepicker-prev') != -1) jQuery(this).removeClass('ui-datepicker-prev-hover');
                    if(this.className.indexOf('ui-datepicker-next') != -1) jQuery(this).removeClass('ui-datepicker-next-hover');
                })
                .bind('mouseover', function(){
                    if (!self._isDisabledDatepicker( inst.inline ? inst.dpDiv.parent()[0] : inst.input[0])) {
                        jQuery(this).parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
                        jQuery(this).addClass('ui-state-hover');
                        if(this.className.indexOf('ui-datepicker-prev') != -1) jQuery(this).addClass('ui-datepicker-prev-hover');
                        if(this.className.indexOf('ui-datepicker-next') != -1) jQuery(this).addClass('ui-datepicker-next-hover');
                    }
                })
            .end()
            .find('.' + this._dayOverClass + ' a')
                .trigger('mouseover')
            .end();
        var numMonths = this._getNumberOfMonths(inst);
        var cols = numMonths[1];
        var width = 17;
        if (cols > 1)
            inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em');
        else
            inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width('');
        inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
            'Class']('ui-datepicker-multi');
        inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
            'Class']('ui-datepicker-rtl');
        if (inst == jQuery.datepicker._curInst && jQuery.datepicker._datepickerShowing && inst.input &&
                inst.input.is(':visible') && !inst.input.is(':disabled'))
            inst.input.focus();
    },

    /* Retrieve the size of left and top borders for an element.
       @param  elem  (jQuery object) the element of interest
       @return  (number[2]) the left and top borders */
    _getBorders: function(elem) {
        var convert = function(value) {
            return {thin: 1, medium: 2, thick: 3}[value] || value;
        };
        return [parseFloat(convert(elem.css('border-left-width'))),
            parseFloat(convert(elem.css('border-top-width')))];
    },

    /* Check positioning to remain on screen. */
    _checkOffset: function(inst, offset, isFixed) {
        var dpWidth = inst.dpDiv.outerWidth();
        var dpHeight = inst.dpDiv.outerHeight();
        var inputWidth = inst.input ? inst.input.outerWidth() : 0;
        var inputHeight = inst.input ? inst.input.outerHeight() : 0;
        var viewWidth = document.documentElement.clientWidth + jQuery(document).scrollLeft();
        var viewHeight = document.documentElement.clientHeight + jQuery(document).scrollTop();

        offset.left -= (this._get(inst, 'isRTL') ? (dpWidth - inputWidth) : 0);
        offset.left -= (isFixed && offset.left == inst.input.offset().left) ? jQuery(document).scrollLeft() : 0;
        offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? jQuery(document).scrollTop() : 0;

        // now check if datepicker is showing outside window viewport - move to a better place if so.
        offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
            Math.abs(offset.left + dpWidth - viewWidth) : 0);
        offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
            Math.abs(dpHeight + inputHeight) : 0);

        return offset;
    },

    /* Find an object's position on the screen. */
    _findPos: function(obj) {
        var inst = this._getInst(obj);
        var isRTL = this._get(inst, 'isRTL');
        while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
            obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
        }
        var position = jQuery(obj).offset();
        return [position.left, position.top];
    },

    /* Hide the date picker from view.
       @param  input  element - the input field attached to the date picker */
    _hideDatepicker: function(input) {
        var inst = this._curInst;
        if (!inst || (input && inst != jQuery.data(input, PROP_NAME)))
            return;
        if (this._datepickerShowing) {
            var showAnim = this._get(inst, 'showAnim');
            var duration = this._get(inst, 'duration');
            var postProcess = function() {
                jQuery.datepicker._tidyDialog(inst);
                this._curInst = null;
            };
            if (jQuery.effects && jQuery.effects[showAnim])
                inst.dpDiv.hide(showAnim, jQuery.datepicker._get(inst, 'showOptions'), duration, postProcess);
            else
                inst.dpDiv[(showAnim == 'slideDown' ? 'slideUp' :
                    (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
            if (!showAnim)
                postProcess();
            var onClose = this._get(inst, 'onClose');
            if (onClose)
                onClose.apply((inst.input ? inst.input[0] : null),
                    [(inst.input ? inst.input.val() : ''), inst]);  // trigger custom callback
            this._datepickerShowing = false;
            this._lastInput = null;
            if (this._inDialog) {
                this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
                if (jQuery.blockUI) {
                    jQuery.unblockUI();
                    jQuery('body').append(this.dpDiv);
                }
            }
            this._inDialog = false;
        }
    },

    /* Tidy up after a dialog display. */
    _tidyDialog: function(inst) {
        inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker-calendar');
    },

    /* Close date picker if clicked elsewhere. */
    _checkExternalClick: function(event) {
        if (!jQuery.datepicker._curInst)
            return;
        var jQuerytarget = jQuery(event.target);
        if (jQuerytarget[0].id != jQuery.datepicker._mainDivId &&
                jQuerytarget.parents('#' + jQuery.datepicker._mainDivId).length == 0 &&
                !jQuerytarget.hasClass(jQuery.datepicker.markerClassName) &&
                !jQuerytarget.hasClass(jQuery.datepicker._triggerClass) &&
                jQuery.datepicker._datepickerShowing && !(jQuery.datepicker._inDialog && jQuery.blockUI))
            jQuery.datepicker._hideDatepicker();
    },

    /* Adjust one of the date sub-fields. */
    _adjustDate: function(id, offset, period) {
        var target = jQuery(id);
        var inst = this._getInst(target[0]);
        if (this._isDisabledDatepicker(target[0])) {
            return;
        }
        this._adjustInstDate(inst, offset +
            (period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // undo positioning
            period);
        this._updateDatepicker(inst);
    },

    /* Action for current link. */
    _gotoToday: function(id) {
        var target = jQuery(id);
        var inst = this._getInst(target[0]);
        if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
            inst.selectedDay = inst.currentDay;
            inst.drawMonth = inst.selectedMonth = inst.currentMonth;
            inst.drawYear = inst.selectedYear = inst.currentYear;
        }
        else {
            var date = new Date();
            inst.selectedDay = date.getDate();
            inst.drawMonth = inst.selectedMonth = date.getMonth();
            inst.drawYear = inst.selectedYear = date.getFullYear();
        }
        this._notifyChange(inst);
        this._adjustDate(target);
    },

    /* Action for selecting a new month/year. */
    _selectMonthYear: function(id, select, period) {

        var target = jQuery(id);
        var inst = this._getInst(target[0]);
        inst._selectingMonthYear = false;
        inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
        inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
            parseInt(select.options[select.selectedIndex].value,10);
        this._notifyChange(inst);
        this._adjustDate(target);
    },

    /* Restore input focus after not changing month/year. */
    _clickMonthYear: function(id) {
        var target = jQuery(id);
        var inst = this._getInst(target[0]);
        if (inst.input && inst._selectingMonthYear && !jQuery.browser.msie)
            inst.input.focus();
        inst._selectingMonthYear = !inst._selectingMonthYear;
    },

    /* Action for selecting a day. */
    _selectDay: function(id, month, year, td) {
        var target = jQuery(id);
        if (jQuery(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
            return;
        }
        var inst = this._getInst(target[0]);
        inst.selectedDay = inst.currentDay = jQuery('a', td).html();
        inst.selectedMonth = inst.currentMonth = month;
        inst.selectedYear = inst.currentYear = year;
        this._selectDate(id, this._formatDate(inst,
            inst.currentDay, inst.currentMonth, inst.currentYear));
    },

    /* Erase the input field and hide the date picker. */
    _clearDate: function(id) {
        var target = jQuery(id);
        var inst = this._getInst(target[0]);
        this._selectDate(target, '');
    },

    /* Update the input field with the selected date. */
    _selectDate: function(id, dateStr) {
        var target = jQuery(id);
        var inst = this._getInst(target[0]);
        dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
        if (inst.input)
            inst.input.val(dateStr);
        this._updateAlternate(inst);
        var onSelect = this._get(inst, 'onSelect');
        if (onSelect)
            onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);  // trigger custom callback
        else if (inst.input)
            inst.input.trigger('change'); // fire the change event
        if (inst.inline)
            this._updateDatepicker(inst);
        else {
            this._hideDatepicker();
            this._lastInput = inst.input[0];
            if (typeof(inst.input[0]) != 'object')
                inst.input.focus(); // restore focus
            this._lastInput = null;
        }
    },

    /* Update any alternate field to synchronise with the main field. */
    _updateAlternate: function(inst) {
        var altField = this._get(inst, 'altField');
        if (altField) { // update alternate field too
            var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
            var date = this._getDate(inst);
            var dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
            jQuery(altField).each(function() { jQuery(this).val(dateStr); });
        }
    },

    /* Set as beforeShowDay function to prevent selection of weekends.
       @param  date  Date - the date to customise
       @return [boolean, string] - is this date selectable?, what is its CSS class? */
    noWeekends: function(date) {
        var day = date.getDay();
        return [(day > 0 && day < 6), ''];
    },

    /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
       @param  date  Date - the date to get the week for
       @return  number - the number of the week within the year that contains this date */
    iso8601Week: function(date) {
        var checkDate = new Date(date.getTime());
        // Find Thursday of this week starting on Monday
        checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
        var time = checkDate.getTime();
        checkDate.setMonth(0); // Compare with Jan 1
        checkDate.setDate(1);
        return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
    },

    /* Parse a string value into a date object.
       See formatDate below for the possible formats.

       @param  format    string - the expected format of the date
       @param  value     string - the date in the above format
       @param  settings  Object - attributes include:
                         shortYearCutoff  number - the cutoff year for determining the century (optional)
                         dayNamesShort    string[7] - abbreviated names of the days from Sunday (optional)
                         dayNames         string[7] - names of the days from Sunday (optional)
                         monthNamesShort  string[12] - abbreviated names of the months (optional)
                         monthNames       string[12] - names of the months (optional)
       @return  Date - the extracted date value or null if value is blank */
    parseDate: function (format, value, settings) {
        if (format == null || value == null)
            throw 'Invalid arguments';
        value = (typeof value == 'object' ? value.toString() : value + '');
        if (value == '')
            return null;
        var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
        var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
        var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
        var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
        var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
        var year = -1;
        var month = -1;
        var day = -1;
        var doy = -1;
        var literal = false;
        // Check whether a format character is doubled
        var lookAhead = function(match) {
            var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
            if (matches)
                iFormat++;
            return matches;
        };
        // Extract a number from the string value
        var getNumber = function(match) {
            lookAhead(match);
            var size = (match == '@' ? 14 : (match == '!' ? 20 :
                (match == 'y' ? 4 : (match == 'o' ? 3 : 2))));
            var digits = new RegExp('^\\d{1,' + size + '}');
            var num = value.substring(iValue).match(digits);
            if (!num)
                throw 'Missing number at position ' + iValue;
            iValue += num[0].length;
            return parseInt(num[0], 10);
        };
        // Extract a name from the string value and convert to an index
        var getName = function(match, shortNames, longNames) {
            var names = (lookAhead(match) ? longNames : shortNames);
            for (var i = 0; i < names.length; i++) {
                if (value.substr(iValue, names[i].length) == names[i]) {
                    iValue += names[i].length;
                    return i + 1;
                }
            }
            throw 'Unknown name at position ' + iValue;
        };
        // Confirm that a literal character matches the string value
        var checkLiteral = function() {
            if (value.charAt(iValue) != format.charAt(iFormat))
                throw 'Unexpected literal at position ' + iValue;
            iValue++;
        };
        var iValue = 0;
        for (var iFormat = 0; iFormat < format.length; iFormat++) {
            if (literal)
                if (format.charAt(iFormat) == "'" && !lookAhead("'"))
                    literal = false;
                else
                    checkLiteral();
            else
                switch (format.charAt(iFormat)) {
                    case 'd':
                        day = getNumber('d');
                        break;
                    case 'D':
                        getName('D', dayNamesShort, dayNames);
                        break;
                    case 'o':
                        doy = getNumber('o');
                        break;
                    case 'm':
                        month = getNumber('m');
                        break;
                    case 'M':
                        month = getName('M', monthNamesShort, monthNames);
                        break;
                    case 'y':
                        year = getNumber('y');
                        break;
                    case '@':
                        var date = new Date(getNumber('@'));
                        year = date.getFullYear();
                        month = date.getMonth() + 1;
                        day = date.getDate();
                        break;
                    case '!':
                        var date = new Date((getNumber('!') - this._ticksTo1970) / 10000);
                        year = date.getFullYear();
                        month = date.getMonth() + 1;
                        day = date.getDate();
                        break;
                    case "'":
                        if (lookAhead("'"))
                            checkLiteral();
                        else
                            literal = true;
                        break;
                    default:
                        checkLiteral();
                }
        }
        if (year == -1)
            year = new Date().getFullYear();
        else if (year < 100)
            year += new Date().getFullYear() - new Date().getFullYear() % 100 +
                (year <= shortYearCutoff ? 0 : -100);
        if (doy > -1) {
            month = 1;
            day = doy;
            do {
                var dim = this._getDaysInMonth(year, month - 1);
                if (day <= dim)
                    break;
                month++;
                day -= dim;
            } while (true);
        }
        var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
        if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
            throw 'Invalid date'; // E.g. 31/02/*
        return date;
    },

    /* Standard date formats. */
    ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
    COOKIE: 'D, dd M yy',
    ISO_8601: 'yy-mm-dd',
    RFC_822: 'D, d M y',
    RFC_850: 'DD, dd-M-y',
    RFC_1036: 'D, d M y',
    RFC_1123: 'D, d M yy',
    RFC_2822: 'D, d M yy',
    RSS: 'D, d M y', // RFC 822
    TICKS: '!',
    TIMESTAMP: '@',
    W3C: 'yy-mm-dd', // ISO 8601

    _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
        Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),

    /* Format a date object into a string value.
       The format can be combinations of the following:
       d  - day of month (no leading zero)
       dd - day of month (two digit)
       o  - day of year (no leading zeros)
       oo - day of year (three digit)
       D  - day name short
       DD - day name long
       m  - month of year (no leading zero)
       mm - month of year (two digit)
       M  - month name short
       MM - month name long
       y  - year (two digit)
       yy - year (four digit)
       @ - Unix timestamp (ms since 01/01/1970)
       ! - Windows ticks (100ns since 01/01/0001)
       '...' - literal text
       '' - single quote

       @param  format    string - the desired format of the date
       @param  date      Date - the date value to format
       @param  settings  Object - attributes include:
                         dayNamesShort    string[7] - abbreviated names of the days from Sunday (optional)
                         dayNames         string[7] - names of the days from Sunday (optional)
                         monthNamesShort  string[12] - abbreviated names of the months (optional)
                         monthNames       string[12] - names of the months (optional)
       @return  string - the date in the above format */
    formatDate: function (format, date, settings) {
        if (!date)
            return '';
        var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
        var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
        var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
        var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
        // Check whether a format character is doubled
        var lookAhead = function(match) {
            var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
            if (matches)
                iFormat++;
            return matches;
        };
        // Format a number, with leading zero if necessary
        var formatNumber = function(match, value, len) {
            var num = '' + value;
            if (lookAhead(match))
                while (num.length < len)
                    num = '0' + num;
            return num;
        };
        // Format a name, short or long as requested
        var formatName = function(match, value, shortNames, longNames) {
            return (lookAhead(match) ? longNames[value] : shortNames[value]);
        };
        var output = '';
        var literal = false;
        if (date)
            for (var iFormat = 0; iFormat < format.length; iFormat++) {
                if (literal)
                    if (format.charAt(iFormat) == "'" && !lookAhead("'"))
                        literal = false;
                    else
                        output += format.charAt(iFormat);
                else
                    switch (format.charAt(iFormat)) {
                        case 'd':
                            output += formatNumber('d', date.getDate(), 2);
                            break;
                        case 'D':
                            output += formatName('D', date.getDay(), dayNamesShort, dayNames);
                            break;
                        case 'o':
                            output += formatNumber('o',
                                (date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000, 3);
                            break;
                        case 'm':
                            output += formatNumber('m', date.getMonth() + 1, 2);
                            break;
                        case 'M':
                            output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
                            break;
                        case 'y':
                            output += (lookAhead('y') ? date.getFullYear() :
                                (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
                            break;
                        case '@':
                            output += date.getTime();
                            break;
                        case '!':
                            output += date.getTime() * 10000 + this._ticksTo1970;
                            break;
                        case "'":
                            if (lookAhead("'"))
                                output += "'";
                            else
                                literal = true;
                            break;
                        default:
                            output += format.charAt(iFormat);
                    }
            }
        return output;
    },

    /* Extract all possible characters from the date format. */
    _possibleChars: function (format) {
        var chars = '';
        var literal = false;
        // Check whether a format character is doubled
        var lookAhead = function(match) {
            var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
            if (matches)
                iFormat++;
            return matches;
        };
        for (var iFormat = 0; iFormat < format.length; iFormat++)
            if (literal)
                if (format.charAt(iFormat) == "'" && !lookAhead("'"))
                    literal = false;
                else
                    chars += format.charAt(iFormat);
            else

                switch (format.charAt(iFormat)) {
                    case 'd': case 'm': case 'y': case '@':
                        chars += '0123456789';
                        break;
                    case 'D': case 'M':
                        return null; // Accept anything
                    case "'":
                        if (lookAhead("'"))
                            chars += "'";
                        else
                            literal = true;
                        break;
                    default:
                        chars += format.charAt(iFormat);
                }
        return chars;
    },

    /* Get a setting value, defaulting if necessary. */
    _get: function(inst, name) {
        return inst.settings[name] !== undefined ?
            inst.settings[name] : this._defaults[name];
    },

    /* Parse existing date and initialise date picker. */
    _setDateFromField: function(inst, noDefault) {
        if (inst.input.val() == inst.lastVal) {
            return;
        }
        var dateFormat = this._get(inst, 'dateFormat');
        var dates = inst.lastVal = inst.input ? inst.input.val() : null;
        var date, defaultDate;
        date = defaultDate = this._getDefaultDate(inst);
        var settings = this._getFormatConfig(inst);
        try {
            date = this.parseDate(dateFormat, dates, settings) || defaultDate;
        } catch (event) {
            this.log(event);
            dates = (noDefault ? '' : dates);
        }
        inst.selectedDay = date.getDate();
        inst.drawMonth = inst.selectedMonth = date.getMonth();
        inst.drawYear = inst.selectedYear = date.getFullYear();
        inst.currentDay = (dates ? date.getDate() : 0);
        inst.currentMonth = (dates ? date.getMonth() : 0);
        inst.currentYear = (dates ? date.getFullYear() : 0);
        this._adjustInstDate(inst);
    },

    /* Retrieve the default date shown on opening. */
    _getDefaultDate: function(inst) {
        return this._restrictMinMax(inst,
            this._determineDate(inst, this._get(inst, 'defaultDate'), new Date()));
    },

    /* A date may be specified as an exact value or a relative one. */
    _determineDate: function(inst, date, defaultDate) {
        var offsetNumeric = function(offset) {
            var date = new Date();
            date.setDate(date.getDate() + offset);
            return date;
        };
        var offsetString = function(offset) {
            try {
                return jQuery.datepicker.parseDate(jQuery.datepicker._get(inst, 'dateFormat'),
                    offset, jQuery.datepicker._getFormatConfig(inst));
            }
            catch (e) {
                // Ignore
            }
            var date = (offset.toLowerCase().match(/^c/) ?
                jQuery.datepicker._getDate(inst) : null) || new Date();
            var year = date.getFullYear();
            var month = date.getMonth();
            var day = date.getDate();
            var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
            var matches = pattern.exec(offset);
            while (matches) {
                switch (matches[2] || 'd') {
                    case 'd' : case 'D' :
                        day += parseInt(matches[1],10); break;
                    case 'w' : case 'W' :
                        day += parseInt(matches[1],10) * 7; break;
                    case 'm' : case 'M' :
                        month += parseInt(matches[1],10);
                        day = Math.min(day, jQuery.datepicker._getDaysInMonth(year, month));
                        break;
                    case 'y': case 'Y' :
                        year += parseInt(matches[1],10);
                        day = Math.min(day, jQuery.datepicker._getDaysInMonth(year, month));
                        break;
                }
                matches = pattern.exec(offset);
            }
            return new Date(year, month, day);
        };
        date = (date == null ? defaultDate : (typeof date == 'string' ? offsetString(date) :
            (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : date)));
        date = (date && date.toString() == 'Invalid Date' ? defaultDate : date);
        if (date) {
            date.setHours(0);
            date.setMinutes(0);
            date.setSeconds(0);
            date.setMilliseconds(0);
        }
        return this._daylightSavingAdjust(date);
    },

    /* Handle switch to/from daylight saving.
       Hours may be non-zero on daylight saving cut-over:
       > 12 when midnight changeover, but then cannot generate
       midnight datetime, so jump to 1AM, otherwise reset.
       @param  date  (Date) the date to check
       @return  (Date) the corrected date */
    _daylightSavingAdjust: function(date) {
        if (!date) return null;
        date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
        return date;
    },

    /* Set the date(s) directly. */
    _setDate: function(inst, date, noChange) {
        var clear = !(date);
        var origMonth = inst.selectedMonth;
        var origYear = inst.selectedYear;
        date = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
        inst.selectedDay = inst.currentDay = date.getDate();
        inst.drawMonth = inst.selectedMonth = inst.currentMonth = date.getMonth();
        inst.drawYear = inst.selectedYear = inst.currentYear = date.getFullYear();
        if ((origMonth != inst.selectedMonth || origYear != inst.selectedYear) && !noChange)
            this._notifyChange(inst);
        this._adjustInstDate(inst);
        if (inst.input) {
            inst.input.val(clear ? '' : this._formatDate(inst));
        }
    },

    /* Retrieve the date(s) directly. */
    _getDate: function(inst) {
        var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null :
            this._daylightSavingAdjust(new Date(
            inst.currentYear, inst.currentMonth, inst.currentDay)));
            return startDate;
    },

    /* Generate the HTML for the current state of the date picker. */
    _generateHTML: function(inst) {
        var today = new Date();
        today = this._daylightSavingAdjust(
            new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time
        var isRTL = this._get(inst, 'isRTL');
        var showButtonPanel = this._get(inst, 'showButtonPanel');
        var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
        var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
        var numMonths = this._getNumberOfMonths(inst);
        var showCurrentAtPos = this._get(inst, 'showCurrentAtPos');
        var stepMonths = this._get(inst, 'stepMonths');
        var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
        var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
            new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
        var minDate = this._getMinMaxDate(inst, 'min');
        var maxDate = this._getMinMaxDate(inst, 'max');
        var drawMonth = inst.drawMonth - showCurrentAtPos;
        var drawYear = inst.drawYear;
        if (drawMonth < 0) {
            drawMonth += 12;
            drawYear--;
        }
        if (maxDate) {
            var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
                maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
            maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
            while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
                drawMonth--;
                if (drawMonth < 0) {
                    drawMonth = 11;
                    drawYear--;
                }
            }
        }
        inst.drawMonth = drawMonth;
        inst.drawYear = drawYear;
        var prevText = this._get(inst, 'prevText');
        prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
            this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
            this._getFormatConfig(inst)));
        var prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
            '<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_' + dpuuid +
            '.datepicker._adjustDate(\'#' + inst.id + '\', -' + stepMonths + ', \'M\');"' +
            ' title="' + prevText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>' :
            (hideIfNoPrevNext ? '' : '<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+ prevText +'"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>'));
        var nextText = this._get(inst, 'nextText');
        nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
            this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
            this._getFormatConfig(inst)));
        var next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
            '<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_' + dpuuid +
            '.datepicker._adjustDate(\'#' + inst.id + '\', +' + stepMonths + ', \'M\');"' +
            ' title="' + nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>' :
            (hideIfNoPrevNext ? '' : '<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+ nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>'));
        var currentText = this._get(inst, 'currentText');
        var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today);
        currentText = (!navigationAsDateFormat ? currentText :
            this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
        var controls = (!inst.inline ? '<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_' + dpuuid +
            '.datepicker._hideDatepicker();">' + this._get(inst, 'closeText') + '</button>' : '');
        var buttonPanel = (showButtonPanel) ? '<div class="ui-datepicker-buttonpane ui-widget-content">' + (isRTL ? controls : '') +
            (this._isInRange(inst, gotoDate) ? '<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_' + dpuuid +
            '.datepicker._gotoToday(\'#' + inst.id + '\');"' +
            '>' + currentText + '</button>' : '') + (isRTL ? '' : controls) + '</div>' : '';
        var firstDay = parseInt(this._get(inst, 'firstDay'),10);
        firstDay = (isNaN(firstDay) ? 0 : firstDay);
        var showWeek = this._get(inst, 'showWeek');
        var dayNames = this._get(inst, 'dayNames');
        var dayNamesShort = this._get(inst, 'dayNamesShort');
        var dayNamesMin = this._get(inst, 'dayNamesMin');
        var monthNames = this._get(inst, 'monthNames');
        var monthNamesShort = this._get(inst, 'monthNamesShort');
        var beforeShowDay = this._get(inst, 'beforeShowDay');
        var showOtherMonths = this._get(inst, 'showOtherMonths');
        var selectOtherMonths = this._get(inst, 'selectOtherMonths');
        var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
        var defaultDate = this._getDefaultDate(inst);
        var html = '';
        for (var row = 0; row < numMonths[0]; row++) {
            var group = '';
            for (var col = 0; col < numMonths[1]; col++) {
                var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
                var cornerClass = ' ui-corner-all';
                var calender = '';
                if (isMultiMonth) {
                    calender += '<div class="ui-datepicker-group';
                    if (numMonths[1] > 1)
                        switch (col) {
                            case 0: calender += ' ui-datepicker-group-first';
                                cornerClass = ' ui-corner-' + (isRTL ? 'right' : 'left'); break;
                            case numMonths[1]-1: calender += ' ui-datepicker-group-last';
                                cornerClass = ' ui-corner-' + (isRTL ? 'left' : 'right'); break;
                            default: calender += ' ui-datepicker-group-middle'; cornerClass = ''; break;
                        }
                    calender += '">';
                }
                calender += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix' + cornerClass + '">' +
                    (/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') +
                    (/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') +
                    this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
                    row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
                    '</div><table class="ui-datepicker-calendar"><thead>' +
                    '<tr>';
                var thead = (showWeek ? '<th class="ui-datepicker-week-col">' + this._get(inst, 'weekHeader') + '</th>' : '');
                for (var dow = 0; dow < 7; dow++) { // days of the week
                    var day = (dow + firstDay) % 7;
                    thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end"' : '') + '>' +
                        '<span title="' + dayNames[day] + '">' + dayNamesMin[day] + '</span></th>';
                }
                calender += thead + '</tr></thead><tbody>';
                var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
                if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
                    inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
                var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
                var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // calculate the number of rows to generate
                var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
                for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
                    calender += '<tr>';
                    var tbody = (!showWeek ? '' : '<td class="ui-datepicker-week-col">' +
                        this._get(inst, 'calculateWeek')(printDate) + '</td>');
                    for (var dow = 0; dow < 7; dow++) { // create date picker days
                        var daySettings = (beforeShowDay ?
                            beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
                        var otherMonth = (printDate.getMonth() != drawMonth);
                        var unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
                            (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
                        tbody += '<td class="' +
                            ((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end' : '') + // highlight weekends
                            (otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months
                            ((printDate.getTime() == selectedDate.getTime() && drawMonth == inst.selectedMonth && inst._keyEvent) || // user pressed key
                            (defaultDate.getTime() == printDate.getTime() && defaultDate.getTime() == selectedDate.getTime()) ?
                            // or defaultDate is current printedDate and defaultDate is selectedDate
                            ' ' + this._dayOverClass : '') + // highlight selected day
                            (unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled': '') +  // highlight unselectable days
                            (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
                            (printDate.getTime() == currentDate.getTime() ? ' ' + this._currentClass : '') + // highlight selected day
                            (printDate.getTime() == today.getTime() ? ' ui-datepicker-today' : '')) + '"' + // highlight today (if different)
                            ((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
                            (unselectable ? '' : ' onclick="DP_jQuery_' + dpuuid + '.datepicker._selectDay(\'#' +
                            inst.id + '\',' + printDate.getMonth() + ',' + printDate.getFullYear() + ', this);return false;"') + '>' + // actions
                            (otherMonth && !showOtherMonths ? '&#xa0;' : // display for other months
                            (unselectable ? '<span class="ui-state-default">' + printDate.getDate() + '</span>' : '<a class="ui-state-default' +
                            (printDate.getTime() == today.getTime() ? ' ui-state-highlight' : '') +
                            (printDate.getTime() == currentDate.getTime() ? ' ui-state-active' : '') + // highlight selected day
                            (otherMonth ? ' ui-priority-secondary' : '') + // distinguish dates from other months
                            '" href="#">' + printDate.getDate() + '</a>')) + '</td>'; // display selectable date
                        printDate.setDate(printDate.getDate() + 1);
                        printDate = this._daylightSavingAdjust(printDate);
                    }
                    calender += tbody + '</tr>';
                }
                drawMonth++;
                if (drawMonth > 11) {
                    drawMonth = 0;
                    drawYear++;
                }
                calender += '</tbody></table>' + (isMultiMonth ? '</div>' + 
                            ((numMonths[0] > 0 && col == numMonths[1]-1) ? '<div class="ui-datepicker-row-break"></div>' : '') : '');
                group += calender;
            }
            html += group;
        }
        html += buttonPanel + (jQuery.browser.msie && parseInt(jQuery.browser.version,10) < 7 && !inst.inline ?
            '<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>' : '');
        inst._keyEvent = false;
        return html;
    },

    /* Generate the month and year header. */
    _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
            secondary, monthNames, monthNamesShort) {
        var changeMonth = this._get(inst, 'changeMonth');
        var changeYear = this._get(inst, 'changeYear');
        var showMonthAfterYear = this._get(inst, 'showMonthAfterYear');
        var html = '<div class="ui-datepicker-title">';
        var monthHtml = '';
        // month selection
        if (secondary || !changeMonth)
            monthHtml += '<span class="ui-datepicker-month">' + monthNames[drawMonth] + '</span>';
        else {
            var inMinYear = (minDate && minDate.getFullYear() == drawYear);
            var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
            monthHtml += '<select class="ui-datepicker-month" ' +
                'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'M\');" ' +
                'onclick="DP_jQuery_' + dpuuid + '.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
                 '>';
            for (var month = 0; month < 12; month++) {
                if ((!inMinYear || month >= minDate.getMonth()) &&
                        (!inMaxYear || month <= maxDate.getMonth()))
                    monthHtml += '<option value="' + month + '"' +
                        (month == drawMonth ? ' selected="selected"' : '') +
                        '>' + monthNamesShort[month] + '</option>';
            }
            monthHtml += '</select>';
        }
        if (!showMonthAfterYear)
            html += monthHtml + (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '');
        // year selection
        if (secondary || !changeYear)
            html += '<span class="ui-datepicker-year">' + drawYear + '</span>';
        else {
            // determine range of years to display
            var years = this._get(inst, 'yearRange').split(':');
            var thisYear = new Date().getFullYear();
            var determineYear = function(value) {
                var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) :
                    (value.match(/[+-].*/) ? thisYear + parseInt(value, 10) :
                    parseInt(value, 10)));
                return (isNaN(year) ? thisYear : year);
            };
            var year = determineYear(years[0]);
            var endYear = Math.max(year, determineYear(years[1] || ''));
            year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
            endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
            html += '<select class="ui-datepicker-year" ' +
                'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'Y\');" ' +
                'onclick="DP_jQuery_' + dpuuid + '.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
                '>';
            for (; year <= endYear; year++) {
                html += '<option value="' + year + '"' +
                    (year == drawYear ? ' selected="selected"' : '') +
                    '>' + year + '</option>';
            }
            html += '</select>';
        }
        html += this._get(inst, 'yearSuffix');
        if (showMonthAfterYear)
            html += (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '') + monthHtml;
        html += '</div>'; // Close datepicker_header
        return html;
    },

    /* Adjust one of the date sub-fields. */
    _adjustInstDate: function(inst, offset, period) {
        var year = inst.drawYear + (period == 'Y' ? offset : 0);
        var month = inst.drawMonth + (period == 'M' ? offset : 0);
        var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) +
            (period == 'D' ? offset : 0);
        var date = this._restrictMinMax(inst,
            this._daylightSavingAdjust(new Date(year, month, day)));
        inst.selectedDay = date.getDate();
        inst.drawMonth = inst.selectedMonth = date.getMonth();
        inst.drawYear = inst.selectedYear = date.getFullYear();
        if (period == 'M' || period == 'Y')
            this._notifyChange(inst);
    },

    /* Ensure a date is within any min/max bounds. */
    _restrictMinMax: function(inst, date) {
        var minDate = this._getMinMaxDate(inst, 'min');
        var maxDate = this._getMinMaxDate(inst, 'max');
        date = (minDate && date < minDate ? minDate : date);
        date = (maxDate && date > maxDate ? maxDate : date);
        return date;
    },

    /* Notify change of month/year. */
    _notifyChange: function(inst) {
        var onChange = this._get(inst, 'onChangeMonthYear');
        if (onChange)
            onChange.apply((inst.input ? inst.input[0] : null),
                [inst.selectedYear, inst.selectedMonth + 1, inst]);
    },

    /* Determine the number of months to show. */
    _getNumberOfMonths: function(inst) {
        var numMonths = this._get(inst, 'numberOfMonths');
        return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
    },

    /* Determine the current maximum date - ensure no time components are set. */
    _getMinMaxDate: function(inst, minMax) {
        return this._determineDate(inst, this._get(inst, minMax + 'Date'), null);
    },

    /* Find the number of days in a given month. */
    _getDaysInMonth: function(year, month) {
        return 32 - new Date(year, month, 32).getDate();
    },

    /* Find the day of the week of the first of a month. */
    _getFirstDayOfMonth: function(year, month) {
        return new Date(year, month, 1).getDay();
    },

    /* Determines if we should allow a "next/prev" month display change. */
    _canAdjustMonth: function(inst, offset, curYear, curMonth) {
        var numMonths = this._getNumberOfMonths(inst);
        var date = this._daylightSavingAdjust(new Date(curYear,
            curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
        if (offset < 0)
            date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
        return this._isInRange(inst, date);
    },

    /* Is the given date in the accepted range? */
    _isInRange: function(inst, date) {
        var minDate = this._getMinMaxDate(inst, 'min');
        var maxDate = this._getMinMaxDate(inst, 'max');
        return ((!minDate || date.getTime() >= minDate.getTime()) &&
            (!maxDate || date.getTime() <= maxDate.getTime()));
    },

    /* Provide the configuration settings for formatting/parsing. */
    _getFormatConfig: function(inst) {
        var shortYearCutoff = this._get(inst, 'shortYearCutoff');
        shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
            new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
        return {shortYearCutoff: shortYearCutoff,
            dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
            monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
    },

    /* Format the given date for display. */
    _formatDate: function(inst, day, month, year) {
        if (!day) {
            inst.currentDay = inst.selectedDay;
            inst.currentMonth = inst.selectedMonth;
            inst.currentYear = inst.selectedYear;
        }
        var date = (day ? (typeof day == 'object' ? day :
            this._daylightSavingAdjust(new Date(year, month, day))) :
            this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
        return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
    }
});

/* jQuery extend now ignores nulls! */
function extendRemove(target, props) {
    jQuery.extend(target, props);
    for (var name in props)
        if (props[name] == null || props[name] == undefined)
            target[name] = props[name];
    return target;
};

/* Determine whether an object is an array. */
function isArray(a) {
    return (a && ((jQuery.browser.safari && typeof a == 'object' && a.length) ||
        (a.constructor && a.constructor.toString().match(/\Array\(\)/))));
};

/* Invoke the datepicker functionality.
   @param  options  string - a command, optionally followed by additional parameters or
                    Object - settings for attaching new datepicker functionality
   @return  jQuery object */
jQuery.fn.datepicker = function(options){

    /* Initialise the date picker. */
    if (!jQuery.datepicker.initialized) {
        jQuery(document).mousedown(jQuery.datepicker._checkExternalClick).
            find('body').append(jQuery.datepicker.dpDiv);
        jQuery.datepicker.initialized = true;
    }

    var otherArgs = Array.prototype.slice.call(arguments, 1);
    if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate' || options == 'widget'))
        return jQuery.datepicker['_' + options + 'Datepicker'].
            apply(jQuery.datepicker, [this[0]].concat(otherArgs));
    if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
        return jQuery.datepicker['_' + options + 'Datepicker'].
            apply(jQuery.datepicker, [this[0]].concat(otherArgs));
    return this.each(function() {
        typeof options == 'string' ?
            jQuery.datepicker['_' + options + 'Datepicker'].
                apply(jQuery.datepicker, [this].concat(otherArgs)) :
            jQuery.datepicker._attachDatepicker(this, options);
    });
};

jQuery.datepicker = new Datepicker(); // singleton instance
jQuery.datepicker.initialized = false;
jQuery.datepicker.uuid = new Date().getTime();
jQuery.datepicker.version = "@VERSION";

// Workaround for #4055
// Add another global to avoid noConflict issues with inline event handlers
window['DP_jQuery_' + dpuuid] = jQuery;

})(jQuery);


