source: Dev/branches/jQueryUI/client/js/jquery/ui/jquery.ui.datepicker.js @ 249

Last change on this file since 249 was 249, checked in by hendrikvanantwerpen, 13 years ago

This one's for Subversion, because it's so close...

First widget (stripped down sequencer).
Seperated client and server code in two direcotry trees.

File size: 74.6 KB
Line 
1/*
2 * jQuery UI Datepicker 1.8.17
3 *
4 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 * http://jquery.org/license
7 *
8 * http://docs.jquery.com/UI/Datepicker
9 *
10 * Depends:
11 *      jquery.ui.core.js
12 */
13(function( $, undefined ) {
14
15$.extend($.ui, { datepicker: { version: "1.8.17" } });
16
17var PROP_NAME = 'datepicker';
18var dpuuid = new Date().getTime();
19var instActive;
20
21/* Date picker manager.
22   Use the singleton instance of this class, $.datepicker, to interact with the date picker.
23   Settings for (groups of) date pickers are maintained in an instance object,
24   allowing multiple different settings on the same page. */
25
26function Datepicker() {
27        this.debug = false; // Change this to true to start debugging
28        this._curInst = null; // The current instance in use
29        this._keyEvent = false; // If the last event was a key event
30        this._disabledInputs = []; // List of date picker inputs that have been disabled
31        this._datepickerShowing = false; // True if the popup picker is showing , false if not
32        this._inDialog = false; // True if showing within a "dialog", false if not
33        this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division
34        this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class
35        this._appendClass = 'ui-datepicker-append'; // The name of the append marker class
36        this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class
37        this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class
38        this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class
39        this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class
40        this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class
41        this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class
42        this.regional = []; // Available regional settings, indexed by language code
43        this.regional[''] = { // Default regional settings
44                closeText: 'Done', // Display text for close link
45                prevText: 'Prev', // Display text for previous month link
46                nextText: 'Next', // Display text for next month link
47                currentText: 'Today', // Display text for current month link
48                monthNames: ['January','February','March','April','May','June',
49                        'July','August','September','October','November','December'], // Names of months for drop-down and formatting
50                monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
51                dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
52                dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
53                dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
54                weekHeader: 'Wk', // Column header for week of the year
55                dateFormat: 'mm/dd/yy', // See format options on parseDate
56                firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
57                isRTL: false, // True if right-to-left language, false if left-to-right
58                showMonthAfterYear: false, // True if the year select precedes month, false for month then year
59                yearSuffix: '' // Additional text to append to the year in the month headers
60        };
61        this._defaults = { // Global defaults for all the date picker instances
62                showOn: 'focus', // 'focus' for popup on focus,
63                        // 'button' for trigger button, or 'both' for either
64                showAnim: 'fadeIn', // Name of jQuery animation for popup
65                showOptions: {}, // Options for enhanced animations
66                defaultDate: null, // Used when field is blank: actual date,
67                        // +/-number for offset from today, null for today
68                appendText: '', // Display text following the input box, e.g. showing the format
69                buttonText: '...', // Text for trigger button
70                buttonImage: '', // URL for trigger button image
71                buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
72                hideIfNoPrevNext: false, // True to hide next/previous month links
73                        // if not applicable, false to just disable them
74                navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
75                gotoCurrent: false, // True if today link goes back to current selection instead
76                changeMonth: false, // True if month can be selected directly, false if only prev/next
77                changeYear: false, // True if year can be selected directly, false if only prev/next
78                yearRange: 'c-10:c+10', // Range of years to display in drop-down,
79                        // either relative to today's year (-nn:+nn), relative to currently displayed year
80                        // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
81                showOtherMonths: false, // True to show dates in other months, false to leave blank
82                selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
83                showWeek: false, // True to show week of the year, false to not show it
84                calculateWeek: this.iso8601Week, // How to calculate the week of the year,
85                        // takes a Date and returns the number of the week for it
86                shortYearCutoff: '+10', // Short year values < this are in the current century,
87                        // > this are in the previous century,
88                        // string value starting with '+' for current year + value
89                minDate: null, // The earliest selectable date, or null for no limit
90                maxDate: null, // The latest selectable date, or null for no limit
91                duration: 'fast', // Duration of display/closure
92                beforeShowDay: null, // Function that takes a date and returns an array with
93                        // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '',
94                        // [2] = cell title (optional), e.g. $.datepicker.noWeekends
95                beforeShow: null, // Function that takes an input field and
96                        // returns a set of custom settings for the date picker
97                onSelect: null, // Define a callback function when a date is selected
98                onChangeMonthYear: null, // Define a callback function when the month or year is changed
99                onClose: null, // Define a callback function when the datepicker is closed
100                numberOfMonths: 1, // Number of months to show at a time
101                showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
102                stepMonths: 1, // Number of months to step back/forward
103                stepBigMonths: 12, // Number of months to step back/forward for the big links
104                altField: '', // Selector for an alternate field to store selected dates into
105                altFormat: '', // The date format to use for the alternate field
106                constrainInput: true, // The input is constrained by the current date format
107                showButtonPanel: false, // True to show button panel, false to not show it
108                autoSize: false, // True to size the input for the date format, false to leave as is
109                disabled: false // The initial disabled state
110        };
111        $.extend(this._defaults, this.regional['']);
112        this.dpDiv = bindHover($('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'));
113}
114
115$.extend(Datepicker.prototype, {
116        /* Class name added to elements to indicate already configured with a date picker. */
117        markerClassName: 'hasDatepicker',
118       
119        //Keep track of the maximum number of rows displayed (see #7043)
120        maxRows: 4,
121
122        /* Debug logging (if enabled). */
123        log: function () {
124                if (this.debug)
125                        console.log.apply('', arguments);
126        },
127       
128        // TODO rename to "widget" when switching to widget factory
129        _widgetDatepicker: function() {
130                return this.dpDiv;
131        },
132
133        /* Override the default settings for all instances of the date picker.
134           @param  settings  object - the new settings to use as defaults (anonymous object)
135           @return the manager object */
136        setDefaults: function(settings) {
137                extendRemove(this._defaults, settings || {});
138                return this;
139        },
140
141        /* Attach the date picker to a jQuery selection.
142           @param  target    element - the target input field or division or span
143           @param  settings  object - the new settings to use for this date picker instance (anonymous) */
144        _attachDatepicker: function(target, settings) {
145                // check for settings on the control itself - in namespace 'date:'
146                var inlineSettings = null;
147                for (var attrName in this._defaults) {
148                        var attrValue = target.getAttribute('date:' + attrName);
149                        if (attrValue) {
150                                inlineSettings = inlineSettings || {};
151                                try {
152                                        inlineSettings[attrName] = eval(attrValue);
153                                } catch (err) {
154                                        inlineSettings[attrName] = attrValue;
155                                }
156                        }
157                }
158                var nodeName = target.nodeName.toLowerCase();
159                var inline = (nodeName == 'div' || nodeName == 'span');
160                if (!target.id) {
161                        this.uuid += 1;
162                        target.id = 'dp' + this.uuid;
163                }
164                var inst = this._newInst($(target), inline);
165                inst.settings = $.extend({}, settings || {}, inlineSettings || {});
166                if (nodeName == 'input') {
167                        this._connectDatepicker(target, inst);
168                } else if (inline) {
169                        this._inlineDatepicker(target, inst);
170                }
171        },
172
173        /* Create a new instance object. */
174        _newInst: function(target, inline) {
175                var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
176                return {id: id, input: target, // associated target
177                        selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
178                        drawMonth: 0, drawYear: 0, // month being drawn
179                        inline: inline, // is datepicker inline or not
180                        dpDiv: (!inline ? this.dpDiv : // presentation div
181                        bindHover($('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')))};
182        },
183
184        /* Attach the date picker to an input field. */
185        _connectDatepicker: function(target, inst) {
186                var input = $(target);
187                inst.append = $([]);
188                inst.trigger = $([]);
189                if (input.hasClass(this.markerClassName))
190                        return;
191                this._attachments(input, inst);
192                input.addClass(this.markerClassName).keydown(this._doKeyDown).
193                        keypress(this._doKeyPress).keyup(this._doKeyUp).
194                        bind("setData.datepicker", function(event, key, value) {
195                                inst.settings[key] = value;
196                        }).bind("getData.datepicker", function(event, key) {
197                                return this._get(inst, key);
198                        });
199                this._autoSize(inst);
200                $.data(target, PROP_NAME, inst);
201                //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
202                if( inst.settings.disabled ) {
203                        this._disableDatepicker( target );
204                }
205        },
206
207        /* Make attachments based on settings. */
208        _attachments: function(input, inst) {
209                var appendText = this._get(inst, 'appendText');
210                var isRTL = this._get(inst, 'isRTL');
211                if (inst.append)
212                        inst.append.remove();
213                if (appendText) {
214                        inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
215                        input[isRTL ? 'before' : 'after'](inst.append);
216                }
217                input.unbind('focus', this._showDatepicker);
218                if (inst.trigger)
219                        inst.trigger.remove();
220                var showOn = this._get(inst, 'showOn');
221                if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
222                        input.focus(this._showDatepicker);
223                if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
224                        var buttonText = this._get(inst, 'buttonText');
225                        var buttonImage = this._get(inst, 'buttonImage');
226                        inst.trigger = $(this._get(inst, 'buttonImageOnly') ?
227                                $('<img/>').addClass(this._triggerClass).
228                                        attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
229                                $('<button type="button"></button>').addClass(this._triggerClass).
230                                        html(buttonImage == '' ? buttonText : $('<img/>').attr(
231                                        { src:buttonImage, alt:buttonText, title:buttonText })));
232                        input[isRTL ? 'before' : 'after'](inst.trigger);
233                        inst.trigger.click(function() {
234                                if ($.datepicker._datepickerShowing && $.datepicker._lastInput == input[0])
235                                        $.datepicker._hideDatepicker();
236                                else
237                                        $.datepicker._showDatepicker(input[0]);
238                                return false;
239                        });
240                }
241        },
242
243        /* Apply the maximum length for the date format. */
244        _autoSize: function(inst) {
245                if (this._get(inst, 'autoSize') && !inst.inline) {
246                        var date = new Date(2009, 12 - 1, 20); // Ensure double digits
247                        var dateFormat = this._get(inst, 'dateFormat');
248                        if (dateFormat.match(/[DM]/)) {
249                                var findMax = function(names) {
250                                        var max = 0;
251                                        var maxI = 0;
252                                        for (var i = 0; i < names.length; i++) {
253                                                if (names[i].length > max) {
254                                                        max = names[i].length;
255                                                        maxI = i;
256                                                }
257                                        }
258                                        return maxI;
259                                };
260                                date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
261                                        'monthNames' : 'monthNamesShort'))));
262                                date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
263                                        'dayNames' : 'dayNamesShort'))) + 20 - date.getDay());
264                        }
265                        inst.input.attr('size', this._formatDate(inst, date).length);
266                }
267        },
268
269        /* Attach an inline date picker to a div. */
270        _inlineDatepicker: function(target, inst) {
271                var divSpan = $(target);
272                if (divSpan.hasClass(this.markerClassName))
273                        return;
274                divSpan.addClass(this.markerClassName).append(inst.dpDiv).
275                        bind("setData.datepicker", function(event, key, value){
276                                inst.settings[key] = value;
277                        }).bind("getData.datepicker", function(event, key){
278                                return this._get(inst, key);
279                        });
280                $.data(target, PROP_NAME, inst);
281                this._setDate(inst, this._getDefaultDate(inst), true);
282                this._updateDatepicker(inst);
283                this._updateAlternate(inst);
284                //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
285                if( inst.settings.disabled ) {
286                        this._disableDatepicker( target );
287                }
288                // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
289                // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
290                inst.dpDiv.css( "display", "block" );
291        },
292
293        /* Pop-up the date picker in a "dialog" box.
294           @param  input     element - ignored
295           @param  date      string or Date - the initial date to display
296           @param  onSelect  function - the function to call when a date is selected
297           @param  settings  object - update the dialog date picker instance's settings (anonymous object)
298           @param  pos       int[2] - coordinates for the dialog's position within the screen or
299                             event - with x/y coordinates or
300                             leave empty for default (screen centre)
301           @return the manager object */
302        _dialogDatepicker: function(input, date, onSelect, settings, pos) {
303                var inst = this._dialogInst; // internal instance
304                if (!inst) {
305                        this.uuid += 1;
306                        var id = 'dp' + this.uuid;
307                        this._dialogInput = $('<input type="text" id="' + id +
308                                '" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');
309                        this._dialogInput.keydown(this._doKeyDown);
310                        $('body').append(this._dialogInput);
311                        inst = this._dialogInst = this._newInst(this._dialogInput, false);
312                        inst.settings = {};
313                        $.data(this._dialogInput[0], PROP_NAME, inst);
314                }
315                extendRemove(inst.settings, settings || {});
316                date = (date && date.constructor == Date ? this._formatDate(inst, date) : date);
317                this._dialogInput.val(date);
318
319                this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
320                if (!this._pos) {
321                        var browserWidth = document.documentElement.clientWidth;
322                        var browserHeight = document.documentElement.clientHeight;
323                        var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
324                        var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
325                        this._pos = // should use actual width/height below
326                                [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
327                }
328
329                // move input on screen for focus, but hidden behind dialog
330                this._dialogInput.css('left', (this._pos[0] + 20) + 'px').css('top', this._pos[1] + 'px');
331                inst.settings.onSelect = onSelect;
332                this._inDialog = true;
333                this.dpDiv.addClass(this._dialogClass);
334                this._showDatepicker(this._dialogInput[0]);
335                if ($.blockUI)
336                        $.blockUI(this.dpDiv);
337                $.data(this._dialogInput[0], PROP_NAME, inst);
338                return this;
339        },
340
341        /* Detach a datepicker from its control.
342           @param  target    element - the target input field or division or span */
343        _destroyDatepicker: function(target) {
344                var $target = $(target);
345                var inst = $.data(target, PROP_NAME);
346                if (!$target.hasClass(this.markerClassName)) {
347                        return;
348                }
349                var nodeName = target.nodeName.toLowerCase();
350                $.removeData(target, PROP_NAME);
351                if (nodeName == 'input') {
352                        inst.append.remove();
353                        inst.trigger.remove();
354                        $target.removeClass(this.markerClassName).
355                                unbind('focus', this._showDatepicker).
356                                unbind('keydown', this._doKeyDown).
357                                unbind('keypress', this._doKeyPress).
358                                unbind('keyup', this._doKeyUp);
359                } else if (nodeName == 'div' || nodeName == 'span')
360                        $target.removeClass(this.markerClassName).empty();
361        },
362
363        /* Enable the date picker to a jQuery selection.
364           @param  target    element - the target input field or division or span */
365        _enableDatepicker: function(target) {
366                var $target = $(target);
367                var inst = $.data(target, PROP_NAME);
368                if (!$target.hasClass(this.markerClassName)) {
369                        return;
370                }
371                var nodeName = target.nodeName.toLowerCase();
372                if (nodeName == 'input') {
373                        target.disabled = false;
374                        inst.trigger.filter('button').
375                                each(function() { this.disabled = false; }).end().
376                                filter('img').css({opacity: '1.0', cursor: ''});
377                }
378                else if (nodeName == 'div' || nodeName == 'span') {
379                        var inline = $target.children('.' + this._inlineClass);
380                        inline.children().removeClass('ui-state-disabled');
381                        inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
382                                removeAttr("disabled");
383                }
384                this._disabledInputs = $.map(this._disabledInputs,
385                        function(value) { return (value == target ? null : value); }); // delete entry
386        },
387
388        /* Disable the date picker to a jQuery selection.
389           @param  target    element - the target input field or division or span */
390        _disableDatepicker: function(target) {
391                var $target = $(target);
392                var inst = $.data(target, PROP_NAME);
393                if (!$target.hasClass(this.markerClassName)) {
394                        return;
395                }
396                var nodeName = target.nodeName.toLowerCase();
397                if (nodeName == 'input') {
398                        target.disabled = true;
399                        inst.trigger.filter('button').
400                                each(function() { this.disabled = true; }).end().
401                                filter('img').css({opacity: '0.5', cursor: 'default'});
402                }
403                else if (nodeName == 'div' || nodeName == 'span') {
404                        var inline = $target.children('.' + this._inlineClass);
405                        inline.children().addClass('ui-state-disabled');
406                        inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
407                                attr("disabled", "disabled");
408                }
409                this._disabledInputs = $.map(this._disabledInputs,
410                        function(value) { return (value == target ? null : value); }); // delete entry
411                this._disabledInputs[this._disabledInputs.length] = target;
412        },
413
414        /* Is the first field in a jQuery collection disabled as a datepicker?
415           @param  target    element - the target input field or division or span
416           @return boolean - true if disabled, false if enabled */
417        _isDisabledDatepicker: function(target) {
418                if (!target) {
419                        return false;
420                }
421                for (var i = 0; i < this._disabledInputs.length; i++) {
422                        if (this._disabledInputs[i] == target)
423                                return true;
424                }
425                return false;
426        },
427
428        /* Retrieve the instance data for the target control.
429           @param  target  element - the target input field or division or span
430           @return  object - the associated instance data
431           @throws  error if a jQuery problem getting data */
432        _getInst: function(target) {
433                try {
434                        return $.data(target, PROP_NAME);
435                }
436                catch (err) {
437                        throw 'Missing instance data for this datepicker';
438                }
439        },
440
441        /* Update or retrieve the settings for a date picker attached to an input field or division.
442           @param  target  element - the target input field or division or span
443           @param  name    object - the new settings to update or
444                           string - the name of the setting to change or retrieve,
445                           when retrieving also 'all' for all instance settings or
446                           'defaults' for all global defaults
447           @param  value   any - the new value for the setting
448                           (omit if above is an object or to retrieve a value) */
449        _optionDatepicker: function(target, name, value) {
450                var inst = this._getInst(target);
451                if (arguments.length == 2 && typeof name == 'string') {
452                        return (name == 'defaults' ? $.extend({}, $.datepicker._defaults) :
453                                (inst ? (name == 'all' ? $.extend({}, inst.settings) :
454                                this._get(inst, name)) : null));
455                }
456                var settings = name || {};
457                if (typeof name == 'string') {
458                        settings = {};
459                        settings[name] = value;
460                }
461                if (inst) {
462                        if (this._curInst == inst) {
463                                this._hideDatepicker();
464                        }
465                        var date = this._getDateDatepicker(target, true);
466                        var minDate = this._getMinMaxDate(inst, 'min');
467                        var maxDate = this._getMinMaxDate(inst, 'max');
468                        extendRemove(inst.settings, settings);
469                        // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
470                        if (minDate !== null && settings['dateFormat'] !== undefined && settings['minDate'] === undefined)
471                                inst.settings.minDate = this._formatDate(inst, minDate);
472                        if (maxDate !== null && settings['dateFormat'] !== undefined && settings['maxDate'] === undefined)
473                                inst.settings.maxDate = this._formatDate(inst, maxDate);
474                        this._attachments($(target), inst);
475                        this._autoSize(inst);
476                        this._setDate(inst, date);
477                        this._updateAlternate(inst);
478                        this._updateDatepicker(inst);
479                }
480        },
481
482        // change method deprecated
483        _changeDatepicker: function(target, name, value) {
484                this._optionDatepicker(target, name, value);
485        },
486
487        /* Redraw the date picker attached to an input field or division.
488           @param  target  element - the target input field or division or span */
489        _refreshDatepicker: function(target) {
490                var inst = this._getInst(target);
491                if (inst) {
492                        this._updateDatepicker(inst);
493                }
494        },
495
496        /* Set the dates for a jQuery selection.
497           @param  target   element - the target input field or division or span
498           @param  date     Date - the new date */
499        _setDateDatepicker: function(target, date) {
500                var inst = this._getInst(target);
501                if (inst) {
502                        this._setDate(inst, date);
503                        this._updateDatepicker(inst);
504                        this._updateAlternate(inst);
505                }
506        },
507
508        /* Get the date(s) for the first entry in a jQuery selection.
509           @param  target     element - the target input field or division or span
510           @param  noDefault  boolean - true if no default date is to be used
511           @return Date - the current date */
512        _getDateDatepicker: function(target, noDefault) {
513                var inst = this._getInst(target);
514                if (inst && !inst.inline)
515                        this._setDateFromField(inst, noDefault);
516                return (inst ? this._getDate(inst) : null);
517        },
518
519        /* Handle keystrokes. */
520        _doKeyDown: function(event) {
521                var inst = $.datepicker._getInst(event.target);
522                var handled = true;
523                var isRTL = inst.dpDiv.is('.ui-datepicker-rtl');
524                inst._keyEvent = true;
525                if ($.datepicker._datepickerShowing)
526                        switch (event.keyCode) {
527                                case 9: $.datepicker._hideDatepicker();
528                                                handled = false;
529                                                break; // hide on tab out
530                                case 13: var sel = $('td.' + $.datepicker._dayOverClass + ':not(.' +
531                                                                        $.datepicker._currentClass + ')', inst.dpDiv);
532                                                if (sel[0])
533                                                        $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
534                                                        var onSelect = $.datepicker._get(inst, 'onSelect');
535                                                        if (onSelect) {
536                                                                var dateStr = $.datepicker._formatDate(inst);
537
538                                                                // trigger custom callback
539                                                                onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
540                                                        }
541                                                else
542                                                        $.datepicker._hideDatepicker();
543                                                return false; // don't submit the form
544                                                break; // select the value on enter
545                                case 27: $.datepicker._hideDatepicker();
546                                                break; // hide on escape
547                                case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
548                                                        -$.datepicker._get(inst, 'stepBigMonths') :
549                                                        -$.datepicker._get(inst, 'stepMonths')), 'M');
550                                                break; // previous month/year on page up/+ ctrl
551                                case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
552                                                        +$.datepicker._get(inst, 'stepBigMonths') :
553                                                        +$.datepicker._get(inst, 'stepMonths')), 'M');
554                                                break; // next month/year on page down/+ ctrl
555                                case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target);
556                                                handled = event.ctrlKey || event.metaKey;
557                                                break; // clear on ctrl or command +end
558                                case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target);
559                                                handled = event.ctrlKey || event.metaKey;
560                                                break; // current on ctrl or command +home
561                                case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D');
562                                                handled = event.ctrlKey || event.metaKey;
563                                                // -1 day on ctrl or command +left
564                                                if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
565                                                                        -$.datepicker._get(inst, 'stepBigMonths') :
566                                                                        -$.datepicker._get(inst, 'stepMonths')), 'M');
567                                                // next month/year on alt +left on Mac
568                                                break;
569                                case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D');
570                                                handled = event.ctrlKey || event.metaKey;
571                                                break; // -1 week on ctrl or command +up
572                                case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D');
573                                                handled = event.ctrlKey || event.metaKey;
574                                                // +1 day on ctrl or command +right
575                                                if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
576                                                                        +$.datepicker._get(inst, 'stepBigMonths') :
577                                                                        +$.datepicker._get(inst, 'stepMonths')), 'M');
578                                                // next month/year on alt +right
579                                                break;
580                                case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D');
581                                                handled = event.ctrlKey || event.metaKey;
582                                                break; // +1 week on ctrl or command +down
583                                default: handled = false;
584                        }
585                else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home
586                        $.datepicker._showDatepicker(this);
587                else {
588                        handled = false;
589                }
590                if (handled) {
591                        event.preventDefault();
592                        event.stopPropagation();
593                }
594        },
595
596        /* Filter entered characters - based on date format. */
597        _doKeyPress: function(event) {
598                var inst = $.datepicker._getInst(event.target);
599                if ($.datepicker._get(inst, 'constrainInput')) {
600                        var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat'));
601                        var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
602                        return event.ctrlKey || event.metaKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
603                }
604        },
605
606        /* Synchronise manual entry and field/alternate field. */
607        _doKeyUp: function(event) {
608                var inst = $.datepicker._getInst(event.target);
609                if (inst.input.val() != inst.lastVal) {
610                        try {
611                                var date = $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
612                                        (inst.input ? inst.input.val() : null),
613                                        $.datepicker._getFormatConfig(inst));
614                                if (date) { // only if valid
615                                        $.datepicker._setDateFromField(inst);
616                                        $.datepicker._updateAlternate(inst);
617                                        $.datepicker._updateDatepicker(inst);
618                                }
619                        }
620                        catch (event) {
621                                $.datepicker.log(event);
622                        }
623                }
624                return true;
625        },
626
627        /* Pop-up the date picker for a given input field.
628       If false returned from beforeShow event handler do not show.
629           @param  input  element - the input field attached to the date picker or
630                          event - if triggered by focus */
631        _showDatepicker: function(input) {
632                input = input.target || input;
633                if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
634                        input = $('input', input.parentNode)[0];
635                if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here
636                        return;
637                var inst = $.datepicker._getInst(input);
638                if ($.datepicker._curInst && $.datepicker._curInst != inst) {
639                        $.datepicker._curInst.dpDiv.stop(true, true);
640                        if ( inst && $.datepicker._datepickerShowing ) {
641                                $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
642                        }
643                }
644                var beforeShow = $.datepicker._get(inst, 'beforeShow');
645                var beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
646                if(beforeShowSettings === false){
647            //false
648                        return;
649                }
650                extendRemove(inst.settings, beforeShowSettings);
651                inst.lastVal = null;
652                $.datepicker._lastInput = input;
653                $.datepicker._setDateFromField(inst);
654                if ($.datepicker._inDialog) // hide cursor
655                        input.value = '';
656                if (!$.datepicker._pos) { // position below input
657                        $.datepicker._pos = $.datepicker._findPos(input);
658                        $.datepicker._pos[1] += input.offsetHeight; // add the height
659                }
660                var isFixed = false;
661                $(input).parents().each(function() {
662                        isFixed |= $(this).css('position') == 'fixed';
663                        return !isFixed;
664                });
665                if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
666                        $.datepicker._pos[0] -= document.documentElement.scrollLeft;
667                        $.datepicker._pos[1] -= document.documentElement.scrollTop;
668                }
669                var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
670                $.datepicker._pos = null;
671                //to avoid flashes on Firefox
672                inst.dpDiv.empty();
673                // determine sizing offscreen
674                inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
675                $.datepicker._updateDatepicker(inst);
676                // fix width for dynamic number of date pickers
677                // and adjust position before showing
678                offset = $.datepicker._checkOffset(inst, offset, isFixed);
679                inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
680                        'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
681                        left: offset.left + 'px', top: offset.top + 'px'});
682                if (!inst.inline) {
683                        var showAnim = $.datepicker._get(inst, 'showAnim');
684                        var duration = $.datepicker._get(inst, 'duration');
685                        var postProcess = function() {
686                                var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
687                                if( !! cover.length ){
688                                        var borders = $.datepicker._getBorders(inst.dpDiv);
689                                        cover.css({left: -borders[0], top: -borders[1],
690                                                width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()});
691                                }
692                        };
693                        inst.dpDiv.zIndex($(input).zIndex()+1);
694                        $.datepicker._datepickerShowing = true;
695                        if ($.effects && $.effects[showAnim])
696                                inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
697                        else
698                                inst.dpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess);
699                        if (!showAnim || !duration)
700                                postProcess();
701                        if (inst.input.is(':visible') && !inst.input.is(':disabled'))
702                                inst.input.focus();
703                        $.datepicker._curInst = inst;
704                }
705        },
706
707        /* Generate the date picker content. */
708        _updateDatepicker: function(inst) {
709                var self = this;
710                self.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
711                var borders = $.datepicker._getBorders(inst.dpDiv);
712                instActive = inst; // for delegate hover events
713                inst.dpDiv.empty().append(this._generateHTML(inst));
714                var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
715                if( !!cover.length ){ //avoid call to outerXXXX() when not in IE6
716                        cover.css({left: -borders[0], top: -borders[1], width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()})
717                }
718                inst.dpDiv.find('.' + this._dayOverClass + ' a').mouseover();
719                var numMonths = this._getNumberOfMonths(inst);
720                var cols = numMonths[1];
721                var width = 17;
722                inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width('');
723                if (cols > 1)
724                        inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em');
725                inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
726                        'Class']('ui-datepicker-multi');
727                inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
728                        'Class']('ui-datepicker-rtl');
729                if (inst == $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input &&
730                                // #6694 - don't focus the input if it's already focused
731                                // this breaks the change event in IE
732                                inst.input.is(':visible') && !inst.input.is(':disabled') && inst.input[0] != document.activeElement)
733                        inst.input.focus();
734                // deffered render of the years select (to avoid flashes on Firefox)
735                if( inst.yearshtml ){
736                        var origyearshtml = inst.yearshtml;
737                        setTimeout(function(){
738                                //assure that inst.yearshtml didn't change.
739                                if( origyearshtml === inst.yearshtml && inst.yearshtml ){
740                                        inst.dpDiv.find('select.ui-datepicker-year:first').replaceWith(inst.yearshtml);
741                                }
742                                origyearshtml = inst.yearshtml = null;
743                        }, 0);
744                }
745        },
746
747        /* Retrieve the size of left and top borders for an element.
748           @param  elem  (jQuery object) the element of interest
749           @return  (number[2]) the left and top borders */
750        _getBorders: function(elem) {
751                var convert = function(value) {
752                        return {thin: 1, medium: 2, thick: 3}[value] || value;
753                };
754                return [parseFloat(convert(elem.css('border-left-width'))),
755                        parseFloat(convert(elem.css('border-top-width')))];
756        },
757
758        /* Check positioning to remain on screen. */
759        _checkOffset: function(inst, offset, isFixed) {
760                var dpWidth = inst.dpDiv.outerWidth();
761                var dpHeight = inst.dpDiv.outerHeight();
762                var inputWidth = inst.input ? inst.input.outerWidth() : 0;
763                var inputHeight = inst.input ? inst.input.outerHeight() : 0;
764                var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
765                var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
766
767                offset.left -= (this._get(inst, 'isRTL') ? (dpWidth - inputWidth) : 0);
768                offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
769                offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
770
771                // now check if datepicker is showing outside window viewport - move to a better place if so.
772                offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
773                        Math.abs(offset.left + dpWidth - viewWidth) : 0);
774                offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
775                        Math.abs(dpHeight + inputHeight) : 0);
776
777                return offset;
778        },
779
780        /* Find an object's position on the screen. */
781        _findPos: function(obj) {
782                var inst = this._getInst(obj);
783                var isRTL = this._get(inst, 'isRTL');
784        while (obj && (obj.type == 'hidden' || obj.nodeType != 1 || $.expr.filters.hidden(obj))) {
785            obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
786        }
787        var position = $(obj).offset();
788            return [position.left, position.top];
789        },
790
791        /* Hide the date picker from view.
792           @param  input  element - the input field attached to the date picker */
793        _hideDatepicker: function(input) {
794                var inst = this._curInst;
795                if (!inst || (input && inst != $.data(input, PROP_NAME)))
796                        return;
797                if (this._datepickerShowing) {
798                        var showAnim = this._get(inst, 'showAnim');
799                        var duration = this._get(inst, 'duration');
800                        var self = this;
801                        var postProcess = function() {
802                                $.datepicker._tidyDialog(inst);
803                                self._curInst = null;
804                        };
805                        if ($.effects && $.effects[showAnim])
806                                inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
807                        else
808                                inst.dpDiv[(showAnim == 'slideDown' ? 'slideUp' :
809                                        (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
810                        if (!showAnim)
811                                postProcess();
812                        this._datepickerShowing = false;
813                        var onClose = this._get(inst, 'onClose');
814                        if (onClose)
815                                onClose.apply((inst.input ? inst.input[0] : null),
816                                        [(inst.input ? inst.input.val() : ''), inst]);
817                        this._lastInput = null;
818                        if (this._inDialog) {
819                                this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
820                                if ($.blockUI) {
821                                        $.unblockUI();
822                                        $('body').append(this.dpDiv);
823                                }
824                        }
825                        this._inDialog = false;
826                }
827        },
828
829        /* Tidy up after a dialog display. */
830        _tidyDialog: function(inst) {
831                inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker-calendar');
832        },
833
834        /* Close date picker if clicked elsewhere. */
835        _checkExternalClick: function(event) {
836                if (!$.datepicker._curInst)
837                        return;
838
839                var $target = $(event.target),
840                        inst = $.datepicker._getInst($target[0]);
841
842                if ( ( ( $target[0].id != $.datepicker._mainDivId &&
843                                $target.parents('#' + $.datepicker._mainDivId).length == 0 &&
844                                !$target.hasClass($.datepicker.markerClassName) &&
845                                !$target.hasClass($.datepicker._triggerClass) &&
846                                $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
847                        ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst != inst ) )
848                        $.datepicker._hideDatepicker();
849        },
850
851        /* Adjust one of the date sub-fields. */
852        _adjustDate: function(id, offset, period) {
853                var target = $(id);
854                var inst = this._getInst(target[0]);
855                if (this._isDisabledDatepicker(target[0])) {
856                        return;
857                }
858                this._adjustInstDate(inst, offset +
859                        (period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // undo positioning
860                        period);
861                this._updateDatepicker(inst);
862        },
863
864        /* Action for current link. */
865        _gotoToday: function(id) {
866                var target = $(id);
867                var inst = this._getInst(target[0]);
868                if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
869                        inst.selectedDay = inst.currentDay;
870                        inst.drawMonth = inst.selectedMonth = inst.currentMonth;
871                        inst.drawYear = inst.selectedYear = inst.currentYear;
872                }
873                else {
874                        var date = new Date();
875                        inst.selectedDay = date.getDate();
876                        inst.drawMonth = inst.selectedMonth = date.getMonth();
877                        inst.drawYear = inst.selectedYear = date.getFullYear();
878                }
879                this._notifyChange(inst);
880                this._adjustDate(target);
881        },
882
883        /* Action for selecting a new month/year. */
884        _selectMonthYear: function(id, select, period) {
885                var target = $(id);
886                var inst = this._getInst(target[0]);
887                inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
888                inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
889                        parseInt(select.options[select.selectedIndex].value,10);
890                this._notifyChange(inst);
891                this._adjustDate(target);
892        },
893
894        /* Action for selecting a day. */
895        _selectDay: function(id, month, year, td) {
896                var target = $(id);
897                if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
898                        return;
899                }
900                var inst = this._getInst(target[0]);
901                inst.selectedDay = inst.currentDay = $('a', td).html();
902                inst.selectedMonth = inst.currentMonth = month;
903                inst.selectedYear = inst.currentYear = year;
904                this._selectDate(id, this._formatDate(inst,
905                        inst.currentDay, inst.currentMonth, inst.currentYear));
906        },
907
908        /* Erase the input field and hide the date picker. */
909        _clearDate: function(id) {
910                var target = $(id);
911                var inst = this._getInst(target[0]);
912                this._selectDate(target, '');
913        },
914
915        /* Update the input field with the selected date. */
916        _selectDate: function(id, dateStr) {
917                var target = $(id);
918                var inst = this._getInst(target[0]);
919                dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
920                if (inst.input)
921                        inst.input.val(dateStr);
922                this._updateAlternate(inst);
923                var onSelect = this._get(inst, 'onSelect');
924                if (onSelect)
925                        onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);  // trigger custom callback
926                else if (inst.input)
927                        inst.input.trigger('change'); // fire the change event
928                if (inst.inline)
929                        this._updateDatepicker(inst);
930                else {
931                        this._hideDatepicker();
932                        this._lastInput = inst.input[0];
933                        if (typeof(inst.input[0]) != 'object')
934                                inst.input.focus(); // restore focus
935                        this._lastInput = null;
936                }
937        },
938
939        /* Update any alternate field to synchronise with the main field. */
940        _updateAlternate: function(inst) {
941                var altField = this._get(inst, 'altField');
942                if (altField) { // update alternate field too
943                        var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
944                        var date = this._getDate(inst);
945                        var dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
946                        $(altField).each(function() { $(this).val(dateStr); });
947                }
948        },
949
950        /* Set as beforeShowDay function to prevent selection of weekends.
951           @param  date  Date - the date to customise
952           @return [boolean, string] - is this date selectable?, what is its CSS class? */
953        noWeekends: function(date) {
954                var day = date.getDay();
955                return [(day > 0 && day < 6), ''];
956        },
957
958        /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
959           @param  date  Date - the date to get the week for
960           @return  number - the number of the week within the year that contains this date */
961        iso8601Week: function(date) {
962                var checkDate = new Date(date.getTime());
963                // Find Thursday of this week starting on Monday
964                checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
965                var time = checkDate.getTime();
966                checkDate.setMonth(0); // Compare with Jan 1
967                checkDate.setDate(1);
968                return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
969        },
970
971        /* Parse a string value into a date object.
972           See formatDate below for the possible formats.
973
974           @param  format    string - the expected format of the date
975           @param  value     string - the date in the above format
976           @param  settings  Object - attributes include:
977                             shortYearCutoff  number - the cutoff year for determining the century (optional)
978                             dayNamesShort    string[7] - abbreviated names of the days from Sunday (optional)
979                             dayNames         string[7] - names of the days from Sunday (optional)
980                             monthNamesShort  string[12] - abbreviated names of the months (optional)
981                             monthNames       string[12] - names of the months (optional)
982           @return  Date - the extracted date value or null if value is blank */
983        parseDate: function (format, value, settings) {
984                if (format == null || value == null)
985                        throw 'Invalid arguments';
986                value = (typeof value == 'object' ? value.toString() : value + '');
987                if (value == '')
988                        return null;
989                var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
990                shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
991                                new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
992                var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
993                var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
994                var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
995                var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
996                var year = -1;
997                var month = -1;
998                var day = -1;
999                var doy = -1;
1000                var literal = false;
1001                // Check whether a format character is doubled
1002                var lookAhead = function(match) {
1003                        var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
1004                        if (matches)
1005                                iFormat++;
1006                        return matches;
1007                };
1008                // Extract a number from the string value
1009                var getNumber = function(match) {
1010                        var isDoubled = lookAhead(match);
1011                        var size = (match == '@' ? 14 : (match == '!' ? 20 :
1012                                (match == 'y' && isDoubled ? 4 : (match == 'o' ? 3 : 2))));
1013                        var digits = new RegExp('^\\d{1,' + size + '}');
1014                        var num = value.substring(iValue).match(digits);
1015                        if (!num)
1016                                throw 'Missing number at position ' + iValue;
1017                        iValue += num[0].length;
1018                        return parseInt(num[0], 10);
1019                };
1020                // Extract a name from the string value and convert to an index
1021                var getName = function(match, shortNames, longNames) {
1022                        var names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
1023                                return [ [k, v] ];
1024                        }).sort(function (a, b) {
1025                                return -(a[1].length - b[1].length);
1026                        });
1027                        var index = -1;
1028                        $.each(names, function (i, pair) {
1029                                var name = pair[1];
1030                                if (value.substr(iValue, name.length).toLowerCase() == name.toLowerCase()) {
1031                                        index = pair[0];
1032                                        iValue += name.length;
1033                                        return false;
1034                                }
1035                        });
1036                        if (index != -1)
1037                                return index + 1;
1038                        else
1039                                throw 'Unknown name at position ' + iValue;
1040                };
1041                // Confirm that a literal character matches the string value
1042                var checkLiteral = function() {
1043                        if (value.charAt(iValue) != format.charAt(iFormat))
1044                                throw 'Unexpected literal at position ' + iValue;
1045                        iValue++;
1046                };
1047                var iValue = 0;
1048                for (var iFormat = 0; iFormat < format.length; iFormat++) {
1049                        if (literal)
1050                                if (format.charAt(iFormat) == "'" && !lookAhead("'"))
1051                                        literal = false;
1052                                else
1053                                        checkLiteral();
1054                        else
1055                                switch (format.charAt(iFormat)) {
1056                                        case 'd':
1057                                                day = getNumber('d');
1058                                                break;
1059                                        case 'D':
1060                                                getName('D', dayNamesShort, dayNames);
1061                                                break;
1062                                        case 'o':
1063                                                doy = getNumber('o');
1064                                                break;
1065                                        case 'm':
1066                                                month = getNumber('m');
1067                                                break;
1068                                        case 'M':
1069                                                month = getName('M', monthNamesShort, monthNames);
1070                                                break;
1071                                        case 'y':
1072                                                year = getNumber('y');
1073                                                break;
1074                                        case '@':
1075                                                var date = new Date(getNumber('@'));
1076                                                year = date.getFullYear();
1077                                                month = date.getMonth() + 1;
1078                                                day = date.getDate();
1079                                                break;
1080                                        case '!':
1081                                                var date = new Date((getNumber('!') - this._ticksTo1970) / 10000);
1082                                                year = date.getFullYear();
1083                                                month = date.getMonth() + 1;
1084                                                day = date.getDate();
1085                                                break;
1086                                        case "'":
1087                                                if (lookAhead("'"))
1088                                                        checkLiteral();
1089                                                else
1090                                                        literal = true;
1091                                                break;
1092                                        default:
1093                                                checkLiteral();
1094                                }
1095                }
1096                if (iValue < value.length){
1097                        throw "Extra/unparsed characters found in date: " + value.substring(iValue);
1098                }
1099                if (year == -1)
1100                        year = new Date().getFullYear();
1101                else if (year < 100)
1102                        year += new Date().getFullYear() - new Date().getFullYear() % 100 +
1103                                (year <= shortYearCutoff ? 0 : -100);
1104                if (doy > -1) {
1105                        month = 1;
1106                        day = doy;
1107                        do {
1108                                var dim = this._getDaysInMonth(year, month - 1);
1109                                if (day <= dim)
1110                                        break;
1111                                month++;
1112                                day -= dim;
1113                        } while (true);
1114                }
1115                var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
1116                if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
1117                        throw 'Invalid date'; // E.g. 31/02/00
1118                return date;
1119        },
1120
1121        /* Standard date formats. */
1122        ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
1123        COOKIE: 'D, dd M yy',
1124        ISO_8601: 'yy-mm-dd',
1125        RFC_822: 'D, d M y',
1126        RFC_850: 'DD, dd-M-y',
1127        RFC_1036: 'D, d M y',
1128        RFC_1123: 'D, d M yy',
1129        RFC_2822: 'D, d M yy',
1130        RSS: 'D, d M y', // RFC 822
1131        TICKS: '!',
1132        TIMESTAMP: '@',
1133        W3C: 'yy-mm-dd', // ISO 8601
1134
1135        _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
1136                Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
1137
1138        /* Format a date object into a string value.
1139           The format can be combinations of the following:
1140           d  - day of month (no leading zero)
1141           dd - day of month (two digit)
1142           o  - day of year (no leading zeros)
1143           oo - day of year (three digit)
1144           D  - day name short
1145           DD - day name long
1146           m  - month of year (no leading zero)
1147           mm - month of year (two digit)
1148           M  - month name short
1149           MM - month name long
1150           y  - year (two digit)
1151           yy - year (four digit)
1152           @ - Unix timestamp (ms since 01/01/1970)
1153           ! - Windows ticks (100ns since 01/01/0001)
1154           '...' - literal text
1155           '' - single quote
1156
1157           @param  format    string - the desired format of the date
1158           @param  date      Date - the date value to format
1159           @param  settings  Object - attributes include:
1160                             dayNamesShort    string[7] - abbreviated names of the days from Sunday (optional)
1161                             dayNames         string[7] - names of the days from Sunday (optional)
1162                             monthNamesShort  string[12] - abbreviated names of the months (optional)
1163                             monthNames       string[12] - names of the months (optional)
1164           @return  string - the date in the above format */
1165        formatDate: function (format, date, settings) {
1166                if (!date)
1167                        return '';
1168                var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
1169                var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
1170                var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
1171                var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
1172                // Check whether a format character is doubled
1173                var lookAhead = function(match) {
1174                        var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
1175                        if (matches)
1176                                iFormat++;
1177                        return matches;
1178                };
1179                // Format a number, with leading zero if necessary
1180                var formatNumber = function(match, value, len) {
1181                        var num = '' + value;
1182                        if (lookAhead(match))
1183                                while (num.length < len)
1184                                        num = '0' + num;
1185                        return num;
1186                };
1187                // Format a name, short or long as requested
1188                var formatName = function(match, value, shortNames, longNames) {
1189                        return (lookAhead(match) ? longNames[value] : shortNames[value]);
1190                };
1191                var output = '';
1192                var literal = false;
1193                if (date)
1194                        for (var iFormat = 0; iFormat < format.length; iFormat++) {
1195                                if (literal)
1196                                        if (format.charAt(iFormat) == "'" && !lookAhead("'"))
1197                                                literal = false;
1198                                        else
1199                                                output += format.charAt(iFormat);
1200                                else
1201                                        switch (format.charAt(iFormat)) {
1202                                                case 'd':
1203                                                        output += formatNumber('d', date.getDate(), 2);
1204                                                        break;
1205                                                case 'D':
1206                                                        output += formatName('D', date.getDay(), dayNamesShort, dayNames);
1207                                                        break;
1208                                                case 'o':
1209                                                        output += formatNumber('o',
1210                                                                Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
1211                                                        break;
1212                                                case 'm':
1213                                                        output += formatNumber('m', date.getMonth() + 1, 2);
1214                                                        break;
1215                                                case 'M':
1216                                                        output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
1217                                                        break;
1218                                                case 'y':
1219                                                        output += (lookAhead('y') ? date.getFullYear() :
1220                                                                (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
1221                                                        break;
1222                                                case '@':
1223                                                        output += date.getTime();
1224                                                        break;
1225                                                case '!':
1226                                                        output += date.getTime() * 10000 + this._ticksTo1970;
1227                                                        break;
1228                                                case "'":
1229                                                        if (lookAhead("'"))
1230                                                                output += "'";
1231                                                        else
1232                                                                literal = true;
1233                                                        break;
1234                                                default:
1235                                                        output += format.charAt(iFormat);
1236                                        }
1237                        }
1238                return output;
1239        },
1240
1241        /* Extract all possible characters from the date format. */
1242        _possibleChars: function (format) {
1243                var chars = '';
1244                var literal = false;
1245                // Check whether a format character is doubled
1246                var lookAhead = function(match) {
1247                        var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
1248                        if (matches)
1249                                iFormat++;
1250                        return matches;
1251                };
1252                for (var iFormat = 0; iFormat < format.length; iFormat++)
1253                        if (literal)
1254                                if (format.charAt(iFormat) == "'" && !lookAhead("'"))
1255                                        literal = false;
1256                                else
1257                                        chars += format.charAt(iFormat);
1258                        else
1259                                switch (format.charAt(iFormat)) {
1260                                        case 'd': case 'm': case 'y': case '@':
1261                                                chars += '0123456789';
1262                                                break;
1263                                        case 'D': case 'M':
1264                                                return null; // Accept anything
1265                                        case "'":
1266                                                if (lookAhead("'"))
1267                                                        chars += "'";
1268                                                else
1269                                                        literal = true;
1270                                                break;
1271                                        default:
1272                                                chars += format.charAt(iFormat);
1273                                }
1274                return chars;
1275        },
1276
1277        /* Get a setting value, defaulting if necessary. */
1278        _get: function(inst, name) {
1279                return inst.settings[name] !== undefined ?
1280                        inst.settings[name] : this._defaults[name];
1281        },
1282
1283        /* Parse existing date and initialise date picker. */
1284        _setDateFromField: function(inst, noDefault) {
1285                if (inst.input.val() == inst.lastVal) {
1286                        return;
1287                }
1288                var dateFormat = this._get(inst, 'dateFormat');
1289                var dates = inst.lastVal = inst.input ? inst.input.val() : null;
1290                var date, defaultDate;
1291                date = defaultDate = this._getDefaultDate(inst);
1292                var settings = this._getFormatConfig(inst);
1293                try {
1294                        date = this.parseDate(dateFormat, dates, settings) || defaultDate;
1295                } catch (event) {
1296                        this.log(event);
1297                        dates = (noDefault ? '' : dates);
1298                }
1299                inst.selectedDay = date.getDate();
1300                inst.drawMonth = inst.selectedMonth = date.getMonth();
1301                inst.drawYear = inst.selectedYear = date.getFullYear();
1302                inst.currentDay = (dates ? date.getDate() : 0);
1303                inst.currentMonth = (dates ? date.getMonth() : 0);
1304                inst.currentYear = (dates ? date.getFullYear() : 0);
1305                this._adjustInstDate(inst);
1306        },
1307
1308        /* Retrieve the default date shown on opening. */
1309        _getDefaultDate: function(inst) {
1310                return this._restrictMinMax(inst,
1311                        this._determineDate(inst, this._get(inst, 'defaultDate'), new Date()));
1312        },
1313
1314        /* A date may be specified as an exact value or a relative one. */
1315        _determineDate: function(inst, date, defaultDate) {
1316                var offsetNumeric = function(offset) {
1317                        var date = new Date();
1318                        date.setDate(date.getDate() + offset);
1319                        return date;
1320                };
1321                var offsetString = function(offset) {
1322                        try {
1323                                return $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
1324                                        offset, $.datepicker._getFormatConfig(inst));
1325                        }
1326                        catch (e) {
1327                                // Ignore
1328                        }
1329                        var date = (offset.toLowerCase().match(/^c/) ?
1330                                $.datepicker._getDate(inst) : null) || new Date();
1331                        var year = date.getFullYear();
1332                        var month = date.getMonth();
1333                        var day = date.getDate();
1334                        var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
1335                        var matches = pattern.exec(offset);
1336                        while (matches) {
1337                                switch (matches[2] || 'd') {
1338                                        case 'd' : case 'D' :
1339                                                day += parseInt(matches[1],10); break;
1340                                        case 'w' : case 'W' :
1341                                                day += parseInt(matches[1],10) * 7; break;
1342                                        case 'm' : case 'M' :
1343                                                month += parseInt(matches[1],10);
1344                                                day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
1345                                                break;
1346                                        case 'y': case 'Y' :
1347                                                year += parseInt(matches[1],10);
1348                                                day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
1349                                                break;
1350                                }
1351                                matches = pattern.exec(offset);
1352                        }
1353                        return new Date(year, month, day);
1354                };
1355                var newDate = (date == null || date === '' ? defaultDate : (typeof date == 'string' ? offsetString(date) :
1356                        (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
1357                newDate = (newDate && newDate.toString() == 'Invalid Date' ? defaultDate : newDate);
1358                if (newDate) {
1359                        newDate.setHours(0);
1360                        newDate.setMinutes(0);
1361                        newDate.setSeconds(0);
1362                        newDate.setMilliseconds(0);
1363                }
1364                return this._daylightSavingAdjust(newDate);
1365        },
1366
1367        /* Handle switch to/from daylight saving.
1368           Hours may be non-zero on daylight saving cut-over:
1369           > 12 when midnight changeover, but then cannot generate
1370           midnight datetime, so jump to 1AM, otherwise reset.
1371           @param  date  (Date) the date to check
1372           @return  (Date) the corrected date */
1373        _daylightSavingAdjust: function(date) {
1374                if (!date) return null;
1375                date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
1376                return date;
1377        },
1378
1379        /* Set the date(s) directly. */
1380        _setDate: function(inst, date, noChange) {
1381                var clear = !date;
1382                var origMonth = inst.selectedMonth;
1383                var origYear = inst.selectedYear;
1384                var newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
1385                inst.selectedDay = inst.currentDay = newDate.getDate();
1386                inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
1387                inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
1388                if ((origMonth != inst.selectedMonth || origYear != inst.selectedYear) && !noChange)
1389                        this._notifyChange(inst);
1390                this._adjustInstDate(inst);
1391                if (inst.input) {
1392                        inst.input.val(clear ? '' : this._formatDate(inst));
1393                }
1394        },
1395
1396        /* Retrieve the date(s) directly. */
1397        _getDate: function(inst) {
1398                var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null :
1399                        this._daylightSavingAdjust(new Date(
1400                        inst.currentYear, inst.currentMonth, inst.currentDay)));
1401                        return startDate;
1402        },
1403
1404        /* Generate the HTML for the current state of the date picker. */
1405        _generateHTML: function(inst) {
1406                var today = new Date();
1407                today = this._daylightSavingAdjust(
1408                        new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time
1409                var isRTL = this._get(inst, 'isRTL');
1410                var showButtonPanel = this._get(inst, 'showButtonPanel');
1411                var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
1412                var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
1413                var numMonths = this._getNumberOfMonths(inst);
1414                var showCurrentAtPos = this._get(inst, 'showCurrentAtPos');
1415                var stepMonths = this._get(inst, 'stepMonths');
1416                var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
1417                var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
1418                        new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
1419                var minDate = this._getMinMaxDate(inst, 'min');
1420                var maxDate = this._getMinMaxDate(inst, 'max');
1421                var drawMonth = inst.drawMonth - showCurrentAtPos;
1422                var drawYear = inst.drawYear;
1423                if (drawMonth < 0) {
1424                        drawMonth += 12;
1425                        drawYear--;
1426                }
1427                if (maxDate) {
1428                        var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
1429                                maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
1430                        maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
1431                        while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
1432                                drawMonth--;
1433                                if (drawMonth < 0) {
1434                                        drawMonth = 11;
1435                                        drawYear--;
1436                                }
1437                        }
1438                }
1439                inst.drawMonth = drawMonth;
1440                inst.drawYear = drawYear;
1441                var prevText = this._get(inst, 'prevText');
1442                prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
1443                        this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
1444                        this._getFormatConfig(inst)));
1445                var prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
1446                        '<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_' + dpuuid +
1447                        '.datepicker._adjustDate(\'#' + inst.id + '\', -' + stepMonths + ', \'M\');"' +
1448                        ' title="' + prevText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>' :
1449                        (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>'));
1450                var nextText = this._get(inst, 'nextText');
1451                nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
1452                        this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
1453                        this._getFormatConfig(inst)));
1454                var next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
1455                        '<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_' + dpuuid +
1456                        '.datepicker._adjustDate(\'#' + inst.id + '\', +' + stepMonths + ', \'M\');"' +
1457                        ' title="' + nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>' :
1458                        (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>'));
1459                var currentText = this._get(inst, 'currentText');
1460                var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today);
1461                currentText = (!navigationAsDateFormat ? currentText :
1462                        this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
1463                var controls = (!inst.inline ? '<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_' + dpuuid +
1464                        '.datepicker._hideDatepicker();">' + this._get(inst, 'closeText') + '</button>' : '');
1465                var buttonPanel = (showButtonPanel) ? '<div class="ui-datepicker-buttonpane ui-widget-content">' + (isRTL ? controls : '') +
1466                        (this._isInRange(inst, gotoDate) ? '<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_' + dpuuid +
1467                        '.datepicker._gotoToday(\'#' + inst.id + '\');"' +
1468                        '>' + currentText + '</button>' : '') + (isRTL ? '' : controls) + '</div>' : '';
1469                var firstDay = parseInt(this._get(inst, 'firstDay'),10);
1470                firstDay = (isNaN(firstDay) ? 0 : firstDay);
1471                var showWeek = this._get(inst, 'showWeek');
1472                var dayNames = this._get(inst, 'dayNames');
1473                var dayNamesShort = this._get(inst, 'dayNamesShort');
1474                var dayNamesMin = this._get(inst, 'dayNamesMin');
1475                var monthNames = this._get(inst, 'monthNames');
1476                var monthNamesShort = this._get(inst, 'monthNamesShort');
1477                var beforeShowDay = this._get(inst, 'beforeShowDay');
1478                var showOtherMonths = this._get(inst, 'showOtherMonths');
1479                var selectOtherMonths = this._get(inst, 'selectOtherMonths');
1480                var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
1481                var defaultDate = this._getDefaultDate(inst);
1482                var html = '';
1483                for (var row = 0; row < numMonths[0]; row++) {
1484                        var group = '';
1485                        this.maxRows = 4;
1486                        for (var col = 0; col < numMonths[1]; col++) {
1487                                var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
1488                                var cornerClass = ' ui-corner-all';
1489                                var calender = '';
1490                                if (isMultiMonth) {
1491                                        calender += '<div class="ui-datepicker-group';
1492                                        if (numMonths[1] > 1)
1493                                                switch (col) {
1494                                                        case 0: calender += ' ui-datepicker-group-first';
1495                                                                cornerClass = ' ui-corner-' + (isRTL ? 'right' : 'left'); break;
1496                                                        case numMonths[1]-1: calender += ' ui-datepicker-group-last';
1497                                                                cornerClass = ' ui-corner-' + (isRTL ? 'left' : 'right'); break;
1498                                                        default: calender += ' ui-datepicker-group-middle'; cornerClass = ''; break;
1499                                                }
1500                                        calender += '">';
1501                                }
1502                                calender += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix' + cornerClass + '">' +
1503                                        (/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') +
1504                                        (/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') +
1505                                        this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
1506                                        row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
1507                                        '</div><table class="ui-datepicker-calendar"><thead>' +
1508                                        '<tr>';
1509                                var thead = (showWeek ? '<th class="ui-datepicker-week-col">' + this._get(inst, 'weekHeader') + '</th>' : '');
1510                                for (var dow = 0; dow < 7; dow++) { // days of the week
1511                                        var day = (dow + firstDay) % 7;
1512                                        thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end"' : '') + '>' +
1513                                                '<span title="' + dayNames[day] + '">' + dayNamesMin[day] + '</span></th>';
1514                                }
1515                                calender += thead + '</tr></thead><tbody>';
1516                                var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
1517                                if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
1518                                        inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
1519                                var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
1520                                var curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
1521                                var numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
1522                                this.maxRows = numRows;
1523                                var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
1524                                for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
1525                                        calender += '<tr>';
1526                                        var tbody = (!showWeek ? '' : '<td class="ui-datepicker-week-col">' +
1527                                                this._get(inst, 'calculateWeek')(printDate) + '</td>');
1528                                        for (var dow = 0; dow < 7; dow++) { // create date picker days
1529                                                var daySettings = (beforeShowDay ?
1530                                                        beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
1531                                                var otherMonth = (printDate.getMonth() != drawMonth);
1532                                                var unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
1533                                                        (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
1534                                                tbody += '<td class="' +
1535                                                        ((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end' : '') + // highlight weekends
1536                                                        (otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months
1537                                                        ((printDate.getTime() == selectedDate.getTime() && drawMonth == inst.selectedMonth && inst._keyEvent) || // user pressed key
1538                                                        (defaultDate.getTime() == printDate.getTime() && defaultDate.getTime() == selectedDate.getTime()) ?
1539                                                        // or defaultDate is current printedDate and defaultDate is selectedDate
1540                                                        ' ' + this._dayOverClass : '') + // highlight selected day
1541                                                        (unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled': '') +  // highlight unselectable days
1542                                                        (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
1543                                                        (printDate.getTime() == currentDate.getTime() ? ' ' + this._currentClass : '') + // highlight selected day
1544                                                        (printDate.getTime() == today.getTime() ? ' ui-datepicker-today' : '')) + '"' + // highlight today (if different)
1545                                                        ((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
1546                                                        (unselectable ? '' : ' onclick="DP_jQuery_' + dpuuid + '.datepicker._selectDay(\'#' +
1547                                                        inst.id + '\',' + printDate.getMonth() + ',' + printDate.getFullYear() + ', this);return false;"') + '>' + // actions
1548                                                        (otherMonth && !showOtherMonths ? '&#xa0;' : // display for other months
1549                                                        (unselectable ? '<span class="ui-state-default">' + printDate.getDate() + '</span>' : '<a class="ui-state-default' +
1550                                                        (printDate.getTime() == today.getTime() ? ' ui-state-highlight' : '') +
1551                                                        (printDate.getTime() == currentDate.getTime() ? ' ui-state-active' : '') + // highlight selected day
1552                                                        (otherMonth ? ' ui-priority-secondary' : '') + // distinguish dates from other months
1553                                                        '" href="#">' + printDate.getDate() + '</a>')) + '</td>'; // display selectable date
1554                                                printDate.setDate(printDate.getDate() + 1);
1555                                                printDate = this._daylightSavingAdjust(printDate);
1556                                        }
1557                                        calender += tbody + '</tr>';
1558                                }
1559                                drawMonth++;
1560                                if (drawMonth > 11) {
1561                                        drawMonth = 0;
1562                                        drawYear++;
1563                                }
1564                                calender += '</tbody></table>' + (isMultiMonth ? '</div>' +
1565                                                        ((numMonths[0] > 0 && col == numMonths[1]-1) ? '<div class="ui-datepicker-row-break"></div>' : '') : '');
1566                                group += calender;
1567                        }
1568                        html += group;
1569                }
1570                html += buttonPanel + ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ?
1571                        '<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>' : '');
1572                inst._keyEvent = false;
1573                return html;
1574        },
1575
1576        /* Generate the month and year header. */
1577        _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
1578                        secondary, monthNames, monthNamesShort) {
1579                var changeMonth = this._get(inst, 'changeMonth');
1580                var changeYear = this._get(inst, 'changeYear');
1581                var showMonthAfterYear = this._get(inst, 'showMonthAfterYear');
1582                var html = '<div class="ui-datepicker-title">';
1583                var monthHtml = '';
1584                // month selection
1585                if (secondary || !changeMonth)
1586                        monthHtml += '<span class="ui-datepicker-month">' + monthNames[drawMonth] + '</span>';
1587                else {
1588                        var inMinYear = (minDate && minDate.getFullYear() == drawYear);
1589                        var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
1590                        monthHtml += '<select class="ui-datepicker-month" ' +
1591                                'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'M\');" ' +
1592                                '>';
1593                        for (var month = 0; month < 12; month++) {
1594                                if ((!inMinYear || month >= minDate.getMonth()) &&
1595                                                (!inMaxYear || month <= maxDate.getMonth()))
1596                                        monthHtml += '<option value="' + month + '"' +
1597                                                (month == drawMonth ? ' selected="selected"' : '') +
1598                                                '>' + monthNamesShort[month] + '</option>';
1599                        }
1600                        monthHtml += '</select>';
1601                }
1602                if (!showMonthAfterYear)
1603                        html += monthHtml + (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '');
1604                // year selection
1605                if ( !inst.yearshtml ) {
1606                        inst.yearshtml = '';
1607                        if (secondary || !changeYear)
1608                                html += '<span class="ui-datepicker-year">' + drawYear + '</span>';
1609                        else {
1610                                // determine range of years to display
1611                                var years = this._get(inst, 'yearRange').split(':');
1612                                var thisYear = new Date().getFullYear();
1613                                var determineYear = function(value) {
1614                                        var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) :
1615                                                (value.match(/[+-].*/) ? thisYear + parseInt(value, 10) :
1616                                                parseInt(value, 10)));
1617                                        return (isNaN(year) ? thisYear : year);
1618                                };
1619                                var year = determineYear(years[0]);
1620                                var endYear = Math.max(year, determineYear(years[1] || ''));
1621                                year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
1622                                endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
1623                                inst.yearshtml += '<select class="ui-datepicker-year" ' +
1624                                        'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'Y\');" ' +
1625                                        '>';
1626                                for (; year <= endYear; year++) {
1627                                        inst.yearshtml += '<option value="' + year + '"' +
1628                                                (year == drawYear ? ' selected="selected"' : '') +
1629                                                '>' + year + '</option>';
1630                                }
1631                                inst.yearshtml += '</select>';
1632                               
1633                                html += inst.yearshtml;
1634                                inst.yearshtml = null;
1635                        }
1636                }
1637                html += this._get(inst, 'yearSuffix');
1638                if (showMonthAfterYear)
1639                        html += (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '') + monthHtml;
1640                html += '</div>'; // Close datepicker_header
1641                return html;
1642        },
1643
1644        /* Adjust one of the date sub-fields. */
1645        _adjustInstDate: function(inst, offset, period) {
1646                var year = inst.drawYear + (period == 'Y' ? offset : 0);
1647                var month = inst.drawMonth + (period == 'M' ? offset : 0);
1648                var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) +
1649                        (period == 'D' ? offset : 0);
1650                var date = this._restrictMinMax(inst,
1651                        this._daylightSavingAdjust(new Date(year, month, day)));
1652                inst.selectedDay = date.getDate();
1653                inst.drawMonth = inst.selectedMonth = date.getMonth();
1654                inst.drawYear = inst.selectedYear = date.getFullYear();
1655                if (period == 'M' || period == 'Y')
1656                        this._notifyChange(inst);
1657        },
1658
1659        /* Ensure a date is within any min/max bounds. */
1660        _restrictMinMax: function(inst, date) {
1661                var minDate = this._getMinMaxDate(inst, 'min');
1662                var maxDate = this._getMinMaxDate(inst, 'max');
1663                var newDate = (minDate && date < minDate ? minDate : date);
1664                newDate = (maxDate && newDate > maxDate ? maxDate : newDate);
1665                return newDate;
1666        },
1667
1668        /* Notify change of month/year. */
1669        _notifyChange: function(inst) {
1670                var onChange = this._get(inst, 'onChangeMonthYear');
1671                if (onChange)
1672                        onChange.apply((inst.input ? inst.input[0] : null),
1673                                [inst.selectedYear, inst.selectedMonth + 1, inst]);
1674        },
1675
1676        /* Determine the number of months to show. */
1677        _getNumberOfMonths: function(inst) {
1678                var numMonths = this._get(inst, 'numberOfMonths');
1679                return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
1680        },
1681
1682        /* Determine the current maximum date - ensure no time components are set. */
1683        _getMinMaxDate: function(inst, minMax) {
1684                return this._determineDate(inst, this._get(inst, minMax + 'Date'), null);
1685        },
1686
1687        /* Find the number of days in a given month. */
1688        _getDaysInMonth: function(year, month) {
1689                return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
1690        },
1691
1692        /* Find the day of the week of the first of a month. */
1693        _getFirstDayOfMonth: function(year, month) {
1694                return new Date(year, month, 1).getDay();
1695        },
1696
1697        /* Determines if we should allow a "next/prev" month display change. */
1698        _canAdjustMonth: function(inst, offset, curYear, curMonth) {
1699                var numMonths = this._getNumberOfMonths(inst);
1700                var date = this._daylightSavingAdjust(new Date(curYear,
1701                        curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
1702                if (offset < 0)
1703                        date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
1704                return this._isInRange(inst, date);
1705        },
1706
1707        /* Is the given date in the accepted range? */
1708        _isInRange: function(inst, date) {
1709                var minDate = this._getMinMaxDate(inst, 'min');
1710                var maxDate = this._getMinMaxDate(inst, 'max');
1711                return ((!minDate || date.getTime() >= minDate.getTime()) &&
1712                        (!maxDate || date.getTime() <= maxDate.getTime()));
1713        },
1714
1715        /* Provide the configuration settings for formatting/parsing. */
1716        _getFormatConfig: function(inst) {
1717                var shortYearCutoff = this._get(inst, 'shortYearCutoff');
1718                shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
1719                        new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
1720                return {shortYearCutoff: shortYearCutoff,
1721                        dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
1722                        monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
1723        },
1724
1725        /* Format the given date for display. */
1726        _formatDate: function(inst, day, month, year) {
1727                if (!day) {
1728                        inst.currentDay = inst.selectedDay;
1729                        inst.currentMonth = inst.selectedMonth;
1730                        inst.currentYear = inst.selectedYear;
1731                }
1732                var date = (day ? (typeof day == 'object' ? day :
1733                        this._daylightSavingAdjust(new Date(year, month, day))) :
1734                        this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
1735                return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
1736        }
1737});
1738
1739/*
1740 * Bind hover events for datepicker elements.
1741 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
1742 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
1743 */
1744function bindHover(dpDiv) {
1745        var selector = 'button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a';
1746        return dpDiv.bind('mouseout', function(event) {
1747                        var elem = $( event.target ).closest( selector );
1748                        if ( !elem.length ) {
1749                                return;
1750                        }
1751                        elem.removeClass( "ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover" );
1752                })
1753                .bind('mouseover', function(event) {
1754                        var elem = $( event.target ).closest( selector );
1755                        if ($.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0]) ||
1756                                        !elem.length ) {
1757                                return;
1758                        }
1759                        elem.parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
1760                        elem.addClass('ui-state-hover');
1761                        if (elem.hasClass('ui-datepicker-prev')) elem.addClass('ui-datepicker-prev-hover');
1762                        if (elem.hasClass('ui-datepicker-next')) elem.addClass('ui-datepicker-next-hover');
1763                });
1764}
1765
1766/* jQuery extend now ignores nulls! */
1767function extendRemove(target, props) {
1768        $.extend(target, props);
1769        for (var name in props)
1770                if (props[name] == null || props[name] == undefined)
1771                        target[name] = props[name];
1772        return target;
1773};
1774
1775/* Determine whether an object is an array. */
1776function isArray(a) {
1777        return (a && (($.browser.safari && typeof a == 'object' && a.length) ||
1778                (a.constructor && a.constructor.toString().match(/\Array\(\)/))));
1779};
1780
1781/* Invoke the datepicker functionality.
1782   @param  options  string - a command, optionally followed by additional parameters or
1783                    Object - settings for attaching new datepicker functionality
1784   @return  jQuery object */
1785$.fn.datepicker = function(options){
1786       
1787        /* Verify an empty collection wasn't passed - Fixes #6976 */
1788        if ( !this.length ) {
1789                return this;
1790        }
1791       
1792        /* Initialise the date picker. */
1793        if (!$.datepicker.initialized) {
1794                $(document).mousedown($.datepicker._checkExternalClick).
1795                        find('body').append($.datepicker.dpDiv);
1796                $.datepicker.initialized = true;
1797        }
1798
1799        var otherArgs = Array.prototype.slice.call(arguments, 1);
1800        if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate' || options == 'widget'))
1801                return $.datepicker['_' + options + 'Datepicker'].
1802                        apply($.datepicker, [this[0]].concat(otherArgs));
1803        if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
1804                return $.datepicker['_' + options + 'Datepicker'].
1805                        apply($.datepicker, [this[0]].concat(otherArgs));
1806        return this.each(function() {
1807                typeof options == 'string' ?
1808                        $.datepicker['_' + options + 'Datepicker'].
1809                                apply($.datepicker, [this].concat(otherArgs)) :
1810                        $.datepicker._attachDatepicker(this, options);
1811        });
1812};
1813
1814$.datepicker = new Datepicker(); // singleton instance
1815$.datepicker.initialized = false;
1816$.datepicker.uuid = new Date().getTime();
1817$.datepicker.version = "1.8.17";
1818
1819// Workaround for #4055
1820// Add another global to avoid noConflict issues with inline event handlers
1821window['DP_jQuery_' + dpuuid] = $;
1822
1823})(jQuery);
Note: See TracBrowser for help on using the repository browser.