source: trunk/themes/default/js/ui/jquery.ui.datepicker.js @ 9559

Last change on this file since 9559 was 9559, checked in by patdenice, 14 years ago

Update jQuery UI to 1.8.10.
Improve jquery ui management in template class.

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