2 * jQuery UI Datepicker 1.10.2
5 * Copyright 2013 jQuery Foundation and other contributors
6 * Released under the MIT license.
7 * http://jquery.org/license
9 * http://api.jqueryui.com/datepicker/
14 (function( $, undefined ) {
16 $.extend($.ui, { datepicker: { version: "1.10.2" } });
18 var PROP_NAME = "datepicker",
19 dpuuid = new Date().getTime(),
22 /* Date picker manager.
23 Use the singleton instance of this class, $.datepicker, to interact with the date picker.
24 Settings for (groups of) date pickers are maintained in an instance object,
25 allowing multiple different settings on the same page. */
27 function Datepicker() {
28 this._curInst = null; // The current instance in use
29 this._keyEvent = false; // If the last event was a key event
30 this._disabledInputs = []; // List of date picker inputs that have been disabled
31 this._datepickerShowing = false; // True if the popup picker is showing , false if not
32 this._inDialog = false; // True if showing within a "dialog", false if not
33 this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
34 this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
35 this._appendClass = "ui-datepicker-append"; // The name of the append marker class
36 this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
37 this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
38 this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
39 this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
40 this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
41 this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
42 this.regional = []; // Available regional settings, indexed by language code
43 this.regional[""] = { // Default regional settings
44 closeText: "Done", // Display text for close link
45 prevText: "Prev", // Display text for previous month link
46 nextText: "Next", // Display text for next month link
47 currentText: "Today", // Display text for current month link
48 monthNames: ["January","February","March","April","May","June",
49 "July","August","September","October","November","December"], // Names of months for drop-down and formatting
50 monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting
51 dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting
52 dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting
53 dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday
54 weekHeader: "Wk", // Column header for week of the year
55 dateFormat: "mm/dd/yy", // See format options on parseDate
56 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
57 isRTL: false, // True if right-to-left language, false if left-to-right
58 showMonthAfterYear: false, // True if the year select precedes month, false for month then year
59 yearSuffix: "" // Additional text to append to the year in the month headers
61 this._defaults = { // Global defaults for all the date picker instances
62 showOn: "focus", // "focus" for popup on focus,
63 // "button" for trigger button, or "both" for either
64 showAnim: "fadeIn", // Name of jQuery animation for popup
65 showOptions: {}, // Options for enhanced animations
66 defaultDate: null, // Used when field is blank: actual date,
67 // +/-number for offset from today, null for today
68 appendText: "", // Display text following the input box, e.g. showing the format
69 buttonText: "...", // Text for trigger button
70 buttonImage: "", // URL for trigger button image
71 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
72 hideIfNoPrevNext: false, // True to hide next/previous month links
73 // if not applicable, false to just disable them
74 navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
75 gotoCurrent: false, // True if today link goes back to current selection instead
76 changeMonth: false, // True if month can be selected directly, false if only prev/next
77 changeYear: false, // True if year can be selected directly, false if only prev/next
78 yearRange: "c-10:c+10", // Range of years to display in drop-down,
79 // either relative to today's year (-nn:+nn), relative to currently displayed year
80 // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
81 showOtherMonths: false, // True to show dates in other months, false to leave blank
82 selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
83 showWeek: false, // True to show week of the year, false to not show it
84 calculateWeek: this.iso8601Week, // How to calculate the week of the year,
85 // takes a Date and returns the number of the week for it
86 shortYearCutoff: "+10", // Short year values < this are in the current century,
87 // > this are in the previous century,
88 // string value starting with "+" for current year + value
89 minDate: null, // The earliest selectable date, or null for no limit
90 maxDate: null, // The latest selectable date, or null for no limit
91 duration: "fast", // Duration of display/closure
92 beforeShowDay: null, // Function that takes a date and returns an array with
93 // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
94 // [2] = cell title (optional), e.g. $.datepicker.noWeekends
95 beforeShow: null, // Function that takes an input field and
96 // returns a set of custom settings for the date picker
97 onSelect: null, // Define a callback function when a date is selected
98 onChangeMonthYear: null, // Define a callback function when the month or year is changed
99 onClose: null, // Define a callback function when the datepicker is closed
100 numberOfMonths: 1, // Number of months to show at a time
101 showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
102 stepMonths: 1, // Number of months to step back/forward
103 stepBigMonths: 12, // Number of months to step back/forward for the big links
104 altField: "", // Selector for an alternate field to store selected dates into
105 altFormat: "", // The date format to use for the alternate field
106 constrainInput: true, // The input is constrained by the current date format
107 showButtonPanel: false, // True to show button panel, false to not show it
108 autoSize: false, // True to size the input for the date format, false to leave as is
109 disabled: false // The initial disabled state
111 $.extend(this._defaults, this.regional[""]);
112 this.dpDiv = bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"));
115 $.extend(Datepicker.prototype, {
116 /* Class name added to elements to indicate already configured with a date picker. */
117 markerClassName: "hasDatepicker",
119 //Keep track of the maximum number of rows displayed (see #7043)
122 // TODO rename to "widget" when switching to widget factory
123 _widgetDatepicker: function() {
127 /* Override the default settings for all instances of the date picker.
128 * @param settings object - the new settings to use as defaults (anonymous object)
129 * @return the manager object
131 setDefaults: function(settings) {
132 extendRemove(this._defaults, settings || {});
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)
140 _attachDatepicker: function(target, settings) {
141 var nodeName, inline, inst;
142 nodeName = target.nodeName.toLowerCase();
143 inline = (nodeName === "div" || nodeName === "span");
146 target.id = "dp" + this.uuid;
148 inst = this._newInst($(target), inline);
149 inst.settings = $.extend({}, settings || {});
150 if (nodeName === "input") {
151 this._connectDatepicker(target, inst);
153 this._inlineDatepicker(target, inst);
157 /* Create a new instance object. */
158 _newInst: function(target, inline) {
159 var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars
160 return {id: id, input: target, // associated target
161 selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
162 drawMonth: 0, drawYear: 0, // month being drawn
163 inline: inline, // is datepicker inline or not
164 dpDiv: (!inline ? this.dpDiv : // presentation div
165 bindHover($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))};
168 /* Attach the date picker to an input field. */
169 _connectDatepicker: function(target, inst) {
170 var input = $(target);
172 inst.trigger = $([]);
173 if (input.hasClass(this.markerClassName)) {
176 this._attachments(input, inst);
177 input.addClass(this.markerClassName).keydown(this._doKeyDown).
178 keypress(this._doKeyPress).keyup(this._doKeyUp);
179 this._autoSize(inst);
180 $.data(target, PROP_NAME, inst);
181 //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
182 if( inst.settings.disabled ) {
183 this._disableDatepicker( target );
187 /* Make attachments based on settings. */
188 _attachments: function(input, inst) {
189 var showOn, buttonText, buttonImage,
190 appendText = this._get(inst, "appendText"),
191 isRTL = this._get(inst, "isRTL");
194 inst.append.remove();
197 inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
198 input[isRTL ? "before" : "after"](inst.append);
201 input.unbind("focus", this._showDatepicker);
204 inst.trigger.remove();
207 showOn = this._get(inst, "showOn");
208 if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
209 input.focus(this._showDatepicker);
211 if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
212 buttonText = this._get(inst, "buttonText");
213 buttonImage = this._get(inst, "buttonImage");
214 inst.trigger = $(this._get(inst, "buttonImageOnly") ?
215 $("<img/>").addClass(this._triggerClass).
216 attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
217 $("<button type='button'></button>").addClass(this._triggerClass).
218 html(!buttonImage ? buttonText : $("<img/>").attr(
219 { src:buttonImage, alt:buttonText, title:buttonText })));
220 input[isRTL ? "before" : "after"](inst.trigger);
221 inst.trigger.click(function() {
222 if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) {
223 $.datepicker._hideDatepicker();
224 } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) {
225 $.datepicker._hideDatepicker();
226 $.datepicker._showDatepicker(input[0]);
228 $.datepicker._showDatepicker(input[0]);
235 /* Apply the maximum length for the date format. */
236 _autoSize: function(inst) {
237 if (this._get(inst, "autoSize") && !inst.inline) {
238 var findMax, max, maxI, i,
239 date = new Date(2009, 12 - 1, 20), // Ensure double digits
240 dateFormat = this._get(inst, "dateFormat");
242 if (dateFormat.match(/[DM]/)) {
243 findMax = function(names) {
246 for (i = 0; i < names.length; i++) {
247 if (names[i].length > max) {
248 max = names[i].length;
254 date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
255 "monthNames" : "monthNamesShort"))));
256 date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
257 "dayNames" : "dayNamesShort"))) + 20 - date.getDay());
259 inst.input.attr("size", this._formatDate(inst, date).length);
263 /* Attach an inline date picker to a div. */
264 _inlineDatepicker: function(target, inst) {
265 var divSpan = $(target);
266 if (divSpan.hasClass(this.markerClassName)) {
269 divSpan.addClass(this.markerClassName).append(inst.dpDiv);
270 $.data(target, PROP_NAME, inst);
271 this._setDate(inst, this._getDefaultDate(inst), true);
272 this._updateDatepicker(inst);
273 this._updateAlternate(inst);
274 //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
275 if( inst.settings.disabled ) {
276 this._disableDatepicker( target );
278 // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
279 // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
280 inst.dpDiv.css( "display", "block" );
283 /* Pop-up the date picker in a "dialog" box.
284 * @param input element - ignored
285 * @param date string or Date - the initial date to display
286 * @param onSelect function - the function to call when a date is selected
287 * @param settings object - update the dialog date picker instance's settings (anonymous object)
288 * @param pos int[2] - coordinates for the dialog's position within the screen or
289 * event - with x/y coordinates or
290 * leave empty for default (screen centre)
291 * @return the manager object
293 _dialogDatepicker: function(input, date, onSelect, settings, pos) {
294 var id, browserWidth, browserHeight, scrollX, scrollY,
295 inst = this._dialogInst; // internal instance
299 id = "dp" + this.uuid;
300 this._dialogInput = $("<input type='text' id='" + id +
301 "' style='position: absolute; top: -100px; width: 0px;'/>");
302 this._dialogInput.keydown(this._doKeyDown);
303 $("body").append(this._dialogInput);
304 inst = this._dialogInst = this._newInst(this._dialogInput, false);
306 $.data(this._dialogInput[0], PROP_NAME, inst);
308 extendRemove(inst.settings, settings || {});
309 date = (date && date.constructor === Date ? this._formatDate(inst, date) : date);
310 this._dialogInput.val(date);
312 this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
314 browserWidth = document.documentElement.clientWidth;
315 browserHeight = document.documentElement.clientHeight;
316 scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
317 scrollY = document.documentElement.scrollTop || document.body.scrollTop;
318 this._pos = // should use actual width/height below
319 [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
322 // move input on screen for focus, but hidden behind dialog
323 this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px");
324 inst.settings.onSelect = onSelect;
325 this._inDialog = true;
326 this.dpDiv.addClass(this._dialogClass);
327 this._showDatepicker(this._dialogInput[0]);
329 $.blockUI(this.dpDiv);
331 $.data(this._dialogInput[0], PROP_NAME, inst);
335 /* Detach a datepicker from its control.
336 * @param target element - the target input field or division or span
338 _destroyDatepicker: function(target) {
341 inst = $.data(target, PROP_NAME);
343 if (!$target.hasClass(this.markerClassName)) {
347 nodeName = target.nodeName.toLowerCase();
348 $.removeData(target, PROP_NAME);
349 if (nodeName === "input") {
350 inst.append.remove();
351 inst.trigger.remove();
352 $target.removeClass(this.markerClassName).
353 unbind("focus", this._showDatepicker).
354 unbind("keydown", this._doKeyDown).
355 unbind("keypress", this._doKeyPress).
356 unbind("keyup", this._doKeyUp);
357 } else if (nodeName === "div" || nodeName === "span") {
358 $target.removeClass(this.markerClassName).empty();
362 /* Enable the date picker to a jQuery selection.
363 * @param target element - the target input field or division or span
365 _enableDatepicker: function(target) {
366 var nodeName, inline,
368 inst = $.data(target, PROP_NAME);
370 if (!$target.hasClass(this.markerClassName)) {
374 nodeName = target.nodeName.toLowerCase();
375 if (nodeName === "input") {
376 target.disabled = false;
377 inst.trigger.filter("button").
378 each(function() { this.disabled = false; }).end().
379 filter("img").css({opacity: "1.0", cursor: ""});
380 } else if (nodeName === "div" || nodeName === "span") {
381 inline = $target.children("." + this._inlineClass);
382 inline.children().removeClass("ui-state-disabled");
383 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
384 prop("disabled", false);
386 this._disabledInputs = $.map(this._disabledInputs,
387 function(value) { return (value === target ? null : value); }); // delete entry
390 /* Disable the date picker to a jQuery selection.
391 * @param target element - the target input field or division or span
393 _disableDatepicker: function(target) {
394 var nodeName, inline,
396 inst = $.data(target, PROP_NAME);
398 if (!$target.hasClass(this.markerClassName)) {
402 nodeName = target.nodeName.toLowerCase();
403 if (nodeName === "input") {
404 target.disabled = true;
405 inst.trigger.filter("button").
406 each(function() { this.disabled = true; }).end().
407 filter("img").css({opacity: "0.5", cursor: "default"});
408 } else if (nodeName === "div" || nodeName === "span") {
409 inline = $target.children("." + this._inlineClass);
410 inline.children().addClass("ui-state-disabled");
411 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
412 prop("disabled", true);
414 this._disabledInputs = $.map(this._disabledInputs,
415 function(value) { return (value === target ? null : value); }); // delete entry
416 this._disabledInputs[this._disabledInputs.length] = target;
419 /* Is the first field in a jQuery collection disabled as a datepicker?
420 * @param target element - the target input field or division or span
421 * @return boolean - true if disabled, false if enabled
423 _isDisabledDatepicker: function(target) {
427 for (var i = 0; i < this._disabledInputs.length; i++) {
428 if (this._disabledInputs[i] === target) {
435 /* Retrieve the instance data for the target control.
436 * @param target element - the target input field or division or span
437 * @return object - the associated instance data
438 * @throws error if a jQuery problem getting data
440 _getInst: function(target) {
442 return $.data(target, PROP_NAME);
445 throw "Missing instance data for this datepicker";
449 /* Update or retrieve the settings for a date picker attached to an input field or division.
450 * @param target element - the target input field or division or span
451 * @param name object - the new settings to update or
452 * string - the name of the setting to change or retrieve,
453 * when retrieving also "all" for all instance settings or
454 * "defaults" for all global defaults
455 * @param value any - the new value for the setting
456 * (omit if above is an object or to retrieve a value)
458 _optionDatepicker: function(target, name, value) {
459 var settings, date, minDate, maxDate,
460 inst = this._getInst(target);
462 if (arguments.length === 2 && typeof name === "string") {
463 return (name === "defaults" ? $.extend({}, $.datepicker._defaults) :
464 (inst ? (name === "all" ? $.extend({}, inst.settings) :
465 this._get(inst, name)) : null));
468 settings = name || {};
469 if (typeof name === "string") {
471 settings[name] = value;
475 if (this._curInst === inst) {
476 this._hideDatepicker();
479 date = this._getDateDatepicker(target, true);
480 minDate = this._getMinMaxDate(inst, "min");
481 maxDate = this._getMinMaxDate(inst, "max");
482 extendRemove(inst.settings, settings);
483 // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
484 if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
485 inst.settings.minDate = this._formatDate(inst, minDate);
487 if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
488 inst.settings.maxDate = this._formatDate(inst, maxDate);
490 if ( "disabled" in settings ) {
491 if ( settings.disabled ) {
492 this._disableDatepicker(target);
494 this._enableDatepicker(target);
497 this._attachments($(target), inst);
498 this._autoSize(inst);
499 this._setDate(inst, date);
500 this._updateAlternate(inst);
501 this._updateDatepicker(inst);
505 // change method deprecated
506 _changeDatepicker: function(target, name, value) {
507 this._optionDatepicker(target, name, value);
510 /* Redraw the date picker attached to an input field or division.
511 * @param target element - the target input field or division or span
513 _refreshDatepicker: function(target) {
514 var inst = this._getInst(target);
516 this._updateDatepicker(inst);
520 /* Set the dates for a jQuery selection.
521 * @param target element - the target input field or division or span
522 * @param date Date - the new date
524 _setDateDatepicker: function(target, date) {
525 var inst = this._getInst(target);
527 this._setDate(inst, date);
528 this._updateDatepicker(inst);
529 this._updateAlternate(inst);
533 /* Get the date(s) for the first entry in a jQuery selection.
534 * @param target element - the target input field or division or span
535 * @param noDefault boolean - true if no default date is to be used
536 * @return Date - the current date
538 _getDateDatepicker: function(target, noDefault) {
539 var inst = this._getInst(target);
540 if (inst && !inst.inline) {
541 this._setDateFromField(inst, noDefault);
543 return (inst ? this._getDate(inst) : null);
546 /* Handle keystrokes. */
547 _doKeyDown: function(event) {
548 var onSelect, dateStr, sel,
549 inst = $.datepicker._getInst(event.target),
551 isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
553 inst._keyEvent = true;
554 if ($.datepicker._datepickerShowing) {
555 switch (event.keyCode) {
556 case 9: $.datepicker._hideDatepicker();
558 break; // hide on tab out
559 case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." +
560 $.datepicker._currentClass + ")", inst.dpDiv);
562 $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
565 onSelect = $.datepicker._get(inst, "onSelect");
567 dateStr = $.datepicker._formatDate(inst);
569 // trigger custom callback
570 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
572 $.datepicker._hideDatepicker();
575 return false; // don't submit the form
576 case 27: $.datepicker._hideDatepicker();
577 break; // hide on escape
578 case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
579 -$.datepicker._get(inst, "stepBigMonths") :
580 -$.datepicker._get(inst, "stepMonths")), "M");
581 break; // previous month/year on page up/+ ctrl
582 case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
583 +$.datepicker._get(inst, "stepBigMonths") :
584 +$.datepicker._get(inst, "stepMonths")), "M");
585 break; // next month/year on page down/+ ctrl
586 case 35: if (event.ctrlKey || event.metaKey) {
587 $.datepicker._clearDate(event.target);
589 handled = event.ctrlKey || event.metaKey;
590 break; // clear on ctrl or command +end
591 case 36: if (event.ctrlKey || event.metaKey) {
592 $.datepicker._gotoToday(event.target);
594 handled = event.ctrlKey || event.metaKey;
595 break; // current on ctrl or command +home
596 case 37: if (event.ctrlKey || event.metaKey) {
597 $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D");
599 handled = event.ctrlKey || event.metaKey;
600 // -1 day on ctrl or command +left
601 if (event.originalEvent.altKey) {
602 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
603 -$.datepicker._get(inst, "stepBigMonths") :
604 -$.datepicker._get(inst, "stepMonths")), "M");
606 // next month/year on alt +left on Mac
608 case 38: if (event.ctrlKey || event.metaKey) {
609 $.datepicker._adjustDate(event.target, -7, "D");
611 handled = event.ctrlKey || event.metaKey;
612 break; // -1 week on ctrl or command +up
613 case 39: if (event.ctrlKey || event.metaKey) {
614 $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D");
616 handled = event.ctrlKey || event.metaKey;
617 // +1 day on ctrl or command +right
618 if (event.originalEvent.altKey) {
619 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
620 +$.datepicker._get(inst, "stepBigMonths") :
621 +$.datepicker._get(inst, "stepMonths")), "M");
623 // next month/year on alt +right
625 case 40: if (event.ctrlKey || event.metaKey) {
626 $.datepicker._adjustDate(event.target, +7, "D");
628 handled = event.ctrlKey || event.metaKey;
629 break; // +1 week on ctrl or command +down
630 default: handled = false;
632 } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
633 $.datepicker._showDatepicker(this);
639 event.preventDefault();
640 event.stopPropagation();
644 /* Filter entered characters - based on date format. */
645 _doKeyPress: function(event) {
647 inst = $.datepicker._getInst(event.target);
649 if ($.datepicker._get(inst, "constrainInput")) {
650 chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat"));
651 chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode);
652 return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1);
656 /* Synchronise manual entry and field/alternate field. */
657 _doKeyUp: function(event) {
659 inst = $.datepicker._getInst(event.target);
661 if (inst.input.val() !== inst.lastVal) {
663 date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
664 (inst.input ? inst.input.val() : null),
665 $.datepicker._getFormatConfig(inst));
667 if (date) { // only if valid
668 $.datepicker._setDateFromField(inst);
669 $.datepicker._updateAlternate(inst);
670 $.datepicker._updateDatepicker(inst);
679 /* Pop-up the date picker for a given input field.
680 * If false returned from beforeShow event handler do not show.
681 * @param input element - the input field attached to the date picker or
682 * event - if triggered by focus
684 _showDatepicker: function(input) {
685 input = input.target || input;
686 if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger
687 input = $("input", input.parentNode)[0];
690 if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here
694 var inst, beforeShow, beforeShowSettings, isFixed,
695 offset, showAnim, duration;
697 inst = $.datepicker._getInst(input);
698 if ($.datepicker._curInst && $.datepicker._curInst !== inst) {
699 $.datepicker._curInst.dpDiv.stop(true, true);
700 if ( inst && $.datepicker._datepickerShowing ) {
701 $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
705 beforeShow = $.datepicker._get(inst, "beforeShow");
706 beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
707 if(beforeShowSettings === false){
710 extendRemove(inst.settings, beforeShowSettings);
713 $.datepicker._lastInput = input;
714 $.datepicker._setDateFromField(inst);
716 if ($.datepicker._inDialog) { // hide cursor
719 if (!$.datepicker._pos) { // position below input
720 $.datepicker._pos = $.datepicker._findPos(input);
721 $.datepicker._pos[1] += input.offsetHeight; // add the height
725 $(input).parents().each(function() {
726 isFixed |= $(this).css("position") === "fixed";
730 offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
731 $.datepicker._pos = null;
732 //to avoid flashes on Firefox
734 // determine sizing offscreen
735 inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"});
736 $.datepicker._updateDatepicker(inst);
737 // fix width for dynamic number of date pickers
738 // and adjust position before showing
739 offset = $.datepicker._checkOffset(inst, offset, isFixed);
740 inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
741 "static" : (isFixed ? "fixed" : "absolute")), display: "none",
742 left: offset.left + "px", top: offset.top + "px"});
745 showAnim = $.datepicker._get(inst, "showAnim");
746 duration = $.datepicker._get(inst, "duration");
747 inst.dpDiv.zIndex($(input).zIndex()+1);
748 $.datepicker._datepickerShowing = true;
750 if ( $.effects && $.effects.effect[ showAnim ] ) {
751 inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration);
753 inst.dpDiv[showAnim || "show"](showAnim ? duration : null);
756 if (inst.input.is(":visible") && !inst.input.is(":disabled")) {
759 $.datepicker._curInst = inst;
763 /* Generate the date picker content. */
764 _updateDatepicker: function(inst) {
765 this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
766 instActive = inst; // for delegate hover events
767 inst.dpDiv.empty().append(this._generateHTML(inst));
768 this._attachHandlers(inst);
769 inst.dpDiv.find("." + this._dayOverClass + " a").mouseover();
772 numMonths = this._getNumberOfMonths(inst),
776 inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");
778 inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em");
780 inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") +
781 "Class"]("ui-datepicker-multi");
782 inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") +
783 "Class"]("ui-datepicker-rtl");
785 // #6694 - don't focus the input if it's already focused
786 // this breaks the change event in IE
787 if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input &&
788 inst.input.is(":visible") && !inst.input.is(":disabled") && inst.input[0] !== document.activeElement) {
792 // deffered render of the years select (to avoid flashes on Firefox)
793 if( inst.yearshtml ){
794 origyearshtml = inst.yearshtml;
795 setTimeout(function(){
796 //assure that inst.yearshtml didn't change.
797 if( origyearshtml === inst.yearshtml && inst.yearshtml ){
798 inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml);
800 origyearshtml = inst.yearshtml = null;
805 /* Retrieve the size of left and top borders for an element.
806 * @param elem (jQuery object) the element of interest
807 * @return (number[2]) the left and top borders
809 _getBorders: function(elem) {
810 var convert = function(value) {
811 return {thin: 1, medium: 2, thick: 3}[value] || value;
813 return [parseFloat(convert(elem.css("border-left-width"))),
814 parseFloat(convert(elem.css("border-top-width")))];
817 /* Check positioning to remain on screen. */
818 _checkOffset: function(inst, offset, isFixed) {
819 var dpWidth = inst.dpDiv.outerWidth(),
820 dpHeight = inst.dpDiv.outerHeight(),
821 inputWidth = inst.input ? inst.input.outerWidth() : 0,
822 inputHeight = inst.input ? inst.input.outerHeight() : 0,
823 viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()),
824 viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
826 offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0);
827 offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0;
828 offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
830 // now check if datepicker is showing outside window viewport - move to a better place if so.
831 offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
832 Math.abs(offset.left + dpWidth - viewWidth) : 0);
833 offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
834 Math.abs(dpHeight + inputHeight) : 0);
839 /* Find an object's position on the screen. */
840 _findPos: function(obj) {
842 inst = this._getInst(obj),
843 isRTL = this._get(inst, "isRTL");
845 while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
846 obj = obj[isRTL ? "previousSibling" : "nextSibling"];
849 position = $(obj).offset();
850 return [position.left, position.top];
853 /* Hide the date picker from view.
854 * @param input element - the input field attached to the date picker
856 _hideDatepicker: function(input) {
857 var showAnim, duration, postProcess, onClose,
858 inst = this._curInst;
860 if (!inst || (input && inst !== $.data(input, PROP_NAME))) {
864 if (this._datepickerShowing) {
865 showAnim = this._get(inst, "showAnim");
866 duration = this._get(inst, "duration");
867 postProcess = function() {
868 $.datepicker._tidyDialog(inst);
871 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
872 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
873 inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess);
875 inst.dpDiv[(showAnim === "slideDown" ? "slideUp" :
876 (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess);
882 this._datepickerShowing = false;
884 onClose = this._get(inst, "onClose");
886 onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]);
889 this._lastInput = null;
890 if (this._inDialog) {
891 this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" });
894 $("body").append(this.dpDiv);
897 this._inDialog = false;
901 /* Tidy up after a dialog display. */
902 _tidyDialog: function(inst) {
903 inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar");
906 /* Close date picker if clicked elsewhere. */
907 _checkExternalClick: function(event) {
908 if (!$.datepicker._curInst) {
912 var $target = $(event.target),
913 inst = $.datepicker._getInst($target[0]);
915 if ( ( ( $target[0].id !== $.datepicker._mainDivId &&
916 $target.parents("#" + $.datepicker._mainDivId).length === 0 &&
917 !$target.hasClass($.datepicker.markerClassName) &&
918 !$target.closest("." + $.datepicker._triggerClass).length &&
919 $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
920 ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) {
921 $.datepicker._hideDatepicker();
925 /* Adjust one of the date sub-fields. */
926 _adjustDate: function(id, offset, period) {
928 inst = this._getInst(target[0]);
930 if (this._isDisabledDatepicker(target[0])) {
933 this._adjustInstDate(inst, offset +
934 (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning
936 this._updateDatepicker(inst);
939 /* Action for current link. */
940 _gotoToday: function(id) {
943 inst = this._getInst(target[0]);
945 if (this._get(inst, "gotoCurrent") && inst.currentDay) {
946 inst.selectedDay = inst.currentDay;
947 inst.drawMonth = inst.selectedMonth = inst.currentMonth;
948 inst.drawYear = inst.selectedYear = inst.currentYear;
951 inst.selectedDay = date.getDate();
952 inst.drawMonth = inst.selectedMonth = date.getMonth();
953 inst.drawYear = inst.selectedYear = date.getFullYear();
955 this._notifyChange(inst);
956 this._adjustDate(target);
959 /* Action for selecting a new month/year. */
960 _selectMonthYear: function(id, select, period) {
962 inst = this._getInst(target[0]);
964 inst["selected" + (period === "M" ? "Month" : "Year")] =
965 inst["draw" + (period === "M" ? "Month" : "Year")] =
966 parseInt(select.options[select.selectedIndex].value,10);
968 this._notifyChange(inst);
969 this._adjustDate(target);
972 /* Action for selecting a day. */
973 _selectDay: function(id, month, year, td) {
977 if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
981 inst = this._getInst(target[0]);
982 inst.selectedDay = inst.currentDay = $("a", td).html();
983 inst.selectedMonth = inst.currentMonth = month;
984 inst.selectedYear = inst.currentYear = year;
985 this._selectDate(id, this._formatDate(inst,
986 inst.currentDay, inst.currentMonth, inst.currentYear));
989 /* Erase the input field and hide the date picker. */
990 _clearDate: function(id) {
992 this._selectDate(target, "");
995 /* Update the input field with the selected date. */
996 _selectDate: function(id, dateStr) {
999 inst = this._getInst(target[0]);
1001 dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
1003 inst.input.val(dateStr);
1005 this._updateAlternate(inst);
1007 onSelect = this._get(inst, "onSelect");
1009 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
1010 } else if (inst.input) {
1011 inst.input.trigger("change"); // fire the change event
1015 this._updateDatepicker(inst);
1017 this._hideDatepicker();
1018 this._lastInput = inst.input[0];
1019 if (typeof(inst.input[0]) !== "object") {
1020 inst.input.focus(); // restore focus
1022 this._lastInput = null;
1026 /* Update any alternate field to synchronise with the main field. */
1027 _updateAlternate: function(inst) {
1028 var altFormat, date, dateStr,
1029 altField = this._get(inst, "altField");
1031 if (altField) { // update alternate field too
1032 altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat");
1033 date = this._getDate(inst);
1034 dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
1035 $(altField).each(function() { $(this).val(dateStr); });
1039 /* Set as beforeShowDay function to prevent selection of weekends.
1040 * @param date Date - the date to customise
1041 * @return [boolean, string] - is this date selectable?, what is its CSS class?
1043 noWeekends: function(date) {
1044 var day = date.getDay();
1045 return [(day > 0 && day < 6), ""];
1048 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
1049 * @param date Date - the date to get the week for
1050 * @return number - the number of the week within the year that contains this date
1052 iso8601Week: function(date) {
1054 checkDate = new Date(date.getTime());
1056 // Find Thursday of this week starting on Monday
1057 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
1059 time = checkDate.getTime();
1060 checkDate.setMonth(0); // Compare with Jan 1
1061 checkDate.setDate(1);
1062 return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
1065 /* Parse a string value into a date object.
1066 * See formatDate below for the possible formats.
1068 * @param format string - the expected format of the date
1069 * @param value string - the date in the above format
1070 * @param settings Object - attributes include:
1071 * shortYearCutoff number - the cutoff year for determining the century (optional)
1072 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
1073 * dayNames string[7] - names of the days from Sunday (optional)
1074 * monthNamesShort string[12] - abbreviated names of the months (optional)
1075 * monthNames string[12] - names of the months (optional)
1076 * @return Date - the extracted date value or null if value is blank
1078 parseDate: function (format, value, settings) {
1079 if (format == null || value == null) {
1080 throw "Invalid arguments";
1083 value = (typeof value === "object" ? value.toString() : value + "");
1088 var iFormat, dim, extra,
1090 shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff,
1091 shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
1092 new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)),
1093 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
1094 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
1095 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
1096 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
1103 // Check whether a format character is doubled
1104 lookAhead = function(match) {
1105 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
1111 // Extract a number from the string value
1112 getNumber = function(match) {
1113 var isDoubled = lookAhead(match),
1114 size = (match === "@" ? 14 : (match === "!" ? 20 :
1115 (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))),
1116 digits = new RegExp("^\\d{1," + size + "}"),
1117 num = value.substring(iValue).match(digits);
1119 throw "Missing number at position " + iValue;
1121 iValue += num[0].length;
1122 return parseInt(num[0], 10);
1124 // Extract a name from the string value and convert to an index
1125 getName = function(match, shortNames, longNames) {
1127 names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
1129 }).sort(function (a, b) {
1130 return -(a[1].length - b[1].length);
1133 $.each(names, function (i, pair) {
1135 if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) {
1137 iValue += name.length;
1144 throw "Unknown name at position " + iValue;
1147 // Confirm that a literal character matches the string value
1148 checkLiteral = function() {
1149 if (value.charAt(iValue) !== format.charAt(iFormat)) {
1150 throw "Unexpected literal at position " + iValue;
1155 for (iFormat = 0; iFormat < format.length; iFormat++) {
1157 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
1163 switch (format.charAt(iFormat)) {
1165 day = getNumber("d");
1168 getName("D", dayNamesShort, dayNames);
1171 doy = getNumber("o");
1174 month = getNumber("m");
1177 month = getName("M", monthNamesShort, monthNames);
1180 year = getNumber("y");
1183 date = new Date(getNumber("@"));
1184 year = date.getFullYear();
1185 month = date.getMonth() + 1;
1186 day = date.getDate();
1189 date = new Date((getNumber("!") - this._ticksTo1970) / 10000);
1190 year = date.getFullYear();
1191 month = date.getMonth() + 1;
1192 day = date.getDate();
1195 if (lookAhead("'")){
1207 if (iValue < value.length){
1208 extra = value.substr(iValue);
1209 if (!/^\s+/.test(extra)) {
1210 throw "Extra/unparsed characters found in date: " + extra;
1215 year = new Date().getFullYear();
1216 } else if (year < 100) {
1217 year += new Date().getFullYear() - new Date().getFullYear() % 100 +
1218 (year <= shortYearCutoff ? 0 : -100);
1225 dim = this._getDaysInMonth(year, month - 1);
1234 date = this._daylightSavingAdjust(new Date(year, month - 1, day));
1235 if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
1236 throw "Invalid date"; // E.g. 31/02/00
1241 /* Standard date formats. */
1242 ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
1243 COOKIE: "D, dd M yy",
1244 ISO_8601: "yy-mm-dd",
1245 RFC_822: "D, d M y",
1246 RFC_850: "DD, dd-M-y",
1247 RFC_1036: "D, d M y",
1248 RFC_1123: "D, d M yy",
1249 RFC_2822: "D, d M yy",
1250 RSS: "D, d M y", // RFC 822
1253 W3C: "yy-mm-dd", // ISO 8601
1255 _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
1256 Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
1258 /* Format a date object into a string value.
1259 * The format can be combinations of the following:
1260 * d - day of month (no leading zero)
1261 * dd - day of month (two digit)
1262 * o - day of year (no leading zeros)
1263 * oo - day of year (three digit)
1264 * D - day name short
1265 * DD - day name long
1266 * m - month of year (no leading zero)
1267 * mm - month of year (two digit)
1268 * M - month name short
1269 * MM - month name long
1270 * y - year (two digit)
1271 * yy - year (four digit)
1272 * @ - Unix timestamp (ms since 01/01/1970)
1273 * ! - Windows ticks (100ns since 01/01/0001)
1274 * "..." - literal text
1277 * @param format string - the desired format of the date
1278 * @param date Date - the date value to format
1279 * @param settings Object - attributes include:
1280 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
1281 * dayNames string[7] - names of the days from Sunday (optional)
1282 * monthNamesShort string[12] - abbreviated names of the months (optional)
1283 * monthNames string[12] - names of the months (optional)
1284 * @return string - the date in the above format
1286 formatDate: function (format, date, settings) {
1292 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
1293 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
1294 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
1295 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
1296 // Check whether a format character is doubled
1297 lookAhead = function(match) {
1298 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
1304 // Format a number, with leading zero if necessary
1305 formatNumber = function(match, value, len) {
1306 var num = "" + value;
1307 if (lookAhead(match)) {
1308 while (num.length < len) {
1314 // Format a name, short or long as requested
1315 formatName = function(match, value, shortNames, longNames) {
1316 return (lookAhead(match) ? longNames[value] : shortNames[value]);
1322 for (iFormat = 0; iFormat < format.length; iFormat++) {
1324 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
1327 output += format.charAt(iFormat);
1330 switch (format.charAt(iFormat)) {
1332 output += formatNumber("d", date.getDate(), 2);
1335 output += formatName("D", date.getDay(), dayNamesShort, dayNames);
1338 output += formatNumber("o",
1339 Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
1342 output += formatNumber("m", date.getMonth() + 1, 2);
1345 output += formatName("M", date.getMonth(), monthNamesShort, monthNames);
1348 output += (lookAhead("y") ? date.getFullYear() :
1349 (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100);
1352 output += date.getTime();
1355 output += date.getTime() * 10000 + this._ticksTo1970;
1358 if (lookAhead("'")) {
1365 output += format.charAt(iFormat);
1373 /* Extract all possible characters from the date format. */
1374 _possibleChars: function (format) {
1378 // Check whether a format character is doubled
1379 lookAhead = function(match) {
1380 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
1387 for (iFormat = 0; iFormat < format.length; iFormat++) {
1389 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
1392 chars += format.charAt(iFormat);
1395 switch (format.charAt(iFormat)) {
1396 case "d": case "m": case "y": case "@":
1397 chars += "0123456789";
1400 return null; // Accept anything
1402 if (lookAhead("'")) {
1409 chars += format.charAt(iFormat);
1416 /* Get a setting value, defaulting if necessary. */
1417 _get: function(inst, name) {
1418 return inst.settings[name] !== undefined ?
1419 inst.settings[name] : this._defaults[name];
1422 /* Parse existing date and initialise date picker. */
1423 _setDateFromField: function(inst, noDefault) {
1424 if (inst.input.val() === inst.lastVal) {
1428 var dateFormat = this._get(inst, "dateFormat"),
1429 dates = inst.lastVal = inst.input ? inst.input.val() : null,
1430 defaultDate = this._getDefaultDate(inst),
1432 settings = this._getFormatConfig(inst);
1435 date = this.parseDate(dateFormat, dates, settings) || defaultDate;
1437 dates = (noDefault ? "" : dates);
1439 inst.selectedDay = date.getDate();
1440 inst.drawMonth = inst.selectedMonth = date.getMonth();
1441 inst.drawYear = inst.selectedYear = date.getFullYear();
1442 inst.currentDay = (dates ? date.getDate() : 0);
1443 inst.currentMonth = (dates ? date.getMonth() : 0);
1444 inst.currentYear = (dates ? date.getFullYear() : 0);
1445 this._adjustInstDate(inst);
1448 /* Retrieve the default date shown on opening. */
1449 _getDefaultDate: function(inst) {
1450 return this._restrictMinMax(inst,
1451 this._determineDate(inst, this._get(inst, "defaultDate"), new Date()));
1454 /* A date may be specified as an exact value or a relative one. */
1455 _determineDate: function(inst, date, defaultDate) {
1456 var offsetNumeric = function(offset) {
1457 var date = new Date();
1458 date.setDate(date.getDate() + offset);
1461 offsetString = function(offset) {
1463 return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
1464 offset, $.datepicker._getFormatConfig(inst));
1470 var date = (offset.toLowerCase().match(/^c/) ?
1471 $.datepicker._getDate(inst) : null) || new Date(),
1472 year = date.getFullYear(),
1473 month = date.getMonth(),
1474 day = date.getDate(),
1475 pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
1476 matches = pattern.exec(offset);
1479 switch (matches[2] || "d") {
1480 case "d" : case "D" :
1481 day += parseInt(matches[1],10); break;
1482 case "w" : case "W" :
1483 day += parseInt(matches[1],10) * 7; break;
1484 case "m" : case "M" :
1485 month += parseInt(matches[1],10);
1486 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
1488 case "y": case "Y" :
1489 year += parseInt(matches[1],10);
1490 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
1493 matches = pattern.exec(offset);
1495 return new Date(year, month, day);
1497 newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) :
1498 (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
1500 newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate);
1502 newDate.setHours(0);
1503 newDate.setMinutes(0);
1504 newDate.setSeconds(0);
1505 newDate.setMilliseconds(0);
1507 return this._daylightSavingAdjust(newDate);
1510 /* Handle switch to/from daylight saving.
1511 * Hours may be non-zero on daylight saving cut-over:
1512 * > 12 when midnight changeover, but then cannot generate
1513 * midnight datetime, so jump to 1AM, otherwise reset.
1514 * @param date (Date) the date to check
1515 * @return (Date) the corrected date
1517 _daylightSavingAdjust: function(date) {
1521 date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
1525 /* Set the date(s) directly. */
1526 _setDate: function(inst, date, noChange) {
1528 origMonth = inst.selectedMonth,
1529 origYear = inst.selectedYear,
1530 newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
1532 inst.selectedDay = inst.currentDay = newDate.getDate();
1533 inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
1534 inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
1535 if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) {
1536 this._notifyChange(inst);
1538 this._adjustInstDate(inst);
1540 inst.input.val(clear ? "" : this._formatDate(inst));
1544 /* Retrieve the date(s) directly. */
1545 _getDate: function(inst) {
1546 var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null :
1547 this._daylightSavingAdjust(new Date(
1548 inst.currentYear, inst.currentMonth, inst.currentDay)));
1552 /* Attach the onxxx handlers. These are declared statically so
1553 * they work with static code transformers like Caja.
1555 _attachHandlers: function(inst) {
1556 var stepMonths = this._get(inst, "stepMonths"),
1557 id = "#" + inst.id.replace( /\\\\/g, "\\" );
1558 inst.dpDiv.find("[data-handler]").map(function () {
1561 window["DP_jQuery_" + dpuuid].datepicker._adjustDate(id, -stepMonths, "M");
1564 window["DP_jQuery_" + dpuuid].datepicker._adjustDate(id, +stepMonths, "M");
1567 window["DP_jQuery_" + dpuuid].datepicker._hideDatepicker();
1569 today: function () {
1570 window["DP_jQuery_" + dpuuid].datepicker._gotoToday(id);
1572 selectDay: function () {
1573 window["DP_jQuery_" + dpuuid].datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this);
1576 selectMonth: function () {
1577 window["DP_jQuery_" + dpuuid].datepicker._selectMonthYear(id, this, "M");
1580 selectYear: function () {
1581 window["DP_jQuery_" + dpuuid].datepicker._selectMonthYear(id, this, "Y");
1585 $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]);
1589 /* Generate the HTML for the current state of the date picker. */
1590 _generateHTML: function(inst) {
1591 var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
1592 controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
1593 monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
1594 selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
1595 cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
1596 printDate, dRow, tbody, daySettings, otherMonth, unselectable,
1597 tempDate = new Date(),
1598 today = this._daylightSavingAdjust(
1599 new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time
1600 isRTL = this._get(inst, "isRTL"),
1601 showButtonPanel = this._get(inst, "showButtonPanel"),
1602 hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"),
1603 navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"),
1604 numMonths = this._getNumberOfMonths(inst),
1605 showCurrentAtPos = this._get(inst, "showCurrentAtPos"),
1606 stepMonths = this._get(inst, "stepMonths"),
1607 isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1),
1608 currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
1609 new Date(inst.currentYear, inst.currentMonth, inst.currentDay))),
1610 minDate = this._getMinMaxDate(inst, "min"),
1611 maxDate = this._getMinMaxDate(inst, "max"),
1612 drawMonth = inst.drawMonth - showCurrentAtPos,
1613 drawYear = inst.drawYear;
1615 if (drawMonth < 0) {
1620 maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
1621 maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
1622 maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
1623 while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
1625 if (drawMonth < 0) {
1631 inst.drawMonth = drawMonth;
1632 inst.drawYear = drawYear;
1634 prevText = this._get(inst, "prevText");
1635 prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
1636 this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
1637 this._getFormatConfig(inst)));
1639 prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
1640 "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
1641 " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" :
1642 (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>"));
1644 nextText = this._get(inst, "nextText");
1645 nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
1646 this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
1647 this._getFormatConfig(inst)));
1649 next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
1650 "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
1651 " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" :
1652 (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>"));
1654 currentText = this._get(inst, "currentText");
1655 gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today);
1656 currentText = (!navigationAsDateFormat ? currentText :
1657 this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
1659 controls = (!inst.inline ? "<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>" +
1660 this._get(inst, "closeText") + "</button>" : "");
1662 buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") +
1663 (this._isInRange(inst, gotoDate) ? "<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'" +
1664 ">" + currentText + "</button>" : "") + (isRTL ? "" : controls) + "</div>" : "";
1666 firstDay = parseInt(this._get(inst, "firstDay"),10);
1667 firstDay = (isNaN(firstDay) ? 0 : firstDay);
1669 showWeek = this._get(inst, "showWeek");
1670 dayNames = this._get(inst, "dayNames");
1671 dayNamesMin = this._get(inst, "dayNamesMin");
1672 monthNames = this._get(inst, "monthNames");
1673 monthNamesShort = this._get(inst, "monthNamesShort");
1674 beforeShowDay = this._get(inst, "beforeShowDay");
1675 showOtherMonths = this._get(inst, "showOtherMonths");
1676 selectOtherMonths = this._get(inst, "selectOtherMonths");
1677 defaultDate = this._getDefaultDate(inst);
1680 for (row = 0; row < numMonths[0]; row++) {
1683 for (col = 0; col < numMonths[1]; col++) {
1684 selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
1685 cornerClass = " ui-corner-all";
1688 calender += "<div class='ui-datepicker-group";
1689 if (numMonths[1] > 1) {
1691 case 0: calender += " ui-datepicker-group-first";
1692 cornerClass = " ui-corner-" + (isRTL ? "right" : "left"); break;
1693 case numMonths[1]-1: calender += " ui-datepicker-group-last";
1694 cornerClass = " ui-corner-" + (isRTL ? "left" : "right"); break;
1695 default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
1700 calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
1701 (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") +
1702 (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") +
1703 this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
1704 row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
1705 "</div><table class='ui-datepicker-calendar'><thead>" +
1707 thead = (showWeek ? "<th class='ui-datepicker-week-col'>" + this._get(inst, "weekHeader") + "</th>" : "");
1708 for (dow = 0; dow < 7; dow++) { // days of the week
1709 day = (dow + firstDay) % 7;
1710 thead += "<th" + ((dow + firstDay + 6) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "") + ">" +
1711 "<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>";
1713 calender += thead + "</tr></thead><tbody>";
1714 daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
1715 if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) {
1716 inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
1718 leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
1719 curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
1720 numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
1721 this.maxRows = numRows;
1722 printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
1723 for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows
1725 tbody = (!showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
1726 this._get(inst, "calculateWeek")(printDate) + "</td>");
1727 for (dow = 0; dow < 7; dow++) { // create date picker days
1728 daySettings = (beforeShowDay ?
1729 beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]);
1730 otherMonth = (printDate.getMonth() !== drawMonth);
1731 unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
1732 (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
1733 tbody += "<td class='" +
1734 ((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends
1735 (otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months
1736 ((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key
1737 (defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ?
1738 // or defaultDate is current printedDate and defaultDate is selectedDate
1739 " " + this._dayOverClass : "") + // highlight selected day
1740 (unselectable ? " " + this._unselectableClass + " ui-state-disabled": "") + // highlight unselectable days
1741 (otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates
1742 (printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day
1743 (printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different)
1744 ((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "'") + "'" : "") + // cell title
1745 (unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions
1746 (otherMonth && !showOtherMonths ? " " : // display for other months
1747 (unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
1748 (printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") +
1749 (printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day
1750 (otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months
1751 "' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date
1752 printDate.setDate(printDate.getDate() + 1);
1753 printDate = this._daylightSavingAdjust(printDate);
1755 calender += tbody + "</tr>";
1758 if (drawMonth > 11) {
1762 calender += "</tbody></table>" + (isMultiMonth ? "</div>" +
1763 ((numMonths[0] > 0 && col === numMonths[1]-1) ? "<div class='ui-datepicker-row-break'></div>" : "") : "");
1768 html += buttonPanel;
1769 inst._keyEvent = false;
1773 /* Generate the month and year header. */
1774 _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
1775 secondary, monthNames, monthNamesShort) {
1777 var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
1778 changeMonth = this._get(inst, "changeMonth"),
1779 changeYear = this._get(inst, "changeYear"),
1780 showMonthAfterYear = this._get(inst, "showMonthAfterYear"),
1781 html = "<div class='ui-datepicker-title'>",
1785 if (secondary || !changeMonth) {
1786 monthHtml += "<span class='ui-datepicker-month'>" + monthNames[drawMonth] + "</span>";
1788 inMinYear = (minDate && minDate.getFullYear() === drawYear);
1789 inMaxYear = (maxDate && maxDate.getFullYear() === drawYear);
1790 monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
1791 for ( month = 0; month < 12; month++) {
1792 if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth())) {
1793 monthHtml += "<option value='" + month + "'" +
1794 (month === drawMonth ? " selected='selected'" : "") +
1795 ">" + monthNamesShort[month] + "</option>";
1798 monthHtml += "</select>";
1801 if (!showMonthAfterYear) {
1802 html += monthHtml + (secondary || !(changeMonth && changeYear) ? " " : "");
1806 if ( !inst.yearshtml ) {
1807 inst.yearshtml = "";
1808 if (secondary || !changeYear) {
1809 html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
1811 // determine range of years to display
1812 years = this._get(inst, "yearRange").split(":");
1813 thisYear = new Date().getFullYear();
1814 determineYear = function(value) {
1815 var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) :
1816 (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) :
1817 parseInt(value, 10)));
1818 return (isNaN(year) ? thisYear : year);
1820 year = determineYear(years[0]);
1821 endYear = Math.max(year, determineYear(years[1] || ""));
1822 year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
1823 endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
1824 inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
1825 for (; year <= endYear; year++) {
1826 inst.yearshtml += "<option value='" + year + "'" +
1827 (year === drawYear ? " selected='selected'" : "") +
1828 ">" + year + "</option>";
1830 inst.yearshtml += "</select>";
1832 html += inst.yearshtml;
1833 inst.yearshtml = null;
1837 html += this._get(inst, "yearSuffix");
1838 if (showMonthAfterYear) {
1839 html += (secondary || !(changeMonth && changeYear) ? " " : "") + monthHtml;
1841 html += "</div>"; // Close datepicker_header
1845 /* Adjust one of the date sub-fields. */
1846 _adjustInstDate: function(inst, offset, period) {
1847 var year = inst.drawYear + (period === "Y" ? offset : 0),
1848 month = inst.drawMonth + (period === "M" ? offset : 0),
1849 day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0),
1850 date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day)));
1852 inst.selectedDay = date.getDate();
1853 inst.drawMonth = inst.selectedMonth = date.getMonth();
1854 inst.drawYear = inst.selectedYear = date.getFullYear();
1855 if (period === "M" || period === "Y") {
1856 this._notifyChange(inst);
1860 /* Ensure a date is within any min/max bounds. */
1861 _restrictMinMax: function(inst, date) {
1862 var minDate = this._getMinMaxDate(inst, "min"),
1863 maxDate = this._getMinMaxDate(inst, "max"),
1864 newDate = (minDate && date < minDate ? minDate : date);
1865 return (maxDate && newDate > maxDate ? maxDate : newDate);
1868 /* Notify change of month/year. */
1869 _notifyChange: function(inst) {
1870 var onChange = this._get(inst, "onChangeMonthYear");
1872 onChange.apply((inst.input ? inst.input[0] : null),
1873 [inst.selectedYear, inst.selectedMonth + 1, inst]);
1877 /* Determine the number of months to show. */
1878 _getNumberOfMonths: function(inst) {
1879 var numMonths = this._get(inst, "numberOfMonths");
1880 return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths));
1883 /* Determine the current maximum date - ensure no time components are set. */
1884 _getMinMaxDate: function(inst, minMax) {
1885 return this._determineDate(inst, this._get(inst, minMax + "Date"), null);
1888 /* Find the number of days in a given month. */
1889 _getDaysInMonth: function(year, month) {
1890 return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
1893 /* Find the day of the week of the first of a month. */
1894 _getFirstDayOfMonth: function(year, month) {
1895 return new Date(year, month, 1).getDay();
1898 /* Determines if we should allow a "next/prev" month display change. */
1899 _canAdjustMonth: function(inst, offset, curYear, curMonth) {
1900 var numMonths = this._getNumberOfMonths(inst),
1901 date = this._daylightSavingAdjust(new Date(curYear,
1902 curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
1905 date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
1907 return this._isInRange(inst, date);
1910 /* Is the given date in the accepted range? */
1911 _isInRange: function(inst, date) {
1912 var yearSplit, currentYear,
1913 minDate = this._getMinMaxDate(inst, "min"),
1914 maxDate = this._getMinMaxDate(inst, "max"),
1917 years = this._get(inst, "yearRange");
1919 yearSplit = years.split(":");
1920 currentYear = new Date().getFullYear();
1921 minYear = parseInt(yearSplit[0], 10);
1922 maxYear = parseInt(yearSplit[1], 10);
1923 if ( yearSplit[0].match(/[+\-].*/) ) {
1924 minYear += currentYear;
1926 if ( yearSplit[1].match(/[+\-].*/) ) {
1927 maxYear += currentYear;
1931 return ((!minDate || date.getTime() >= minDate.getTime()) &&
1932 (!maxDate || date.getTime() <= maxDate.getTime()) &&
1933 (!minYear || date.getFullYear() >= minYear) &&
1934 (!maxYear || date.getFullYear() <= maxYear));
1937 /* Provide the configuration settings for formatting/parsing. */
1938 _getFormatConfig: function(inst) {
1939 var shortYearCutoff = this._get(inst, "shortYearCutoff");
1940 shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff :
1941 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
1942 return {shortYearCutoff: shortYearCutoff,
1943 dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"),
1944 monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")};
1947 /* Format the given date for display. */
1948 _formatDate: function(inst, day, month, year) {
1950 inst.currentDay = inst.selectedDay;
1951 inst.currentMonth = inst.selectedMonth;
1952 inst.currentYear = inst.selectedYear;
1954 var date = (day ? (typeof day === "object" ? day :
1955 this._daylightSavingAdjust(new Date(year, month, day))) :
1956 this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
1957 return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst));
1962 * Bind hover events for datepicker elements.
1963 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
1964 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
1966 function bindHover(dpDiv) {
1967 var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
1968 return dpDiv.delegate(selector, "mouseout", function() {
1969 $(this).removeClass("ui-state-hover");
1970 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
1971 $(this).removeClass("ui-datepicker-prev-hover");
1973 if (this.className.indexOf("ui-datepicker-next") !== -1) {
1974 $(this).removeClass("ui-datepicker-next-hover");
1977 .delegate(selector, "mouseover", function(){
1978 if (!$.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0])) {
1979 $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
1980 $(this).addClass("ui-state-hover");
1981 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
1982 $(this).addClass("ui-datepicker-prev-hover");
1984 if (this.className.indexOf("ui-datepicker-next") !== -1) {
1985 $(this).addClass("ui-datepicker-next-hover");
1991 /* jQuery extend now ignores nulls! */
1992 function extendRemove(target, props) {
1993 $.extend(target, props);
1994 for (var name in props) {
1995 if (props[name] == null) {
1996 target[name] = props[name];
2002 /* Invoke the datepicker functionality.
2003 @param options string - a command, optionally followed by additional parameters or
2004 Object - settings for attaching new datepicker functionality
2005 @return jQuery object */
2006 $.fn.datepicker = function(options){
2008 /* Verify an empty collection wasn't passed - Fixes #6976 */
2009 if ( !this.length ) {
2013 /* Initialise the date picker. */
2014 if (!$.datepicker.initialized) {
2015 $(document).mousedown($.datepicker._checkExternalClick);
2016 $.datepicker.initialized = true;
2019 /* Append datepicker main container to body if not exist. */
2020 if ($("#"+$.datepicker._mainDivId).length === 0) {
2021 $("body").append($.datepicker.dpDiv);
2024 var otherArgs = Array.prototype.slice.call(arguments, 1);
2025 if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) {
2026 return $.datepicker["_" + options + "Datepicker"].
2027 apply($.datepicker, [this[0]].concat(otherArgs));
2029 if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") {
2030 return $.datepicker["_" + options + "Datepicker"].
2031 apply($.datepicker, [this[0]].concat(otherArgs));
2033 return this.each(function() {
2034 typeof options === "string" ?
2035 $.datepicker["_" + options + "Datepicker"].
2036 apply($.datepicker, [this].concat(otherArgs)) :
2037 $.datepicker._attachDatepicker(this, options);
2041 $.datepicker = new Datepicker(); // singleton instance
2042 $.datepicker.initialized = false;
2043 $.datepicker.uuid = new Date().getTime();
2044 $.datepicker.version = "1.10.2";
2046 // Workaround for #4055
2047 // Add another global to avoid noConflict issues with inline event handlers
2048 window["DP_jQuery_" + dpuuid] = $;