2 * jQuery UI Datepicker 1.10.3
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.3" } });
18 var PROP_NAME = "datepicker",
21 /* Date picker manager.
22 Use the singleton instance of this class, $.datepicker, to interact with the date picker.
23 Settings for (groups of) date pickers are maintained in an instance object,
24 allowing multiple different settings on the same page. */
26 function Datepicker() {
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
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 disabled: false // The initial disabled state
110 $.extend(this._defaults, this.regional[""]);
111 this.dpDiv = bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"));
114 $.extend(Datepicker.prototype, {
115 /* Class name added to elements to indicate already configured with a date picker. */
116 markerClassName: "hasDatepicker",
118 //Keep track of the maximum number of rows displayed (see #7043)
121 // TODO rename to "widget" when switching to widget factory
122 _widgetDatepicker: function() {
126 /* Override the default settings for all instances of the date picker.
127 * @param settings object - the new settings to use as defaults (anonymous object)
128 * @return the manager object
130 setDefaults: function(settings) {
131 extendRemove(this._defaults, settings || {});
135 /* Attach the date picker to a jQuery selection.
136 * @param target element - the target input field or division or span
137 * @param settings object - the new settings to use for this date picker instance (anonymous)
139 _attachDatepicker: function(target, settings) {
140 var nodeName, inline, inst;
141 nodeName = target.nodeName.toLowerCase();
142 inline = (nodeName === "div" || nodeName === "span");
145 target.id = "dp" + this.uuid;
147 inst = this._newInst($(target), inline);
148 inst.settings = $.extend({}, settings || {});
149 if (nodeName === "input") {
150 this._connectDatepicker(target, inst);
152 this._inlineDatepicker(target, inst);
156 /* Create a new instance object. */
157 _newInst: function(target, inline) {
158 var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars
159 return {id: id, input: target, // associated target
160 selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
161 drawMonth: 0, drawYear: 0, // month being drawn
162 inline: inline, // is datepicker inline or not
163 dpDiv: (!inline ? this.dpDiv : // presentation div
164 bindHover($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))};
167 /* Attach the date picker to an input field. */
168 _connectDatepicker: function(target, inst) {
169 var input = $(target);
171 inst.trigger = $([]);
172 if (input.hasClass(this.markerClassName)) {
175 this._attachments(input, inst);
176 input.addClass(this.markerClassName).keydown(this._doKeyDown).
177 keypress(this._doKeyPress).keyup(this._doKeyUp);
178 this._autoSize(inst);
179 $.data(target, PROP_NAME, inst);
180 //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
181 if( inst.settings.disabled ) {
182 this._disableDatepicker( target );
186 /* Make attachments based on settings. */
187 _attachments: function(input, inst) {
188 var showOn, buttonText, buttonImage,
189 appendText = this._get(inst, "appendText"),
190 isRTL = this._get(inst, "isRTL");
193 inst.append.remove();
196 inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
197 input[isRTL ? "before" : "after"](inst.append);
200 input.unbind("focus", this._showDatepicker);
203 inst.trigger.remove();
206 showOn = this._get(inst, "showOn");
207 if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
208 input.focus(this._showDatepicker);
210 if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
211 buttonText = this._get(inst, "buttonText");
212 buttonImage = this._get(inst, "buttonImage");
213 inst.trigger = $(this._get(inst, "buttonImageOnly") ?
214 $("<img/>").addClass(this._triggerClass).
215 attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
216 $("<button type='button'></button>").addClass(this._triggerClass).
217 html(!buttonImage ? buttonText : $("<img/>").attr(
218 { src:buttonImage, alt:buttonText, title:buttonText })));
219 input[isRTL ? "before" : "after"](inst.trigger);
220 inst.trigger.click(function() {
221 if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) {
222 $.datepicker._hideDatepicker();
223 } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) {
224 $.datepicker._hideDatepicker();
225 $.datepicker._showDatepicker(input[0]);
227 $.datepicker._showDatepicker(input[0]);
234 /* Apply the maximum length for the date format. */
235 _autoSize: function(inst) {
236 if (this._get(inst, "autoSize") && !inst.inline) {
237 var findMax, max, maxI, i,
238 date = new Date(2009, 12 - 1, 20), // Ensure double digits
239 dateFormat = this._get(inst, "dateFormat");
241 if (dateFormat.match(/[DM]/)) {
242 findMax = function(names) {
245 for (i = 0; i < names.length; i++) {
246 if (names[i].length > max) {
247 max = names[i].length;
253 date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
254 "monthNames" : "monthNamesShort"))));
255 date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
256 "dayNames" : "dayNamesShort"))) + 20 - date.getDay());
258 inst.input.attr("size", this._formatDate(inst, date).length);
262 /* Attach an inline date picker to a div. */
263 _inlineDatepicker: function(target, inst) {
264 var divSpan = $(target);
265 if (divSpan.hasClass(this.markerClassName)) {
268 divSpan.addClass(this.markerClassName).append(inst.dpDiv);
269 $.data(target, PROP_NAME, inst);
270 this._setDate(inst, this._getDefaultDate(inst), true);
271 this._updateDatepicker(inst);
272 this._updateAlternate(inst);
273 //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
274 if( inst.settings.disabled ) {
275 this._disableDatepicker( target );
277 // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
278 // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
279 inst.dpDiv.css( "display", "block" );
282 /* Pop-up the date picker in a "dialog" box.
283 * @param input element - ignored
284 * @param date string or Date - the initial date to display
285 * @param onSelect function - the function to call when a date is selected
286 * @param settings object - update the dialog date picker instance's settings (anonymous object)
287 * @param pos int[2] - coordinates for the dialog's position within the screen or
288 * event - with x/y coordinates or
289 * leave empty for default (screen centre)
290 * @return the manager object
292 _dialogDatepicker: function(input, date, onSelect, settings, pos) {
293 var id, browserWidth, browserHeight, scrollX, scrollY,
294 inst = this._dialogInst; // internal instance
298 id = "dp" + this.uuid;
299 this._dialogInput = $("<input type='text' id='" + id +
300 "' style='position: absolute; top: -100px; width: 0px;'/>");
301 this._dialogInput.keydown(this._doKeyDown);
302 $("body").append(this._dialogInput);
303 inst = this._dialogInst = this._newInst(this._dialogInput, false);
305 $.data(this._dialogInput[0], PROP_NAME, inst);
307 extendRemove(inst.settings, settings || {});
308 date = (date && date.constructor === Date ? this._formatDate(inst, date) : date);
309 this._dialogInput.val(date);
311 this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
313 browserWidth = document.documentElement.clientWidth;
314 browserHeight = document.documentElement.clientHeight;
315 scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
316 scrollY = document.documentElement.scrollTop || document.body.scrollTop;
317 this._pos = // should use actual width/height below
318 [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
321 // move input on screen for focus, but hidden behind dialog
322 this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px");
323 inst.settings.onSelect = onSelect;
324 this._inDialog = true;
325 this.dpDiv.addClass(this._dialogClass);
326 this._showDatepicker(this._dialogInput[0]);
328 $.blockUI(this.dpDiv);
330 $.data(this._dialogInput[0], PROP_NAME, inst);
334 /* Detach a datepicker from its control.
335 * @param target element - the target input field or division or span
337 _destroyDatepicker: function(target) {
340 inst = $.data(target, PROP_NAME);
342 if (!$target.hasClass(this.markerClassName)) {
346 nodeName = target.nodeName.toLowerCase();
347 $.removeData(target, PROP_NAME);
348 if (nodeName === "input") {
349 inst.append.remove();
350 inst.trigger.remove();
351 $target.removeClass(this.markerClassName).
352 unbind("focus", this._showDatepicker).
353 unbind("keydown", this._doKeyDown).
354 unbind("keypress", this._doKeyPress).
355 unbind("keyup", this._doKeyUp);
356 } else if (nodeName === "div" || nodeName === "span") {
357 $target.removeClass(this.markerClassName).empty();
361 /* Enable the date picker to a jQuery selection.
362 * @param target element - the target input field or division or span
364 _enableDatepicker: function(target) {
365 var nodeName, inline,
367 inst = $.data(target, PROP_NAME);
369 if (!$target.hasClass(this.markerClassName)) {
373 nodeName = target.nodeName.toLowerCase();
374 if (nodeName === "input") {
375 target.disabled = false;
376 inst.trigger.filter("button").
377 each(function() { this.disabled = false; }).end().
378 filter("img").css({opacity: "1.0", cursor: ""});
379 } else if (nodeName === "div" || nodeName === "span") {
380 inline = $target.children("." + this._inlineClass);
381 inline.children().removeClass("ui-state-disabled");
382 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
383 prop("disabled", false);
385 this._disabledInputs = $.map(this._disabledInputs,
386 function(value) { return (value === target ? null : value); }); // delete entry
389 /* Disable the date picker to a jQuery selection.
390 * @param target element - the target input field or division or span
392 _disableDatepicker: function(target) {
393 var nodeName, inline,
395 inst = $.data(target, PROP_NAME);
397 if (!$target.hasClass(this.markerClassName)) {
401 nodeName = target.nodeName.toLowerCase();
402 if (nodeName === "input") {
403 target.disabled = true;
404 inst.trigger.filter("button").
405 each(function() { this.disabled = true; }).end().
406 filter("img").css({opacity: "0.5", cursor: "default"});
407 } else if (nodeName === "div" || nodeName === "span") {
408 inline = $target.children("." + this._inlineClass);
409 inline.children().addClass("ui-state-disabled");
410 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
411 prop("disabled", true);
413 this._disabledInputs = $.map(this._disabledInputs,
414 function(value) { return (value === target ? null : value); }); // delete entry
415 this._disabledInputs[this._disabledInputs.length] = target;
418 /* Is the first field in a jQuery collection disabled as a datepicker?
419 * @param target element - the target input field or division or span
420 * @return boolean - true if disabled, false if enabled
422 _isDisabledDatepicker: function(target) {
426 for (var i = 0; i < this._disabledInputs.length; i++) {
427 if (this._disabledInputs[i] === target) {
434 /* Retrieve the instance data for the target control.
435 * @param target element - the target input field or division or span
436 * @return object - the associated instance data
437 * @throws error if a jQuery problem getting data
439 _getInst: function(target) {
441 return $.data(target, PROP_NAME);
444 throw "Missing instance data for this datepicker";
448 /* Update or retrieve the settings for a date picker attached to an input field or division.
449 * @param target element - the target input field or division or span
450 * @param name object - the new settings to update or
451 * string - the name of the setting to change or retrieve,
452 * when retrieving also "all" for all instance settings or
453 * "defaults" for all global defaults
454 * @param value any - the new value for the setting
455 * (omit if above is an object or to retrieve a value)
457 _optionDatepicker: function(target, name, value) {
458 var settings, date, minDate, maxDate,
459 inst = this._getInst(target);
461 if (arguments.length === 2 && typeof name === "string") {
462 return (name === "defaults" ? $.extend({}, $.datepicker._defaults) :
463 (inst ? (name === "all" ? $.extend({}, inst.settings) :
464 this._get(inst, name)) : null));
467 settings = name || {};
468 if (typeof name === "string") {
470 settings[name] = value;
474 if (this._curInst === inst) {
475 this._hideDatepicker();
478 date = this._getDateDatepicker(target, true);
479 minDate = this._getMinMaxDate(inst, "min");
480 maxDate = this._getMinMaxDate(inst, "max");
481 extendRemove(inst.settings, settings);
482 // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
483 if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
484 inst.settings.minDate = this._formatDate(inst, minDate);
486 if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
487 inst.settings.maxDate = this._formatDate(inst, maxDate);
489 if ( "disabled" in settings ) {
490 if ( settings.disabled ) {
491 this._disableDatepicker(target);
493 this._enableDatepicker(target);
496 this._attachments($(target), inst);
497 this._autoSize(inst);
498 this._setDate(inst, date);
499 this._updateAlternate(inst);
500 this._updateDatepicker(inst);
504 // change method deprecated
505 _changeDatepicker: function(target, name, value) {
506 this._optionDatepicker(target, name, value);
509 /* Redraw the date picker attached to an input field or division.
510 * @param target element - the target input field or division or span
512 _refreshDatepicker: function(target) {
513 var inst = this._getInst(target);
515 this._updateDatepicker(inst);
519 /* Set the dates for a jQuery selection.
520 * @param target element - the target input field or division or span
521 * @param date Date - the new date
523 _setDateDatepicker: function(target, date) {
524 var inst = this._getInst(target);
526 this._setDate(inst, date);
527 this._updateDatepicker(inst);
528 this._updateAlternate(inst);
532 /* Get the date(s) for the first entry in a jQuery selection.
533 * @param target element - the target input field or division or span
534 * @param noDefault boolean - true if no default date is to be used
535 * @return Date - the current date
537 _getDateDatepicker: function(target, noDefault) {
538 var inst = this._getInst(target);
539 if (inst && !inst.inline) {
540 this._setDateFromField(inst, noDefault);
542 return (inst ? this._getDate(inst) : null);
545 /* Handle keystrokes. */
546 _doKeyDown: function(event) {
547 var onSelect, dateStr, sel,
548 inst = $.datepicker._getInst(event.target),
550 isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
552 inst._keyEvent = true;
553 if ($.datepicker._datepickerShowing) {
554 switch (event.keyCode) {
555 case 9: $.datepicker._hideDatepicker();
557 break; // hide on tab out
558 case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." +
559 $.datepicker._currentClass + ")", inst.dpDiv);
561 $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
564 onSelect = $.datepicker._get(inst, "onSelect");
566 dateStr = $.datepicker._formatDate(inst);
568 // trigger custom callback
569 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
571 $.datepicker._hideDatepicker();
574 return false; // don't submit the form
575 case 27: $.datepicker._hideDatepicker();
576 break; // hide on escape
577 case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
578 -$.datepicker._get(inst, "stepBigMonths") :
579 -$.datepicker._get(inst, "stepMonths")), "M");
580 break; // previous month/year on page up/+ ctrl
581 case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
582 +$.datepicker._get(inst, "stepBigMonths") :
583 +$.datepicker._get(inst, "stepMonths")), "M");
584 break; // next month/year on page down/+ ctrl
585 case 35: if (event.ctrlKey || event.metaKey) {
586 $.datepicker._clearDate(event.target);
588 handled = event.ctrlKey || event.metaKey;
589 break; // clear on ctrl or command +end
590 case 36: if (event.ctrlKey || event.metaKey) {
591 $.datepicker._gotoToday(event.target);
593 handled = event.ctrlKey || event.metaKey;
594 break; // current on ctrl or command +home
595 case 37: if (event.ctrlKey || event.metaKey) {
596 $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D");
598 handled = event.ctrlKey || event.metaKey;
599 // -1 day on ctrl or command +left
600 if (event.originalEvent.altKey) {
601 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
602 -$.datepicker._get(inst, "stepBigMonths") :
603 -$.datepicker._get(inst, "stepMonths")), "M");
605 // next month/year on alt +left on Mac
607 case 38: if (event.ctrlKey || event.metaKey) {
608 $.datepicker._adjustDate(event.target, -7, "D");
610 handled = event.ctrlKey || event.metaKey;
611 break; // -1 week on ctrl or command +up
612 case 39: if (event.ctrlKey || event.metaKey) {
613 $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D");
615 handled = event.ctrlKey || event.metaKey;
616 // +1 day on ctrl or command +right
617 if (event.originalEvent.altKey) {
618 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
619 +$.datepicker._get(inst, "stepBigMonths") :
620 +$.datepicker._get(inst, "stepMonths")), "M");
622 // next month/year on alt +right
624 case 40: if (event.ctrlKey || event.metaKey) {
625 $.datepicker._adjustDate(event.target, +7, "D");
627 handled = event.ctrlKey || event.metaKey;
628 break; // +1 week on ctrl or command +down
629 default: handled = false;
631 } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
632 $.datepicker._showDatepicker(this);
638 event.preventDefault();
639 event.stopPropagation();
643 /* Filter entered characters - based on date format. */
644 _doKeyPress: function(event) {
646 inst = $.datepicker._getInst(event.target);
648 if ($.datepicker._get(inst, "constrainInput")) {
649 chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat"));
650 chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode);
651 return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1);
655 /* Synchronise manual entry and field/alternate field. */
656 _doKeyUp: function(event) {
658 inst = $.datepicker._getInst(event.target);
660 if (inst.input.val() !== inst.lastVal) {
662 date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
663 (inst.input ? inst.input.val() : null),
664 $.datepicker._getFormatConfig(inst));
666 if (date) { // only if valid
667 $.datepicker._setDateFromField(inst);
668 $.datepicker._updateAlternate(inst);
669 $.datepicker._updateDatepicker(inst);
678 /* Pop-up the date picker for a given input field.
679 * If false returned from beforeShow event handler do not show.
680 * @param input element - the input field attached to the date picker or
681 * event - if triggered by focus
683 _showDatepicker: function(input) {
684 input = input.target || input;
685 if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger
686 input = $("input", input.parentNode)[0];
689 if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here
693 var inst, beforeShow, beforeShowSettings, isFixed,
694 offset, showAnim, duration;
696 inst = $.datepicker._getInst(input);
697 if ($.datepicker._curInst && $.datepicker._curInst !== inst) {
698 $.datepicker._curInst.dpDiv.stop(true, true);
699 if ( inst && $.datepicker._datepickerShowing ) {
700 $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
704 beforeShow = $.datepicker._get(inst, "beforeShow");
705 beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
706 if(beforeShowSettings === false){
709 extendRemove(inst.settings, beforeShowSettings);
712 $.datepicker._lastInput = input;
713 $.datepicker._setDateFromField(inst);
715 if ($.datepicker._inDialog) { // hide cursor
718 if (!$.datepicker._pos) { // position below input
719 $.datepicker._pos = $.datepicker._findPos(input);
720 $.datepicker._pos[1] += input.offsetHeight; // add the height
724 $(input).parents().each(function() {
725 isFixed |= $(this).css("position") === "fixed";
729 offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
730 $.datepicker._pos = null;
731 //to avoid flashes on Firefox
733 // determine sizing offscreen
734 inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"});
735 $.datepicker._updateDatepicker(inst);
736 // fix width for dynamic number of date pickers
737 // and adjust position before showing
738 offset = $.datepicker._checkOffset(inst, offset, isFixed);
739 inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
740 "static" : (isFixed ? "fixed" : "absolute")), display: "none",
741 left: offset.left + "px", top: offset.top + "px"});
744 showAnim = $.datepicker._get(inst, "showAnim");
745 duration = $.datepicker._get(inst, "duration");
746 inst.dpDiv.zIndex($(input).zIndex()+1);
747 $.datepicker._datepickerShowing = true;
749 if ( $.effects && $.effects.effect[ showAnim ] ) {
750 inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration);
752 inst.dpDiv[showAnim || "show"](showAnim ? duration : null);
755 if ( $.datepicker._shouldFocusInput( inst ) ) {
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 if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
789 // deffered render of the years select (to avoid flashes on Firefox)
790 if( inst.yearshtml ){
791 origyearshtml = inst.yearshtml;
792 setTimeout(function(){
793 //assure that inst.yearshtml didn't change.
794 if( origyearshtml === inst.yearshtml && inst.yearshtml ){
795 inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml);
797 origyearshtml = inst.yearshtml = null;
802 // #6694 - don't focus the input if it's already focused
803 // this breaks the change event in IE
804 // Support: IE and jQuery <1.9
805 _shouldFocusInput: function( inst ) {
806 return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
809 /* Check positioning to remain on screen. */
810 _checkOffset: function(inst, offset, isFixed) {
811 var dpWidth = inst.dpDiv.outerWidth(),
812 dpHeight = inst.dpDiv.outerHeight(),
813 inputWidth = inst.input ? inst.input.outerWidth() : 0,
814 inputHeight = inst.input ? inst.input.outerHeight() : 0,
815 viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()),
816 viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
818 offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0);
819 offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0;
820 offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
822 // now check if datepicker is showing outside window viewport - move to a better place if so.
823 offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
824 Math.abs(offset.left + dpWidth - viewWidth) : 0);
825 offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
826 Math.abs(dpHeight + inputHeight) : 0);
831 /* Find an object's position on the screen. */
832 _findPos: function(obj) {
834 inst = this._getInst(obj),
835 isRTL = this._get(inst, "isRTL");
837 while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
838 obj = obj[isRTL ? "previousSibling" : "nextSibling"];
841 position = $(obj).offset();
842 return [position.left, position.top];
845 /* Hide the date picker from view.
846 * @param input element - the input field attached to the date picker
848 _hideDatepicker: function(input) {
849 var showAnim, duration, postProcess, onClose,
850 inst = this._curInst;
852 if (!inst || (input && inst !== $.data(input, PROP_NAME))) {
856 if (this._datepickerShowing) {
857 showAnim = this._get(inst, "showAnim");
858 duration = this._get(inst, "duration");
859 postProcess = function() {
860 $.datepicker._tidyDialog(inst);
863 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
864 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
865 inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess);
867 inst.dpDiv[(showAnim === "slideDown" ? "slideUp" :
868 (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess);
874 this._datepickerShowing = false;
876 onClose = this._get(inst, "onClose");
878 onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]);
881 this._lastInput = null;
882 if (this._inDialog) {
883 this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" });
886 $("body").append(this.dpDiv);
889 this._inDialog = false;
893 /* Tidy up after a dialog display. */
894 _tidyDialog: function(inst) {
895 inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar");
898 /* Close date picker if clicked elsewhere. */
899 _checkExternalClick: function(event) {
900 if (!$.datepicker._curInst) {
904 var $target = $(event.target),
905 inst = $.datepicker._getInst($target[0]);
907 if ( ( ( $target[0].id !== $.datepicker._mainDivId &&
908 $target.parents("#" + $.datepicker._mainDivId).length === 0 &&
909 !$target.hasClass($.datepicker.markerClassName) &&
910 !$target.closest("." + $.datepicker._triggerClass).length &&
911 $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
912 ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) {
913 $.datepicker._hideDatepicker();
917 /* Adjust one of the date sub-fields. */
918 _adjustDate: function(id, offset, period) {
920 inst = this._getInst(target[0]);
922 if (this._isDisabledDatepicker(target[0])) {
925 this._adjustInstDate(inst, offset +
926 (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning
928 this._updateDatepicker(inst);
931 /* Action for current link. */
932 _gotoToday: function(id) {
935 inst = this._getInst(target[0]);
937 if (this._get(inst, "gotoCurrent") && inst.currentDay) {
938 inst.selectedDay = inst.currentDay;
939 inst.drawMonth = inst.selectedMonth = inst.currentMonth;
940 inst.drawYear = inst.selectedYear = inst.currentYear;
943 inst.selectedDay = date.getDate();
944 inst.drawMonth = inst.selectedMonth = date.getMonth();
945 inst.drawYear = inst.selectedYear = date.getFullYear();
947 this._notifyChange(inst);
948 this._adjustDate(target);
951 /* Action for selecting a new month/year. */
952 _selectMonthYear: function(id, select, period) {
954 inst = this._getInst(target[0]);
956 inst["selected" + (period === "M" ? "Month" : "Year")] =
957 inst["draw" + (period === "M" ? "Month" : "Year")] =
958 parseInt(select.options[select.selectedIndex].value,10);
960 this._notifyChange(inst);
961 this._adjustDate(target);
964 /* Action for selecting a day. */
965 _selectDay: function(id, month, year, td) {
969 if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
973 inst = this._getInst(target[0]);
974 inst.selectedDay = inst.currentDay = $("a", td).html();
975 inst.selectedMonth = inst.currentMonth = month;
976 inst.selectedYear = inst.currentYear = year;
977 this._selectDate(id, this._formatDate(inst,
978 inst.currentDay, inst.currentMonth, inst.currentYear));
981 /* Erase the input field and hide the date picker. */
982 _clearDate: function(id) {
984 this._selectDate(target, "");
987 /* Update the input field with the selected date. */
988 _selectDate: function(id, dateStr) {
991 inst = this._getInst(target[0]);
993 dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
995 inst.input.val(dateStr);
997 this._updateAlternate(inst);
999 onSelect = this._get(inst, "onSelect");
1001 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
1002 } else if (inst.input) {
1003 inst.input.trigger("change"); // fire the change event
1007 this._updateDatepicker(inst);
1009 this._hideDatepicker();
1010 this._lastInput = inst.input[0];
1011 if (typeof(inst.input[0]) !== "object") {
1012 inst.input.focus(); // restore focus
1014 this._lastInput = null;
1018 /* Update any alternate field to synchronise with the main field. */
1019 _updateAlternate: function(inst) {
1020 var altFormat, date, dateStr,
1021 altField = this._get(inst, "altField");
1023 if (altField) { // update alternate field too
1024 altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat");
1025 date = this._getDate(inst);
1026 dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
1027 $(altField).each(function() { $(this).val(dateStr); });
1031 /* Set as beforeShowDay function to prevent selection of weekends.
1032 * @param date Date - the date to customise
1033 * @return [boolean, string] - is this date selectable?, what is its CSS class?
1035 noWeekends: function(date) {
1036 var day = date.getDay();
1037 return [(day > 0 && day < 6), ""];
1040 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
1041 * @param date Date - the date to get the week for
1042 * @return number - the number of the week within the year that contains this date
1044 iso8601Week: function(date) {
1046 checkDate = new Date(date.getTime());
1048 // Find Thursday of this week starting on Monday
1049 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
1051 time = checkDate.getTime();
1052 checkDate.setMonth(0); // Compare with Jan 1
1053 checkDate.setDate(1);
1054 return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
1057 /* Parse a string value into a date object.
1058 * See formatDate below for the possible formats.
1060 * @param format string - the expected format of the date
1061 * @param value string - the date in the above format
1062 * @param settings Object - attributes include:
1063 * shortYearCutoff number - the cutoff year for determining the century (optional)
1064 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
1065 * dayNames string[7] - names of the days from Sunday (optional)
1066 * monthNamesShort string[12] - abbreviated names of the months (optional)
1067 * monthNames string[12] - names of the months (optional)
1068 * @return Date - the extracted date value or null if value is blank
1070 parseDate: function (format, value, settings) {
1071 if (format == null || value == null) {
1072 throw "Invalid arguments";
1075 value = (typeof value === "object" ? value.toString() : value + "");
1080 var iFormat, dim, extra,
1082 shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff,
1083 shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
1084 new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)),
1085 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
1086 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
1087 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
1088 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
1095 // Check whether a format character is doubled
1096 lookAhead = function(match) {
1097 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
1103 // Extract a number from the string value
1104 getNumber = function(match) {
1105 var isDoubled = lookAhead(match),
1106 size = (match === "@" ? 14 : (match === "!" ? 20 :
1107 (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))),
1108 digits = new RegExp("^\\d{1," + size + "}"),
1109 num = value.substring(iValue).match(digits);
1111 throw "Missing number at position " + iValue;
1113 iValue += num[0].length;
1114 return parseInt(num[0], 10);
1116 // Extract a name from the string value and convert to an index
1117 getName = function(match, shortNames, longNames) {
1119 names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
1121 }).sort(function (a, b) {
1122 return -(a[1].length - b[1].length);
1125 $.each(names, function (i, pair) {
1127 if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) {
1129 iValue += name.length;
1136 throw "Unknown name at position " + iValue;
1139 // Confirm that a literal character matches the string value
1140 checkLiteral = function() {
1141 if (value.charAt(iValue) !== format.charAt(iFormat)) {
1142 throw "Unexpected literal at position " + iValue;
1147 for (iFormat = 0; iFormat < format.length; iFormat++) {
1149 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
1155 switch (format.charAt(iFormat)) {
1157 day = getNumber("d");
1160 getName("D", dayNamesShort, dayNames);
1163 doy = getNumber("o");
1166 month = getNumber("m");
1169 month = getName("M", monthNamesShort, monthNames);
1172 year = getNumber("y");
1175 date = new Date(getNumber("@"));
1176 year = date.getFullYear();
1177 month = date.getMonth() + 1;
1178 day = date.getDate();
1181 date = new Date((getNumber("!") - this._ticksTo1970) / 10000);
1182 year = date.getFullYear();
1183 month = date.getMonth() + 1;
1184 day = date.getDate();
1187 if (lookAhead("'")){
1199 if (iValue < value.length){
1200 extra = value.substr(iValue);
1201 if (!/^\s+/.test(extra)) {
1202 throw "Extra/unparsed characters found in date: " + extra;
1207 year = new Date().getFullYear();
1208 } else if (year < 100) {
1209 year += new Date().getFullYear() - new Date().getFullYear() % 100 +
1210 (year <= shortYearCutoff ? 0 : -100);
1217 dim = this._getDaysInMonth(year, month - 1);
1226 date = this._daylightSavingAdjust(new Date(year, month - 1, day));
1227 if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
1228 throw "Invalid date"; // E.g. 31/02/00
1233 /* Standard date formats. */
1234 ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
1235 COOKIE: "D, dd M yy",
1236 ISO_8601: "yy-mm-dd",
1237 RFC_822: "D, d M y",
1238 RFC_850: "DD, dd-M-y",
1239 RFC_1036: "D, d M y",
1240 RFC_1123: "D, d M yy",
1241 RFC_2822: "D, d M yy",
1242 RSS: "D, d M y", // RFC 822
1245 W3C: "yy-mm-dd", // ISO 8601
1247 _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
1248 Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
1250 /* Format a date object into a string value.
1251 * The format can be combinations of the following:
1252 * d - day of month (no leading zero)
1253 * dd - day of month (two digit)
1254 * o - day of year (no leading zeros)
1255 * oo - day of year (three digit)
1256 * D - day name short
1257 * DD - day name long
1258 * m - month of year (no leading zero)
1259 * mm - month of year (two digit)
1260 * M - month name short
1261 * MM - month name long
1262 * y - year (two digit)
1263 * yy - year (four digit)
1264 * @ - Unix timestamp (ms since 01/01/1970)
1265 * ! - Windows ticks (100ns since 01/01/0001)
1266 * "..." - literal text
1269 * @param format string - the desired format of the date
1270 * @param date Date - the date value to format
1271 * @param settings Object - attributes include:
1272 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
1273 * dayNames string[7] - names of the days from Sunday (optional)
1274 * monthNamesShort string[12] - abbreviated names of the months (optional)
1275 * monthNames string[12] - names of the months (optional)
1276 * @return string - the date in the above format
1278 formatDate: function (format, date, settings) {
1284 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
1285 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
1286 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
1287 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
1288 // Check whether a format character is doubled
1289 lookAhead = function(match) {
1290 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
1296 // Format a number, with leading zero if necessary
1297 formatNumber = function(match, value, len) {
1298 var num = "" + value;
1299 if (lookAhead(match)) {
1300 while (num.length < len) {
1306 // Format a name, short or long as requested
1307 formatName = function(match, value, shortNames, longNames) {
1308 return (lookAhead(match) ? longNames[value] : shortNames[value]);
1314 for (iFormat = 0; iFormat < format.length; iFormat++) {
1316 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
1319 output += format.charAt(iFormat);
1322 switch (format.charAt(iFormat)) {
1324 output += formatNumber("d", date.getDate(), 2);
1327 output += formatName("D", date.getDay(), dayNamesShort, dayNames);
1330 output += formatNumber("o",
1331 Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
1334 output += formatNumber("m", date.getMonth() + 1, 2);
1337 output += formatName("M", date.getMonth(), monthNamesShort, monthNames);
1340 output += (lookAhead("y") ? date.getFullYear() :
1341 (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100);
1344 output += date.getTime();
1347 output += date.getTime() * 10000 + this._ticksTo1970;
1350 if (lookAhead("'")) {
1357 output += format.charAt(iFormat);
1365 /* Extract all possible characters from the date format. */
1366 _possibleChars: function (format) {
1370 // Check whether a format character is doubled
1371 lookAhead = function(match) {
1372 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
1379 for (iFormat = 0; iFormat < format.length; iFormat++) {
1381 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
1384 chars += format.charAt(iFormat);
1387 switch (format.charAt(iFormat)) {
1388 case "d": case "m": case "y": case "@":
1389 chars += "0123456789";
1392 return null; // Accept anything
1394 if (lookAhead("'")) {
1401 chars += format.charAt(iFormat);
1408 /* Get a setting value, defaulting if necessary. */
1409 _get: function(inst, name) {
1410 return inst.settings[name] !== undefined ?
1411 inst.settings[name] : this._defaults[name];
1414 /* Parse existing date and initialise date picker. */
1415 _setDateFromField: function(inst, noDefault) {
1416 if (inst.input.val() === inst.lastVal) {
1420 var dateFormat = this._get(inst, "dateFormat"),
1421 dates = inst.lastVal = inst.input ? inst.input.val() : null,
1422 defaultDate = this._getDefaultDate(inst),
1424 settings = this._getFormatConfig(inst);
1427 date = this.parseDate(dateFormat, dates, settings) || defaultDate;
1429 dates = (noDefault ? "" : dates);
1431 inst.selectedDay = date.getDate();
1432 inst.drawMonth = inst.selectedMonth = date.getMonth();
1433 inst.drawYear = inst.selectedYear = date.getFullYear();
1434 inst.currentDay = (dates ? date.getDate() : 0);
1435 inst.currentMonth = (dates ? date.getMonth() : 0);
1436 inst.currentYear = (dates ? date.getFullYear() : 0);
1437 this._adjustInstDate(inst);
1440 /* Retrieve the default date shown on opening. */
1441 _getDefaultDate: function(inst) {
1442 return this._restrictMinMax(inst,
1443 this._determineDate(inst, this._get(inst, "defaultDate"), new Date()));
1446 /* A date may be specified as an exact value or a relative one. */
1447 _determineDate: function(inst, date, defaultDate) {
1448 var offsetNumeric = function(offset) {
1449 var date = new Date();
1450 date.setDate(date.getDate() + offset);
1453 offsetString = function(offset) {
1455 return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
1456 offset, $.datepicker._getFormatConfig(inst));
1462 var date = (offset.toLowerCase().match(/^c/) ?
1463 $.datepicker._getDate(inst) : null) || new Date(),
1464 year = date.getFullYear(),
1465 month = date.getMonth(),
1466 day = date.getDate(),
1467 pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
1468 matches = pattern.exec(offset);
1471 switch (matches[2] || "d") {
1472 case "d" : case "D" :
1473 day += parseInt(matches[1],10); break;
1474 case "w" : case "W" :
1475 day += parseInt(matches[1],10) * 7; break;
1476 case "m" : case "M" :
1477 month += parseInt(matches[1],10);
1478 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
1480 case "y": case "Y" :
1481 year += parseInt(matches[1],10);
1482 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
1485 matches = pattern.exec(offset);
1487 return new Date(year, month, day);
1489 newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) :
1490 (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
1492 newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate);
1494 newDate.setHours(0);
1495 newDate.setMinutes(0);
1496 newDate.setSeconds(0);
1497 newDate.setMilliseconds(0);
1499 return this._daylightSavingAdjust(newDate);
1502 /* Handle switch to/from daylight saving.
1503 * Hours may be non-zero on daylight saving cut-over:
1504 * > 12 when midnight changeover, but then cannot generate
1505 * midnight datetime, so jump to 1AM, otherwise reset.
1506 * @param date (Date) the date to check
1507 * @return (Date) the corrected date
1509 _daylightSavingAdjust: function(date) {
1513 date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
1517 /* Set the date(s) directly. */
1518 _setDate: function(inst, date, noChange) {
1520 origMonth = inst.selectedMonth,
1521 origYear = inst.selectedYear,
1522 newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
1524 inst.selectedDay = inst.currentDay = newDate.getDate();
1525 inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
1526 inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
1527 if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) {
1528 this._notifyChange(inst);
1530 this._adjustInstDate(inst);
1532 inst.input.val(clear ? "" : this._formatDate(inst));
1536 /* Retrieve the date(s) directly. */
1537 _getDate: function(inst) {
1538 var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null :
1539 this._daylightSavingAdjust(new Date(
1540 inst.currentYear, inst.currentMonth, inst.currentDay)));
1544 /* Attach the onxxx handlers. These are declared statically so
1545 * they work with static code transformers like Caja.
1547 _attachHandlers: function(inst) {
1548 var stepMonths = this._get(inst, "stepMonths"),
1549 id = "#" + inst.id.replace( /\\\\/g, "\\" );
1550 inst.dpDiv.find("[data-handler]").map(function () {
1553 $.datepicker._adjustDate(id, -stepMonths, "M");
1556 $.datepicker._adjustDate(id, +stepMonths, "M");
1559 $.datepicker._hideDatepicker();
1561 today: function () {
1562 $.datepicker._gotoToday(id);
1564 selectDay: function () {
1565 $.datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this);
1568 selectMonth: function () {
1569 $.datepicker._selectMonthYear(id, this, "M");
1572 selectYear: function () {
1573 $.datepicker._selectMonthYear(id, this, "Y");
1577 $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]);
1581 /* Generate the HTML for the current state of the date picker. */
1582 _generateHTML: function(inst) {
1583 var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
1584 controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
1585 monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
1586 selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
1587 cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
1588 printDate, dRow, tbody, daySettings, otherMonth, unselectable,
1589 tempDate = new Date(),
1590 today = this._daylightSavingAdjust(
1591 new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time
1592 isRTL = this._get(inst, "isRTL"),
1593 showButtonPanel = this._get(inst, "showButtonPanel"),
1594 hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"),
1595 navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"),
1596 numMonths = this._getNumberOfMonths(inst),
1597 showCurrentAtPos = this._get(inst, "showCurrentAtPos"),
1598 stepMonths = this._get(inst, "stepMonths"),
1599 isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1),
1600 currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
1601 new Date(inst.currentYear, inst.currentMonth, inst.currentDay))),
1602 minDate = this._getMinMaxDate(inst, "min"),
1603 maxDate = this._getMinMaxDate(inst, "max"),
1604 drawMonth = inst.drawMonth - showCurrentAtPos,
1605 drawYear = inst.drawYear;
1607 if (drawMonth < 0) {
1612 maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
1613 maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
1614 maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
1615 while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
1617 if (drawMonth < 0) {
1623 inst.drawMonth = drawMonth;
1624 inst.drawYear = drawYear;
1626 prevText = this._get(inst, "prevText");
1627 prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
1628 this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
1629 this._getFormatConfig(inst)));
1631 prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
1632 "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
1633 " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" :
1634 (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>"));
1636 nextText = this._get(inst, "nextText");
1637 nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
1638 this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
1639 this._getFormatConfig(inst)));
1641 next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
1642 "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
1643 " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" :
1644 (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>"));
1646 currentText = this._get(inst, "currentText");
1647 gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today);
1648 currentText = (!navigationAsDateFormat ? currentText :
1649 this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
1651 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'>" +
1652 this._get(inst, "closeText") + "</button>" : "");
1654 buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") +
1655 (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'" +
1656 ">" + currentText + "</button>" : "") + (isRTL ? "" : controls) + "</div>" : "";
1658 firstDay = parseInt(this._get(inst, "firstDay"),10);
1659 firstDay = (isNaN(firstDay) ? 0 : firstDay);
1661 showWeek = this._get(inst, "showWeek");
1662 dayNames = this._get(inst, "dayNames");
1663 dayNamesMin = this._get(inst, "dayNamesMin");
1664 monthNames = this._get(inst, "monthNames");
1665 monthNamesShort = this._get(inst, "monthNamesShort");
1666 beforeShowDay = this._get(inst, "beforeShowDay");
1667 showOtherMonths = this._get(inst, "showOtherMonths");
1668 selectOtherMonths = this._get(inst, "selectOtherMonths");
1669 defaultDate = this._getDefaultDate(inst);
1672 for (row = 0; row < numMonths[0]; row++) {
1675 for (col = 0; col < numMonths[1]; col++) {
1676 selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
1677 cornerClass = " ui-corner-all";
1680 calender += "<div class='ui-datepicker-group";
1681 if (numMonths[1] > 1) {
1683 case 0: calender += " ui-datepicker-group-first";
1684 cornerClass = " ui-corner-" + (isRTL ? "right" : "left"); break;
1685 case numMonths[1]-1: calender += " ui-datepicker-group-last";
1686 cornerClass = " ui-corner-" + (isRTL ? "left" : "right"); break;
1687 default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
1692 calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
1693 (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") +
1694 (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") +
1695 this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
1696 row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
1697 "</div><table class='ui-datepicker-calendar'><thead>" +
1699 thead = (showWeek ? "<th class='ui-datepicker-week-col'>" + this._get(inst, "weekHeader") + "</th>" : "");
1700 for (dow = 0; dow < 7; dow++) { // days of the week
1701 day = (dow + firstDay) % 7;
1702 thead += "<th" + ((dow + firstDay + 6) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "") + ">" +
1703 "<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>";
1705 calender += thead + "</tr></thead><tbody>";
1706 daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
1707 if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) {
1708 inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
1710 leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
1711 curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
1712 numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
1713 this.maxRows = numRows;
1714 printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
1715 for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows
1717 tbody = (!showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
1718 this._get(inst, "calculateWeek")(printDate) + "</td>");
1719 for (dow = 0; dow < 7; dow++) { // create date picker days
1720 daySettings = (beforeShowDay ?
1721 beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]);
1722 otherMonth = (printDate.getMonth() !== drawMonth);
1723 unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
1724 (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
1725 tbody += "<td class='" +
1726 ((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends
1727 (otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months
1728 ((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key
1729 (defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ?
1730 // or defaultDate is current printedDate and defaultDate is selectedDate
1731 " " + this._dayOverClass : "") + // highlight selected day
1732 (unselectable ? " " + this._unselectableClass + " ui-state-disabled": "") + // highlight unselectable days
1733 (otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates
1734 (printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day
1735 (printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different)
1736 ((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "'") + "'" : "") + // cell title
1737 (unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions
1738 (otherMonth && !showOtherMonths ? " " : // display for other months
1739 (unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
1740 (printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") +
1741 (printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day
1742 (otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months
1743 "' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date
1744 printDate.setDate(printDate.getDate() + 1);
1745 printDate = this._daylightSavingAdjust(printDate);
1747 calender += tbody + "</tr>";
1750 if (drawMonth > 11) {
1754 calender += "</tbody></table>" + (isMultiMonth ? "</div>" +
1755 ((numMonths[0] > 0 && col === numMonths[1]-1) ? "<div class='ui-datepicker-row-break'></div>" : "") : "");
1760 html += buttonPanel;
1761 inst._keyEvent = false;
1765 /* Generate the month and year header. */
1766 _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
1767 secondary, monthNames, monthNamesShort) {
1769 var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
1770 changeMonth = this._get(inst, "changeMonth"),
1771 changeYear = this._get(inst, "changeYear"),
1772 showMonthAfterYear = this._get(inst, "showMonthAfterYear"),
1773 html = "<div class='ui-datepicker-title'>",
1777 if (secondary || !changeMonth) {
1778 monthHtml += "<span class='ui-datepicker-month'>" + monthNames[drawMonth] + "</span>";
1780 inMinYear = (minDate && minDate.getFullYear() === drawYear);
1781 inMaxYear = (maxDate && maxDate.getFullYear() === drawYear);
1782 monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
1783 for ( month = 0; month < 12; month++) {
1784 if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth())) {
1785 monthHtml += "<option value='" + month + "'" +
1786 (month === drawMonth ? " selected='selected'" : "") +
1787 ">" + monthNamesShort[month] + "</option>";
1790 monthHtml += "</select>";
1793 if (!showMonthAfterYear) {
1794 html += monthHtml + (secondary || !(changeMonth && changeYear) ? " " : "");
1798 if ( !inst.yearshtml ) {
1799 inst.yearshtml = "";
1800 if (secondary || !changeYear) {
1801 html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
1803 // determine range of years to display
1804 years = this._get(inst, "yearRange").split(":");
1805 thisYear = new Date().getFullYear();
1806 determineYear = function(value) {
1807 var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) :
1808 (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) :
1809 parseInt(value, 10)));
1810 return (isNaN(year) ? thisYear : year);
1812 year = determineYear(years[0]);
1813 endYear = Math.max(year, determineYear(years[1] || ""));
1814 year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
1815 endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
1816 inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
1817 for (; year <= endYear; year++) {
1818 inst.yearshtml += "<option value='" + year + "'" +
1819 (year === drawYear ? " selected='selected'" : "") +
1820 ">" + year + "</option>";
1822 inst.yearshtml += "</select>";
1824 html += inst.yearshtml;
1825 inst.yearshtml = null;
1829 html += this._get(inst, "yearSuffix");
1830 if (showMonthAfterYear) {
1831 html += (secondary || !(changeMonth && changeYear) ? " " : "") + monthHtml;
1833 html += "</div>"; // Close datepicker_header
1837 /* Adjust one of the date sub-fields. */
1838 _adjustInstDate: function(inst, offset, period) {
1839 var year = inst.drawYear + (period === "Y" ? offset : 0),
1840 month = inst.drawMonth + (period === "M" ? offset : 0),
1841 day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0),
1842 date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day)));
1844 inst.selectedDay = date.getDate();
1845 inst.drawMonth = inst.selectedMonth = date.getMonth();
1846 inst.drawYear = inst.selectedYear = date.getFullYear();
1847 if (period === "M" || period === "Y") {
1848 this._notifyChange(inst);
1852 /* Ensure a date is within any min/max bounds. */
1853 _restrictMinMax: function(inst, date) {
1854 var minDate = this._getMinMaxDate(inst, "min"),
1855 maxDate = this._getMinMaxDate(inst, "max"),
1856 newDate = (minDate && date < minDate ? minDate : date);
1857 return (maxDate && newDate > maxDate ? maxDate : newDate);
1860 /* Notify change of month/year. */
1861 _notifyChange: function(inst) {
1862 var onChange = this._get(inst, "onChangeMonthYear");
1864 onChange.apply((inst.input ? inst.input[0] : null),
1865 [inst.selectedYear, inst.selectedMonth + 1, inst]);
1869 /* Determine the number of months to show. */
1870 _getNumberOfMonths: function(inst) {
1871 var numMonths = this._get(inst, "numberOfMonths");
1872 return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths));
1875 /* Determine the current maximum date - ensure no time components are set. */
1876 _getMinMaxDate: function(inst, minMax) {
1877 return this._determineDate(inst, this._get(inst, minMax + "Date"), null);
1880 /* Find the number of days in a given month. */
1881 _getDaysInMonth: function(year, month) {
1882 return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
1885 /* Find the day of the week of the first of a month. */
1886 _getFirstDayOfMonth: function(year, month) {
1887 return new Date(year, month, 1).getDay();
1890 /* Determines if we should allow a "next/prev" month display change. */
1891 _canAdjustMonth: function(inst, offset, curYear, curMonth) {
1892 var numMonths = this._getNumberOfMonths(inst),
1893 date = this._daylightSavingAdjust(new Date(curYear,
1894 curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
1897 date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
1899 return this._isInRange(inst, date);
1902 /* Is the given date in the accepted range? */
1903 _isInRange: function(inst, date) {
1904 var yearSplit, currentYear,
1905 minDate = this._getMinMaxDate(inst, "min"),
1906 maxDate = this._getMinMaxDate(inst, "max"),
1909 years = this._get(inst, "yearRange");
1911 yearSplit = years.split(":");
1912 currentYear = new Date().getFullYear();
1913 minYear = parseInt(yearSplit[0], 10);
1914 maxYear = parseInt(yearSplit[1], 10);
1915 if ( yearSplit[0].match(/[+\-].*/) ) {
1916 minYear += currentYear;
1918 if ( yearSplit[1].match(/[+\-].*/) ) {
1919 maxYear += currentYear;
1923 return ((!minDate || date.getTime() >= minDate.getTime()) &&
1924 (!maxDate || date.getTime() <= maxDate.getTime()) &&
1925 (!minYear || date.getFullYear() >= minYear) &&
1926 (!maxYear || date.getFullYear() <= maxYear));
1929 /* Provide the configuration settings for formatting/parsing. */
1930 _getFormatConfig: function(inst) {
1931 var shortYearCutoff = this._get(inst, "shortYearCutoff");
1932 shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff :
1933 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
1934 return {shortYearCutoff: shortYearCutoff,
1935 dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"),
1936 monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")};
1939 /* Format the given date for display. */
1940 _formatDate: function(inst, day, month, year) {
1942 inst.currentDay = inst.selectedDay;
1943 inst.currentMonth = inst.selectedMonth;
1944 inst.currentYear = inst.selectedYear;
1946 var date = (day ? (typeof day === "object" ? day :
1947 this._daylightSavingAdjust(new Date(year, month, day))) :
1948 this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
1949 return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst));
1954 * Bind hover events for datepicker elements.
1955 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
1956 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
1958 function bindHover(dpDiv) {
1959 var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
1960 return dpDiv.delegate(selector, "mouseout", function() {
1961 $(this).removeClass("ui-state-hover");
1962 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
1963 $(this).removeClass("ui-datepicker-prev-hover");
1965 if (this.className.indexOf("ui-datepicker-next") !== -1) {
1966 $(this).removeClass("ui-datepicker-next-hover");
1969 .delegate(selector, "mouseover", function(){
1970 if (!$.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0])) {
1971 $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
1972 $(this).addClass("ui-state-hover");
1973 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
1974 $(this).addClass("ui-datepicker-prev-hover");
1976 if (this.className.indexOf("ui-datepicker-next") !== -1) {
1977 $(this).addClass("ui-datepicker-next-hover");
1983 /* jQuery extend now ignores nulls! */
1984 function extendRemove(target, props) {
1985 $.extend(target, props);
1986 for (var name in props) {
1987 if (props[name] == null) {
1988 target[name] = props[name];
1994 /* Invoke the datepicker functionality.
1995 @param options string - a command, optionally followed by additional parameters or
1996 Object - settings for attaching new datepicker functionality
1997 @return jQuery object */
1998 $.fn.datepicker = function(options){
2000 /* Verify an empty collection wasn't passed - Fixes #6976 */
2001 if ( !this.length ) {
2005 /* Initialise the date picker. */
2006 if (!$.datepicker.initialized) {
2007 $(document).mousedown($.datepicker._checkExternalClick);
2008 $.datepicker.initialized = true;
2011 /* Append datepicker main container to body if not exist. */
2012 if ($("#"+$.datepicker._mainDivId).length === 0) {
2013 $("body").append($.datepicker.dpDiv);
2016 var otherArgs = Array.prototype.slice.call(arguments, 1);
2017 if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) {
2018 return $.datepicker["_" + options + "Datepicker"].
2019 apply($.datepicker, [this[0]].concat(otherArgs));
2021 if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") {
2022 return $.datepicker["_" + options + "Datepicker"].
2023 apply($.datepicker, [this[0]].concat(otherArgs));
2025 return this.each(function() {
2026 typeof options === "string" ?
2027 $.datepicker["_" + options + "Datepicker"].
2028 apply($.datepicker, [this].concat(otherArgs)) :
2029 $.datepicker._attachDatepicker(this, options);
2033 $.datepicker = new Datepicker(); // singleton instance
2034 $.datepicker.initialized = false;
2035 $.datepicker.uuid = new Date().getTime();
2036 $.datepicker.version = "1.10.3";