1 // jscs:disable maximumLineLength
2 /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */
4 * jQuery UI Datepicker 1.12.1
7 * Copyright jQuery Foundation and other contributors
8 * Released under the MIT license.
9 * http://jquery.org/license
14 //>>description: Displays a calendar from an input or inline for selecting dates.
15 //>>docs: http://api.jqueryui.com/datepicker/
16 //>>demos: http://jqueryui.com/datepicker/
17 //>>css.structure: ../../themes/base/core.css
18 //>>css.structure: ../../themes/base/datepicker.css
19 //>>css.theme: ../../themes/base/theme.css
21 ( function( factory ) {
22 if ( typeof define === "function" && define.amd ) {
24 // AMD. Register as an anonymous module.
37 $.extend( $.ui, { datepicker: { version: "1.12.1" } } );
39 var datepicker_instActive;
41 function datepicker_getZindex( elem ) {
43 while ( elem.length && elem[ 0 ] !== document ) {
45 // Ignore z-index if position is set to a value where z-index is ignored by the browser
46 // This makes behavior of this function consistent across browsers
47 // WebKit always returns auto if the element is positioned
48 position = elem.css( "position" );
49 if ( position === "absolute" || position === "relative" || position === "fixed" ) {
51 // IE returns 0 when zIndex is not specified
52 // other browsers return a string
53 // we ignore the case of nested elements with an explicit value of 0
54 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
55 value = parseInt( elem.css( "zIndex" ), 10 );
56 if ( !isNaN( value ) && value !== 0 ) {
65 /* Date picker manager.
66 Use the singleton instance of this class, $.datepicker, to interact with the date picker.
67 Settings for (groups of) date pickers are maintained in an instance object,
68 allowing multiple different settings on the same page. */
70 function Datepicker() {
71 this._curInst = null; // The current instance in use
72 this._keyEvent = false; // If the last event was a key event
73 this._disabledInputs = []; // List of date picker inputs that have been disabled
74 this._datepickerShowing = false; // True if the popup picker is showing , false if not
75 this._inDialog = false; // True if showing within a "dialog", false if not
76 this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
77 this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
78 this._appendClass = "ui-datepicker-append"; // The name of the append marker class
79 this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
80 this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
81 this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
82 this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
83 this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
84 this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
85 this.regional = []; // Available regional settings, indexed by language code
86 this.regional[ "" ] = { // Default regional settings
87 closeText: "Done", // Display text for close link
88 prevText: "Prev", // Display text for previous month link
89 nextText: "Next", // Display text for next month link
90 currentText: "Today", // Display text for current month link
91 monthNames: [ "January","February","March","April","May","June",
92 "July","August","September","October","November","December" ], // Names of months for drop-down and formatting
93 monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ], // For formatting
94 dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], // For formatting
95 dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], // For formatting
96 dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ], // Column headings for days starting at Sunday
97 weekHeader: "Wk", // Column header for week of the year
98 dateFormat: "mm/dd/yy", // See format options on parseDate
99 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
100 isRTL: false, // True if right-to-left language, false if left-to-right
101 showMonthAfterYear: false, // True if the year select precedes month, false for month then year
102 yearSuffix: "" // Additional text to append to the year in the month headers
104 this._defaults = { // Global defaults for all the date picker instances
105 showOn: "focus", // "focus" for popup on focus,
106 // "button" for trigger button, or "both" for either
107 showAnim: "fadeIn", // Name of jQuery animation for popup
108 showOptions: {}, // Options for enhanced animations
109 defaultDate: null, // Used when field is blank: actual date,
110 // +/-number for offset from today, null for today
111 appendText: "", // Display text following the input box, e.g. showing the format
112 buttonText: "...", // Text for trigger button
113 buttonImage: "", // URL for trigger button image
114 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
115 hideIfNoPrevNext: false, // True to hide next/previous month links
116 // if not applicable, false to just disable them
117 navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
118 gotoCurrent: false, // True if today link goes back to current selection instead
119 changeMonth: false, // True if month can be selected directly, false if only prev/next
120 changeYear: false, // True if year can be selected directly, false if only prev/next
121 yearRange: "c-10:c+10", // Range of years to display in drop-down,
122 // either relative to today's year (-nn:+nn), relative to currently displayed year
123 // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
124 showOtherMonths: false, // True to show dates in other months, false to leave blank
125 selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
126 showWeek: false, // True to show week of the year, false to not show it
127 calculateWeek: this.iso8601Week, // How to calculate the week of the year,
128 // takes a Date and returns the number of the week for it
129 shortYearCutoff: "+10", // Short year values < this are in the current century,
130 // > this are in the previous century,
131 // string value starting with "+" for current year + value
132 minDate: null, // The earliest selectable date, or null for no limit
133 maxDate: null, // The latest selectable date, or null for no limit
134 duration: "fast", // Duration of display/closure
135 beforeShowDay: null, // Function that takes a date and returns an array with
136 // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
137 // [2] = cell title (optional), e.g. $.datepicker.noWeekends
138 beforeShow: null, // Function that takes an input field and
139 // returns a set of custom settings for the date picker
140 onSelect: null, // Define a callback function when a date is selected
141 onChangeMonthYear: null, // Define a callback function when the month or year is changed
142 onClose: null, // Define a callback function when the datepicker is closed
143 numberOfMonths: 1, // Number of months to show at a time
144 showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
145 stepMonths: 1, // Number of months to step back/forward
146 stepBigMonths: 12, // Number of months to step back/forward for the big links
147 altField: "", // Selector for an alternate field to store selected dates into
148 altFormat: "", // The date format to use for the alternate field
149 constrainInput: true, // The input is constrained by the current date format
150 showButtonPanel: false, // True to show button panel, false to not show it
151 autoSize: false, // True to size the input for the date format, false to leave as is
152 disabled: false // The initial disabled state
154 $.extend( this._defaults, this.regional[ "" ] );
155 this.regional.en = $.extend( true, {}, this.regional[ "" ] );
156 this.regional[ "en-US" ] = $.extend( true, {}, this.regional.en );
157 this.dpDiv = datepicker_bindHover( $( "<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) );
160 $.extend( Datepicker.prototype, {
161 /* Class name added to elements to indicate already configured with a date picker. */
162 markerClassName: "hasDatepicker",
164 //Keep track of the maximum number of rows displayed (see #7043)
167 // TODO rename to "widget" when switching to widget factory
168 _widgetDatepicker: function() {
172 /* Override the default settings for all instances of the date picker.
173 * @param settings object - the new settings to use as defaults (anonymous object)
174 * @return the manager object
176 setDefaults: function( settings ) {
177 datepicker_extendRemove( this._defaults, settings || {} );
181 /* Attach the date picker to a jQuery selection.
182 * @param target element - the target input field or division or span
183 * @param settings object - the new settings to use for this date picker instance (anonymous)
185 _attachDatepicker: function( target, settings ) {
186 var nodeName, inline, inst;
187 nodeName = target.nodeName.toLowerCase();
188 inline = ( nodeName === "div" || nodeName === "span" );
191 target.id = "dp" + this.uuid;
193 inst = this._newInst( $( target ), inline );
194 inst.settings = $.extend( {}, settings || {} );
195 if ( nodeName === "input" ) {
196 this._connectDatepicker( target, inst );
197 } else if ( inline ) {
198 this._inlineDatepicker( target, inst );
202 /* Create a new instance object. */
203 _newInst: function( target, inline ) {
204 var id = target[ 0 ].id.replace( /([^A-Za-z0-9_\-])/g, "\\\\$1" ); // escape jQuery meta chars
205 return { id: id, input: target, // associated target
206 selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
207 drawMonth: 0, drawYear: 0, // month being drawn
208 inline: inline, // is datepicker inline or not
209 dpDiv: ( !inline ? this.dpDiv : // presentation div
210 datepicker_bindHover( $( "<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) ) ) };
213 /* Attach the date picker to an input field. */
214 _connectDatepicker: function( target, inst ) {
215 var input = $( target );
216 inst.append = $( [] );
217 inst.trigger = $( [] );
218 if ( input.hasClass( this.markerClassName ) ) {
221 this._attachments( input, inst );
222 input.addClass( this.markerClassName ).on( "keydown", this._doKeyDown ).
223 on( "keypress", this._doKeyPress ).on( "keyup", this._doKeyUp );
224 this._autoSize( inst );
225 $.data( target, "datepicker", inst );
227 //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
228 if ( inst.settings.disabled ) {
229 this._disableDatepicker( target );
233 /* Make attachments based on settings. */
234 _attachments: function( input, inst ) {
235 var showOn, buttonText, buttonImage,
236 appendText = this._get( inst, "appendText" ),
237 isRTL = this._get( inst, "isRTL" );
240 inst.append.remove();
243 inst.append = $( "<span class='" + this._appendClass + "'>" + appendText + "</span>" );
244 input[ isRTL ? "before" : "after" ]( inst.append );
247 input.off( "focus", this._showDatepicker );
249 if ( inst.trigger ) {
250 inst.trigger.remove();
253 showOn = this._get( inst, "showOn" );
254 if ( showOn === "focus" || showOn === "both" ) { // pop-up date picker when in the marked field
255 input.on( "focus", this._showDatepicker );
257 if ( showOn === "button" || showOn === "both" ) { // pop-up date picker when button clicked
258 buttonText = this._get( inst, "buttonText" );
259 buttonImage = this._get( inst, "buttonImage" );
260 inst.trigger = $( this._get( inst, "buttonImageOnly" ) ?
261 $( "<img/>" ).addClass( this._triggerClass ).
262 attr( { src: buttonImage, alt: buttonText, title: buttonText } ) :
263 $( "<button type='button'></button>" ).addClass( this._triggerClass ).
264 html( !buttonImage ? buttonText : $( "<img/>" ).attr(
265 { src:buttonImage, alt:buttonText, title:buttonText } ) ) );
266 input[ isRTL ? "before" : "after" ]( inst.trigger );
267 inst.trigger.on( "click", function() {
268 if ( $.datepicker._datepickerShowing && $.datepicker._lastInput === input[ 0 ] ) {
269 $.datepicker._hideDatepicker();
270 } else if ( $.datepicker._datepickerShowing && $.datepicker._lastInput !== input[ 0 ] ) {
271 $.datepicker._hideDatepicker();
272 $.datepicker._showDatepicker( input[ 0 ] );
274 $.datepicker._showDatepicker( input[ 0 ] );
281 /* Apply the maximum length for the date format. */
282 _autoSize: function( inst ) {
283 if ( this._get( inst, "autoSize" ) && !inst.inline ) {
284 var findMax, max, maxI, i,
285 date = new Date( 2009, 12 - 1, 20 ), // Ensure double digits
286 dateFormat = this._get( inst, "dateFormat" );
288 if ( dateFormat.match( /[DM]/ ) ) {
289 findMax = function( names ) {
292 for ( i = 0; i < names.length; i++ ) {
293 if ( names[ i ].length > max ) {
294 max = names[ i ].length;
300 date.setMonth( findMax( this._get( inst, ( dateFormat.match( /MM/ ) ?
301 "monthNames" : "monthNamesShort" ) ) ) );
302 date.setDate( findMax( this._get( inst, ( dateFormat.match( /DD/ ) ?
303 "dayNames" : "dayNamesShort" ) ) ) + 20 - date.getDay() );
305 inst.input.attr( "size", this._formatDate( inst, date ).length );
309 /* Attach an inline date picker to a div. */
310 _inlineDatepicker: function( target, inst ) {
311 var divSpan = $( target );
312 if ( divSpan.hasClass( this.markerClassName ) ) {
315 divSpan.addClass( this.markerClassName ).append( inst.dpDiv );
316 $.data( target, "datepicker", inst );
317 this._setDate( inst, this._getDefaultDate( inst ), true );
318 this._updateDatepicker( inst );
319 this._updateAlternate( inst );
321 //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
322 if ( inst.settings.disabled ) {
323 this._disableDatepicker( target );
326 // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
327 // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
328 inst.dpDiv.css( "display", "block" );
331 /* Pop-up the date picker in a "dialog" box.
332 * @param input element - ignored
333 * @param date string or Date - the initial date to display
334 * @param onSelect function - the function to call when a date is selected
335 * @param settings object - update the dialog date picker instance's settings (anonymous object)
336 * @param pos int[2] - coordinates for the dialog's position within the screen or
337 * event - with x/y coordinates or
338 * leave empty for default (screen centre)
339 * @return the manager object
341 _dialogDatepicker: function( input, date, onSelect, settings, pos ) {
342 var id, browserWidth, browserHeight, scrollX, scrollY,
343 inst = this._dialogInst; // internal instance
347 id = "dp" + this.uuid;
348 this._dialogInput = $( "<input type='text' id='" + id +
349 "' style='position: absolute; top: -100px; width: 0px;'/>" );
350 this._dialogInput.on( "keydown", this._doKeyDown );
351 $( "body" ).append( this._dialogInput );
352 inst = this._dialogInst = this._newInst( this._dialogInput, false );
354 $.data( this._dialogInput[ 0 ], "datepicker", inst );
356 datepicker_extendRemove( inst.settings, settings || {} );
357 date = ( date && date.constructor === Date ? this._formatDate( inst, date ) : date );
358 this._dialogInput.val( date );
360 this._pos = ( pos ? ( pos.length ? pos : [ pos.pageX, pos.pageY ] ) : null );
362 browserWidth = document.documentElement.clientWidth;
363 browserHeight = document.documentElement.clientHeight;
364 scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
365 scrollY = document.documentElement.scrollTop || document.body.scrollTop;
366 this._pos = // should use actual width/height below
367 [ ( browserWidth / 2 ) - 100 + scrollX, ( browserHeight / 2 ) - 150 + scrollY ];
370 // Move input on screen for focus, but hidden behind dialog
371 this._dialogInput.css( "left", ( this._pos[ 0 ] + 20 ) + "px" ).css( "top", this._pos[ 1 ] + "px" );
372 inst.settings.onSelect = onSelect;
373 this._inDialog = true;
374 this.dpDiv.addClass( this._dialogClass );
375 this._showDatepicker( this._dialogInput[ 0 ] );
377 $.blockUI( this.dpDiv );
379 $.data( this._dialogInput[ 0 ], "datepicker", inst );
383 /* Detach a datepicker from its control.
384 * @param target element - the target input field or division or span
386 _destroyDatepicker: function( target ) {
388 $target = $( target ),
389 inst = $.data( target, "datepicker" );
391 if ( !$target.hasClass( this.markerClassName ) ) {
395 nodeName = target.nodeName.toLowerCase();
396 $.removeData( target, "datepicker" );
397 if ( nodeName === "input" ) {
398 inst.append.remove();
399 inst.trigger.remove();
400 $target.removeClass( this.markerClassName ).
401 off( "focus", this._showDatepicker ).
402 off( "keydown", this._doKeyDown ).
403 off( "keypress", this._doKeyPress ).
404 off( "keyup", this._doKeyUp );
405 } else if ( nodeName === "div" || nodeName === "span" ) {
406 $target.removeClass( this.markerClassName ).empty();
409 if ( datepicker_instActive === inst ) {
410 datepicker_instActive = null;
414 /* Enable the date picker to a jQuery selection.
415 * @param target element - the target input field or division or span
417 _enableDatepicker: function( target ) {
418 var nodeName, inline,
419 $target = $( target ),
420 inst = $.data( target, "datepicker" );
422 if ( !$target.hasClass( this.markerClassName ) ) {
426 nodeName = target.nodeName.toLowerCase();
427 if ( nodeName === "input" ) {
428 target.disabled = false;
429 inst.trigger.filter( "button" ).
430 each( function() { this.disabled = false; } ).end().
431 filter( "img" ).css( { opacity: "1.0", cursor: "" } );
432 } else if ( nodeName === "div" || nodeName === "span" ) {
433 inline = $target.children( "." + this._inlineClass );
434 inline.children().removeClass( "ui-state-disabled" );
435 inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ).
436 prop( "disabled", false );
438 this._disabledInputs = $.map( this._disabledInputs,
439 function( value ) { return ( value === target ? null : value ); } ); // delete entry
442 /* Disable the date picker to a jQuery selection.
443 * @param target element - the target input field or division or span
445 _disableDatepicker: function( target ) {
446 var nodeName, inline,
447 $target = $( target ),
448 inst = $.data( target, "datepicker" );
450 if ( !$target.hasClass( this.markerClassName ) ) {
454 nodeName = target.nodeName.toLowerCase();
455 if ( nodeName === "input" ) {
456 target.disabled = true;
457 inst.trigger.filter( "button" ).
458 each( function() { this.disabled = true; } ).end().
459 filter( "img" ).css( { opacity: "0.5", cursor: "default" } );
460 } else if ( nodeName === "div" || nodeName === "span" ) {
461 inline = $target.children( "." + this._inlineClass );
462 inline.children().addClass( "ui-state-disabled" );
463 inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ).
464 prop( "disabled", true );
466 this._disabledInputs = $.map( this._disabledInputs,
467 function( value ) { return ( value === target ? null : value ); } ); // delete entry
468 this._disabledInputs[ this._disabledInputs.length ] = target;
471 /* Is the first field in a jQuery collection disabled as a datepicker?
472 * @param target element - the target input field or division or span
473 * @return boolean - true if disabled, false if enabled
475 _isDisabledDatepicker: function( target ) {
479 for ( var i = 0; i < this._disabledInputs.length; i++ ) {
480 if ( this._disabledInputs[ i ] === target ) {
487 /* Retrieve the instance data for the target control.
488 * @param target element - the target input field or division or span
489 * @return object - the associated instance data
490 * @throws error if a jQuery problem getting data
492 _getInst: function( target ) {
494 return $.data( target, "datepicker" );
497 throw "Missing instance data for this datepicker";
501 /* Update or retrieve the settings for a date picker attached to an input field or division.
502 * @param target element - the target input field or division or span
503 * @param name object - the new settings to update or
504 * string - the name of the setting to change or retrieve,
505 * when retrieving also "all" for all instance settings or
506 * "defaults" for all global defaults
507 * @param value any - the new value for the setting
508 * (omit if above is an object or to retrieve a value)
510 _optionDatepicker: function( target, name, value ) {
511 var settings, date, minDate, maxDate,
512 inst = this._getInst( target );
514 if ( arguments.length === 2 && typeof name === "string" ) {
515 return ( name === "defaults" ? $.extend( {}, $.datepicker._defaults ) :
516 ( inst ? ( name === "all" ? $.extend( {}, inst.settings ) :
517 this._get( inst, name ) ) : null ) );
520 settings = name || {};
521 if ( typeof name === "string" ) {
523 settings[ name ] = value;
527 if ( this._curInst === inst ) {
528 this._hideDatepicker();
531 date = this._getDateDatepicker( target, true );
532 minDate = this._getMinMaxDate( inst, "min" );
533 maxDate = this._getMinMaxDate( inst, "max" );
534 datepicker_extendRemove( inst.settings, settings );
536 // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
537 if ( minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined ) {
538 inst.settings.minDate = this._formatDate( inst, minDate );
540 if ( maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined ) {
541 inst.settings.maxDate = this._formatDate( inst, maxDate );
543 if ( "disabled" in settings ) {
544 if ( settings.disabled ) {
545 this._disableDatepicker( target );
547 this._enableDatepicker( target );
550 this._attachments( $( target ), inst );
551 this._autoSize( inst );
552 this._setDate( inst, date );
553 this._updateAlternate( inst );
554 this._updateDatepicker( inst );
558 // Change method deprecated
559 _changeDatepicker: function( target, name, value ) {
560 this._optionDatepicker( target, name, value );
563 /* Redraw the date picker attached to an input field or division.
564 * @param target element - the target input field or division or span
566 _refreshDatepicker: function( target ) {
567 var inst = this._getInst( target );
569 this._updateDatepicker( inst );
573 /* Set the dates for a jQuery selection.
574 * @param target element - the target input field or division or span
575 * @param date Date - the new date
577 _setDateDatepicker: function( target, date ) {
578 var inst = this._getInst( target );
580 this._setDate( inst, date );
581 this._updateDatepicker( inst );
582 this._updateAlternate( inst );
586 /* Get the date(s) for the first entry in a jQuery selection.
587 * @param target element - the target input field or division or span
588 * @param noDefault boolean - true if no default date is to be used
589 * @return Date - the current date
591 _getDateDatepicker: function( target, noDefault ) {
592 var inst = this._getInst( target );
593 if ( inst && !inst.inline ) {
594 this._setDateFromField( inst, noDefault );
596 return ( inst ? this._getDate( inst ) : null );
599 /* Handle keystrokes. */
600 _doKeyDown: function( event ) {
601 var onSelect, dateStr, sel,
602 inst = $.datepicker._getInst( event.target ),
604 isRTL = inst.dpDiv.is( ".ui-datepicker-rtl" );
606 inst._keyEvent = true;
607 if ( $.datepicker._datepickerShowing ) {
608 switch ( event.keyCode ) {
609 case 9: $.datepicker._hideDatepicker();
611 break; // hide on tab out
612 case 13: sel = $( "td." + $.datepicker._dayOverClass + ":not(." +
613 $.datepicker._currentClass + ")", inst.dpDiv );
615 $.datepicker._selectDay( event.target, inst.selectedMonth, inst.selectedYear, sel[ 0 ] );
618 onSelect = $.datepicker._get( inst, "onSelect" );
620 dateStr = $.datepicker._formatDate( inst );
622 // Trigger custom callback
623 onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] );
625 $.datepicker._hideDatepicker();
628 return false; // don't submit the form
629 case 27: $.datepicker._hideDatepicker();
630 break; // hide on escape
631 case 33: $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
632 -$.datepicker._get( inst, "stepBigMonths" ) :
633 -$.datepicker._get( inst, "stepMonths" ) ), "M" );
634 break; // previous month/year on page up/+ ctrl
635 case 34: $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
636 +$.datepicker._get( inst, "stepBigMonths" ) :
637 +$.datepicker._get( inst, "stepMonths" ) ), "M" );
638 break; // next month/year on page down/+ ctrl
639 case 35: if ( event.ctrlKey || event.metaKey ) {
640 $.datepicker._clearDate( event.target );
642 handled = event.ctrlKey || event.metaKey;
643 break; // clear on ctrl or command +end
644 case 36: if ( event.ctrlKey || event.metaKey ) {
645 $.datepicker._gotoToday( event.target );
647 handled = event.ctrlKey || event.metaKey;
648 break; // current on ctrl or command +home
649 case 37: if ( event.ctrlKey || event.metaKey ) {
650 $.datepicker._adjustDate( event.target, ( isRTL ? +1 : -1 ), "D" );
652 handled = event.ctrlKey || event.metaKey;
654 // -1 day on ctrl or command +left
655 if ( event.originalEvent.altKey ) {
656 $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
657 -$.datepicker._get( inst, "stepBigMonths" ) :
658 -$.datepicker._get( inst, "stepMonths" ) ), "M" );
661 // next month/year on alt +left on Mac
663 case 38: if ( event.ctrlKey || event.metaKey ) {
664 $.datepicker._adjustDate( event.target, -7, "D" );
666 handled = event.ctrlKey || event.metaKey;
667 break; // -1 week on ctrl or command +up
668 case 39: if ( event.ctrlKey || event.metaKey ) {
669 $.datepicker._adjustDate( event.target, ( isRTL ? -1 : +1 ), "D" );
671 handled = event.ctrlKey || event.metaKey;
673 // +1 day on ctrl or command +right
674 if ( event.originalEvent.altKey ) {
675 $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
676 +$.datepicker._get( inst, "stepBigMonths" ) :
677 +$.datepicker._get( inst, "stepMonths" ) ), "M" );
680 // next month/year on alt +right
682 case 40: if ( event.ctrlKey || event.metaKey ) {
683 $.datepicker._adjustDate( event.target, +7, "D" );
685 handled = event.ctrlKey || event.metaKey;
686 break; // +1 week on ctrl or command +down
687 default: handled = false;
689 } else if ( event.keyCode === 36 && event.ctrlKey ) { // display the date picker on ctrl+home
690 $.datepicker._showDatepicker( this );
696 event.preventDefault();
697 event.stopPropagation();
701 /* Filter entered characters - based on date format. */
702 _doKeyPress: function( event ) {
704 inst = $.datepicker._getInst( event.target );
706 if ( $.datepicker._get( inst, "constrainInput" ) ) {
707 chars = $.datepicker._possibleChars( $.datepicker._get( inst, "dateFormat" ) );
708 chr = String.fromCharCode( event.charCode == null ? event.keyCode : event.charCode );
709 return event.ctrlKey || event.metaKey || ( chr < " " || !chars || chars.indexOf( chr ) > -1 );
713 /* Synchronise manual entry and field/alternate field. */
714 _doKeyUp: function( event ) {
716 inst = $.datepicker._getInst( event.target );
718 if ( inst.input.val() !== inst.lastVal ) {
720 date = $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ),
721 ( inst.input ? inst.input.val() : null ),
722 $.datepicker._getFormatConfig( inst ) );
724 if ( date ) { // only if valid
725 $.datepicker._setDateFromField( inst );
726 $.datepicker._updateAlternate( inst );
727 $.datepicker._updateDatepicker( inst );
736 /* Pop-up the date picker for a given input field.
737 * If false returned from beforeShow event handler do not show.
738 * @param input element - the input field attached to the date picker or
739 * event - if triggered by focus
741 _showDatepicker: function( input ) {
742 input = input.target || input;
743 if ( input.nodeName.toLowerCase() !== "input" ) { // find from button/image trigger
744 input = $( "input", input.parentNode )[ 0 ];
747 if ( $.datepicker._isDisabledDatepicker( input ) || $.datepicker._lastInput === input ) { // already here
751 var inst, beforeShow, beforeShowSettings, isFixed,
752 offset, showAnim, duration;
754 inst = $.datepicker._getInst( input );
755 if ( $.datepicker._curInst && $.datepicker._curInst !== inst ) {
756 $.datepicker._curInst.dpDiv.stop( true, true );
757 if ( inst && $.datepicker._datepickerShowing ) {
758 $.datepicker._hideDatepicker( $.datepicker._curInst.input[ 0 ] );
762 beforeShow = $.datepicker._get( inst, "beforeShow" );
763 beforeShowSettings = beforeShow ? beforeShow.apply( input, [ input, inst ] ) : {};
764 if ( beforeShowSettings === false ) {
767 datepicker_extendRemove( inst.settings, beforeShowSettings );
770 $.datepicker._lastInput = input;
771 $.datepicker._setDateFromField( inst );
773 if ( $.datepicker._inDialog ) { // hide cursor
776 if ( !$.datepicker._pos ) { // position below input
777 $.datepicker._pos = $.datepicker._findPos( input );
778 $.datepicker._pos[ 1 ] += input.offsetHeight; // add the height
782 $( input ).parents().each( function() {
783 isFixed |= $( this ).css( "position" ) === "fixed";
787 offset = { left: $.datepicker._pos[ 0 ], top: $.datepicker._pos[ 1 ] };
788 $.datepicker._pos = null;
790 //to avoid flashes on Firefox
793 // determine sizing offscreen
794 inst.dpDiv.css( { position: "absolute", display: "block", top: "-1000px" } );
795 $.datepicker._updateDatepicker( inst );
797 // fix width for dynamic number of date pickers
798 // and adjust position before showing
799 offset = $.datepicker._checkOffset( inst, offset, isFixed );
800 inst.dpDiv.css( { position: ( $.datepicker._inDialog && $.blockUI ?
801 "static" : ( isFixed ? "fixed" : "absolute" ) ), display: "none",
802 left: offset.left + "px", top: offset.top + "px" } );
804 if ( !inst.inline ) {
805 showAnim = $.datepicker._get( inst, "showAnim" );
806 duration = $.datepicker._get( inst, "duration" );
807 inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 );
808 $.datepicker._datepickerShowing = true;
810 if ( $.effects && $.effects.effect[ showAnim ] ) {
811 inst.dpDiv.show( showAnim, $.datepicker._get( inst, "showOptions" ), duration );
813 inst.dpDiv[ showAnim || "show" ]( showAnim ? duration : null );
816 if ( $.datepicker._shouldFocusInput( inst ) ) {
817 inst.input.trigger( "focus" );
820 $.datepicker._curInst = inst;
824 /* Generate the date picker content. */
825 _updateDatepicker: function( inst ) {
826 this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
827 datepicker_instActive = inst; // for delegate hover events
828 inst.dpDiv.empty().append( this._generateHTML( inst ) );
829 this._attachHandlers( inst );
832 numMonths = this._getNumberOfMonths( inst ),
833 cols = numMonths[ 1 ],
835 activeCell = inst.dpDiv.find( "." + this._dayOverClass + " a" );
837 if ( activeCell.length > 0 ) {
838 datepicker_handleMouseover.apply( activeCell.get( 0 ) );
841 inst.dpDiv.removeClass( "ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4" ).width( "" );
843 inst.dpDiv.addClass( "ui-datepicker-multi-" + cols ).css( "width", ( width * cols ) + "em" );
845 inst.dpDiv[ ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ? "add" : "remove" ) +
846 "Class" ]( "ui-datepicker-multi" );
847 inst.dpDiv[ ( this._get( inst, "isRTL" ) ? "add" : "remove" ) +
848 "Class" ]( "ui-datepicker-rtl" );
850 if ( inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
851 inst.input.trigger( "focus" );
854 // Deffered render of the years select (to avoid flashes on Firefox)
855 if ( inst.yearshtml ) {
856 origyearshtml = inst.yearshtml;
857 setTimeout( function() {
859 //assure that inst.yearshtml didn't change.
860 if ( origyearshtml === inst.yearshtml && inst.yearshtml ) {
861 inst.dpDiv.find( "select.ui-datepicker-year:first" ).replaceWith( inst.yearshtml );
863 origyearshtml = inst.yearshtml = null;
868 // #6694 - don't focus the input if it's already focused
869 // this breaks the change event in IE
870 // Support: IE and jQuery <1.9
871 _shouldFocusInput: function( inst ) {
872 return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
875 /* Check positioning to remain on screen. */
876 _checkOffset: function( inst, offset, isFixed ) {
877 var dpWidth = inst.dpDiv.outerWidth(),
878 dpHeight = inst.dpDiv.outerHeight(),
879 inputWidth = inst.input ? inst.input.outerWidth() : 0,
880 inputHeight = inst.input ? inst.input.outerHeight() : 0,
881 viewWidth = document.documentElement.clientWidth + ( isFixed ? 0 : $( document ).scrollLeft() ),
882 viewHeight = document.documentElement.clientHeight + ( isFixed ? 0 : $( document ).scrollTop() );
884 offset.left -= ( this._get( inst, "isRTL" ) ? ( dpWidth - inputWidth ) : 0 );
885 offset.left -= ( isFixed && offset.left === inst.input.offset().left ) ? $( document ).scrollLeft() : 0;
886 offset.top -= ( isFixed && offset.top === ( inst.input.offset().top + inputHeight ) ) ? $( document ).scrollTop() : 0;
888 // Now check if datepicker is showing outside window viewport - move to a better place if so.
889 offset.left -= Math.min( offset.left, ( offset.left + dpWidth > viewWidth && viewWidth > dpWidth ) ?
890 Math.abs( offset.left + dpWidth - viewWidth ) : 0 );
891 offset.top -= Math.min( offset.top, ( offset.top + dpHeight > viewHeight && viewHeight > dpHeight ) ?
892 Math.abs( dpHeight + inputHeight ) : 0 );
897 /* Find an object's position on the screen. */
898 _findPos: function( obj ) {
900 inst = this._getInst( obj ),
901 isRTL = this._get( inst, "isRTL" );
903 while ( obj && ( obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden( obj ) ) ) {
904 obj = obj[ isRTL ? "previousSibling" : "nextSibling" ];
907 position = $( obj ).offset();
908 return [ position.left, position.top ];
911 /* Hide the date picker from view.
912 * @param input element - the input field attached to the date picker
914 _hideDatepicker: function( input ) {
915 var showAnim, duration, postProcess, onClose,
916 inst = this._curInst;
918 if ( !inst || ( input && inst !== $.data( input, "datepicker" ) ) ) {
922 if ( this._datepickerShowing ) {
923 showAnim = this._get( inst, "showAnim" );
924 duration = this._get( inst, "duration" );
925 postProcess = function() {
926 $.datepicker._tidyDialog( inst );
929 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
930 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
931 inst.dpDiv.hide( showAnim, $.datepicker._get( inst, "showOptions" ), duration, postProcess );
933 inst.dpDiv[ ( showAnim === "slideDown" ? "slideUp" :
934 ( showAnim === "fadeIn" ? "fadeOut" : "hide" ) ) ]( ( showAnim ? duration : null ), postProcess );
940 this._datepickerShowing = false;
942 onClose = this._get( inst, "onClose" );
944 onClose.apply( ( inst.input ? inst.input[ 0 ] : null ), [ ( inst.input ? inst.input.val() : "" ), inst ] );
947 this._lastInput = null;
948 if ( this._inDialog ) {
949 this._dialogInput.css( { position: "absolute", left: "0", top: "-100px" } );
952 $( "body" ).append( this.dpDiv );
955 this._inDialog = false;
959 /* Tidy up after a dialog display. */
960 _tidyDialog: function( inst ) {
961 inst.dpDiv.removeClass( this._dialogClass ).off( ".ui-datepicker-calendar" );
964 /* Close date picker if clicked elsewhere. */
965 _checkExternalClick: function( event ) {
966 if ( !$.datepicker._curInst ) {
970 var $target = $( event.target ),
971 inst = $.datepicker._getInst( $target[ 0 ] );
973 if ( ( ( $target[ 0 ].id !== $.datepicker._mainDivId &&
974 $target.parents( "#" + $.datepicker._mainDivId ).length === 0 &&
975 !$target.hasClass( $.datepicker.markerClassName ) &&
976 !$target.closest( "." + $.datepicker._triggerClass ).length &&
977 $.datepicker._datepickerShowing && !( $.datepicker._inDialog && $.blockUI ) ) ) ||
978 ( $target.hasClass( $.datepicker.markerClassName ) && $.datepicker._curInst !== inst ) ) {
979 $.datepicker._hideDatepicker();
983 /* Adjust one of the date sub-fields. */
984 _adjustDate: function( id, offset, period ) {
985 var target = $( id ),
986 inst = this._getInst( target[ 0 ] );
988 if ( this._isDisabledDatepicker( target[ 0 ] ) ) {
991 this._adjustInstDate( inst, offset +
992 ( period === "M" ? this._get( inst, "showCurrentAtPos" ) : 0 ), // undo positioning
994 this._updateDatepicker( inst );
997 /* Action for current link. */
998 _gotoToday: function( id ) {
1001 inst = this._getInst( target[ 0 ] );
1003 if ( this._get( inst, "gotoCurrent" ) && inst.currentDay ) {
1004 inst.selectedDay = inst.currentDay;
1005 inst.drawMonth = inst.selectedMonth = inst.currentMonth;
1006 inst.drawYear = inst.selectedYear = inst.currentYear;
1009 inst.selectedDay = date.getDate();
1010 inst.drawMonth = inst.selectedMonth = date.getMonth();
1011 inst.drawYear = inst.selectedYear = date.getFullYear();
1013 this._notifyChange( inst );
1014 this._adjustDate( target );
1017 /* Action for selecting a new month/year. */
1018 _selectMonthYear: function( id, select, period ) {
1019 var target = $( id ),
1020 inst = this._getInst( target[ 0 ] );
1022 inst[ "selected" + ( period === "M" ? "Month" : "Year" ) ] =
1023 inst[ "draw" + ( period === "M" ? "Month" : "Year" ) ] =
1024 parseInt( select.options[ select.selectedIndex ].value, 10 );
1026 this._notifyChange( inst );
1027 this._adjustDate( target );
1030 /* Action for selecting a day. */
1031 _selectDay: function( id, month, year, td ) {
1035 if ( $( td ).hasClass( this._unselectableClass ) || this._isDisabledDatepicker( target[ 0 ] ) ) {
1039 inst = this._getInst( target[ 0 ] );
1040 inst.selectedDay = inst.currentDay = $( "a", td ).html();
1041 inst.selectedMonth = inst.currentMonth = month;
1042 inst.selectedYear = inst.currentYear = year;
1043 this._selectDate( id, this._formatDate( inst,
1044 inst.currentDay, inst.currentMonth, inst.currentYear ) );
1047 /* Erase the input field and hide the date picker. */
1048 _clearDate: function( id ) {
1049 var target = $( id );
1050 this._selectDate( target, "" );
1053 /* Update the input field with the selected date. */
1054 _selectDate: function( id, dateStr ) {
1057 inst = this._getInst( target[ 0 ] );
1059 dateStr = ( dateStr != null ? dateStr : this._formatDate( inst ) );
1061 inst.input.val( dateStr );
1063 this._updateAlternate( inst );
1065 onSelect = this._get( inst, "onSelect" );
1067 onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] ); // trigger custom callback
1068 } else if ( inst.input ) {
1069 inst.input.trigger( "change" ); // fire the change event
1072 if ( inst.inline ) {
1073 this._updateDatepicker( inst );
1075 this._hideDatepicker();
1076 this._lastInput = inst.input[ 0 ];
1077 if ( typeof( inst.input[ 0 ] ) !== "object" ) {
1078 inst.input.trigger( "focus" ); // restore focus
1080 this._lastInput = null;
1084 /* Update any alternate field to synchronise with the main field. */
1085 _updateAlternate: function( inst ) {
1086 var altFormat, date, dateStr,
1087 altField = this._get( inst, "altField" );
1089 if ( altField ) { // update alternate field too
1090 altFormat = this._get( inst, "altFormat" ) || this._get( inst, "dateFormat" );
1091 date = this._getDate( inst );
1092 dateStr = this.formatDate( altFormat, date, this._getFormatConfig( inst ) );
1093 $( altField ).val( dateStr );
1097 /* Set as beforeShowDay function to prevent selection of weekends.
1098 * @param date Date - the date to customise
1099 * @return [boolean, string] - is this date selectable?, what is its CSS class?
1101 noWeekends: function( date ) {
1102 var day = date.getDay();
1103 return [ ( day > 0 && day < 6 ), "" ];
1106 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
1107 * @param date Date - the date to get the week for
1108 * @return number - the number of the week within the year that contains this date
1110 iso8601Week: function( date ) {
1112 checkDate = new Date( date.getTime() );
1114 // Find Thursday of this week starting on Monday
1115 checkDate.setDate( checkDate.getDate() + 4 - ( checkDate.getDay() || 7 ) );
1117 time = checkDate.getTime();
1118 checkDate.setMonth( 0 ); // Compare with Jan 1
1119 checkDate.setDate( 1 );
1120 return Math.floor( Math.round( ( time - checkDate ) / 86400000 ) / 7 ) + 1;
1123 /* Parse a string value into a date object.
1124 * See formatDate below for the possible formats.
1126 * @param format string - the expected format of the date
1127 * @param value string - the date in the above format
1128 * @param settings Object - attributes include:
1129 * shortYearCutoff number - the cutoff year for determining the century (optional)
1130 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
1131 * dayNames string[7] - names of the days from Sunday (optional)
1132 * monthNamesShort string[12] - abbreviated names of the months (optional)
1133 * monthNames string[12] - names of the months (optional)
1134 * @return Date - the extracted date value or null if value is blank
1136 parseDate: function( format, value, settings ) {
1137 if ( format == null || value == null ) {
1138 throw "Invalid arguments";
1141 value = ( typeof value === "object" ? value.toString() : value + "" );
1142 if ( value === "" ) {
1146 var iFormat, dim, extra,
1148 shortYearCutoffTemp = ( settings ? settings.shortYearCutoff : null ) || this._defaults.shortYearCutoff,
1149 shortYearCutoff = ( typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
1150 new Date().getFullYear() % 100 + parseInt( shortYearCutoffTemp, 10 ) ),
1151 dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort,
1152 dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
1153 monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
1154 monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,
1162 // Check whether a format character is doubled
1163 lookAhead = function( match ) {
1164 var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
1171 // Extract a number from the string value
1172 getNumber = function( match ) {
1173 var isDoubled = lookAhead( match ),
1174 size = ( match === "@" ? 14 : ( match === "!" ? 20 :
1175 ( match === "y" && isDoubled ? 4 : ( match === "o" ? 3 : 2 ) ) ) ),
1176 minSize = ( match === "y" ? size : 1 ),
1177 digits = new RegExp( "^\\d{" + minSize + "," + size + "}" ),
1178 num = value.substring( iValue ).match( digits );
1180 throw "Missing number at position " + iValue;
1182 iValue += num[ 0 ].length;
1183 return parseInt( num[ 0 ], 10 );
1186 // Extract a name from the string value and convert to an index
1187 getName = function( match, shortNames, longNames ) {
1189 names = $.map( lookAhead( match ) ? longNames : shortNames, function( v, k ) {
1190 return [ [ k, v ] ];
1191 } ).sort( function( a, b ) {
1192 return -( a[ 1 ].length - b[ 1 ].length );
1195 $.each( names, function( i, pair ) {
1196 var name = pair[ 1 ];
1197 if ( value.substr( iValue, name.length ).toLowerCase() === name.toLowerCase() ) {
1199 iValue += name.length;
1203 if ( index !== -1 ) {
1206 throw "Unknown name at position " + iValue;
1210 // Confirm that a literal character matches the string value
1211 checkLiteral = function() {
1212 if ( value.charAt( iValue ) !== format.charAt( iFormat ) ) {
1213 throw "Unexpected literal at position " + iValue;
1218 for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
1220 if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
1226 switch ( format.charAt( iFormat ) ) {
1228 day = getNumber( "d" );
1231 getName( "D", dayNamesShort, dayNames );
1234 doy = getNumber( "o" );
1237 month = getNumber( "m" );
1240 month = getName( "M", monthNamesShort, monthNames );
1243 year = getNumber( "y" );
1246 date = new Date( getNumber( "@" ) );
1247 year = date.getFullYear();
1248 month = date.getMonth() + 1;
1249 day = date.getDate();
1252 date = new Date( ( getNumber( "!" ) - this._ticksTo1970 ) / 10000 );
1253 year = date.getFullYear();
1254 month = date.getMonth() + 1;
1255 day = date.getDate();
1258 if ( lookAhead( "'" ) ) {
1270 if ( iValue < value.length ) {
1271 extra = value.substr( iValue );
1272 if ( !/^\s+/.test( extra ) ) {
1273 throw "Extra/unparsed characters found in date: " + extra;
1277 if ( year === -1 ) {
1278 year = new Date().getFullYear();
1279 } else if ( year < 100 ) {
1280 year += new Date().getFullYear() - new Date().getFullYear() % 100 +
1281 ( year <= shortYearCutoff ? 0 : -100 );
1288 dim = this._getDaysInMonth( year, month - 1 );
1297 date = this._daylightSavingAdjust( new Date( year, month - 1, day ) );
1298 if ( date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day ) {
1299 throw "Invalid date"; // E.g. 31/02/00
1304 /* Standard date formats. */
1305 ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
1306 COOKIE: "D, dd M yy",
1307 ISO_8601: "yy-mm-dd",
1308 RFC_822: "D, d M y",
1309 RFC_850: "DD, dd-M-y",
1310 RFC_1036: "D, d M y",
1311 RFC_1123: "D, d M yy",
1312 RFC_2822: "D, d M yy",
1313 RSS: "D, d M y", // RFC 822
1316 W3C: "yy-mm-dd", // ISO 8601
1318 _ticksTo1970: ( ( ( 1970 - 1 ) * 365 + Math.floor( 1970 / 4 ) - Math.floor( 1970 / 100 ) +
1319 Math.floor( 1970 / 400 ) ) * 24 * 60 * 60 * 10000000 ),
1321 /* Format a date object into a string value.
1322 * The format can be combinations of the following:
1323 * d - day of month (no leading zero)
1324 * dd - day of month (two digit)
1325 * o - day of year (no leading zeros)
1326 * oo - day of year (three digit)
1327 * D - day name short
1328 * DD - day name long
1329 * m - month of year (no leading zero)
1330 * mm - month of year (two digit)
1331 * M - month name short
1332 * MM - month name long
1333 * y - year (two digit)
1334 * yy - year (four digit)
1335 * @ - Unix timestamp (ms since 01/01/1970)
1336 * ! - Windows ticks (100ns since 01/01/0001)
1337 * "..." - literal text
1340 * @param format string - the desired format of the date
1341 * @param date Date - the date value to format
1342 * @param settings Object - attributes include:
1343 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
1344 * dayNames string[7] - names of the days from Sunday (optional)
1345 * monthNamesShort string[12] - abbreviated names of the months (optional)
1346 * monthNames string[12] - names of the months (optional)
1347 * @return string - the date in the above format
1349 formatDate: function( format, date, settings ) {
1355 dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort,
1356 dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
1357 monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
1358 monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,
1360 // Check whether a format character is doubled
1361 lookAhead = function( match ) {
1362 var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
1369 // Format a number, with leading zero if necessary
1370 formatNumber = function( match, value, len ) {
1371 var num = "" + value;
1372 if ( lookAhead( match ) ) {
1373 while ( num.length < len ) {
1380 // Format a name, short or long as requested
1381 formatName = function( match, value, shortNames, longNames ) {
1382 return ( lookAhead( match ) ? longNames[ value ] : shortNames[ value ] );
1388 for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
1390 if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
1393 output += format.charAt( iFormat );
1396 switch ( format.charAt( iFormat ) ) {
1398 output += formatNumber( "d", date.getDate(), 2 );
1401 output += formatName( "D", date.getDay(), dayNamesShort, dayNames );
1404 output += formatNumber( "o",
1405 Math.round( ( new Date( date.getFullYear(), date.getMonth(), date.getDate() ).getTime() - new Date( date.getFullYear(), 0, 0 ).getTime() ) / 86400000 ), 3 );
1408 output += formatNumber( "m", date.getMonth() + 1, 2 );
1411 output += formatName( "M", date.getMonth(), monthNamesShort, monthNames );
1414 output += ( lookAhead( "y" ) ? date.getFullYear() :
1415 ( date.getFullYear() % 100 < 10 ? "0" : "" ) + date.getFullYear() % 100 );
1418 output += date.getTime();
1421 output += date.getTime() * 10000 + this._ticksTo1970;
1424 if ( lookAhead( "'" ) ) {
1431 output += format.charAt( iFormat );
1439 /* Extract all possible characters from the date format. */
1440 _possibleChars: function( format ) {
1445 // Check whether a format character is doubled
1446 lookAhead = function( match ) {
1447 var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
1454 for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
1456 if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
1459 chars += format.charAt( iFormat );
1462 switch ( format.charAt( iFormat ) ) {
1463 case "d": case "m": case "y": case "@":
1464 chars += "0123456789";
1467 return null; // Accept anything
1469 if ( lookAhead( "'" ) ) {
1476 chars += format.charAt( iFormat );
1483 /* Get a setting value, defaulting if necessary. */
1484 _get: function( inst, name ) {
1485 return inst.settings[ name ] !== undefined ?
1486 inst.settings[ name ] : this._defaults[ name ];
1489 /* Parse existing date and initialise date picker. */
1490 _setDateFromField: function( inst, noDefault ) {
1491 if ( inst.input.val() === inst.lastVal ) {
1495 var dateFormat = this._get( inst, "dateFormat" ),
1496 dates = inst.lastVal = inst.input ? inst.input.val() : null,
1497 defaultDate = this._getDefaultDate( inst ),
1499 settings = this._getFormatConfig( inst );
1502 date = this.parseDate( dateFormat, dates, settings ) || defaultDate;
1504 dates = ( noDefault ? "" : dates );
1506 inst.selectedDay = date.getDate();
1507 inst.drawMonth = inst.selectedMonth = date.getMonth();
1508 inst.drawYear = inst.selectedYear = date.getFullYear();
1509 inst.currentDay = ( dates ? date.getDate() : 0 );
1510 inst.currentMonth = ( dates ? date.getMonth() : 0 );
1511 inst.currentYear = ( dates ? date.getFullYear() : 0 );
1512 this._adjustInstDate( inst );
1515 /* Retrieve the default date shown on opening. */
1516 _getDefaultDate: function( inst ) {
1517 return this._restrictMinMax( inst,
1518 this._determineDate( inst, this._get( inst, "defaultDate" ), new Date() ) );
1521 /* A date may be specified as an exact value or a relative one. */
1522 _determineDate: function( inst, date, defaultDate ) {
1523 var offsetNumeric = function( offset ) {
1524 var date = new Date();
1525 date.setDate( date.getDate() + offset );
1528 offsetString = function( offset ) {
1530 return $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ),
1531 offset, $.datepicker._getFormatConfig( inst ) );
1538 var date = ( offset.toLowerCase().match( /^c/ ) ?
1539 $.datepicker._getDate( inst ) : null ) || new Date(),
1540 year = date.getFullYear(),
1541 month = date.getMonth(),
1542 day = date.getDate(),
1543 pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
1544 matches = pattern.exec( offset );
1547 switch ( matches[ 2 ] || "d" ) {
1548 case "d" : case "D" :
1549 day += parseInt( matches[ 1 ], 10 ); break;
1550 case "w" : case "W" :
1551 day += parseInt( matches[ 1 ], 10 ) * 7; break;
1552 case "m" : case "M" :
1553 month += parseInt( matches[ 1 ], 10 );
1554 day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) );
1556 case "y": case "Y" :
1557 year += parseInt( matches[ 1 ], 10 );
1558 day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) );
1561 matches = pattern.exec( offset );
1563 return new Date( year, month, day );
1565 newDate = ( date == null || date === "" ? defaultDate : ( typeof date === "string" ? offsetString( date ) :
1566 ( typeof date === "number" ? ( isNaN( date ) ? defaultDate : offsetNumeric( date ) ) : new Date( date.getTime() ) ) ) );
1568 newDate = ( newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate );
1570 newDate.setHours( 0 );
1571 newDate.setMinutes( 0 );
1572 newDate.setSeconds( 0 );
1573 newDate.setMilliseconds( 0 );
1575 return this._daylightSavingAdjust( newDate );
1578 /* Handle switch to/from daylight saving.
1579 * Hours may be non-zero on daylight saving cut-over:
1580 * > 12 when midnight changeover, but then cannot generate
1581 * midnight datetime, so jump to 1AM, otherwise reset.
1582 * @param date (Date) the date to check
1583 * @return (Date) the corrected date
1585 _daylightSavingAdjust: function( date ) {
1589 date.setHours( date.getHours() > 12 ? date.getHours() + 2 : 0 );
1593 /* Set the date(s) directly. */
1594 _setDate: function( inst, date, noChange ) {
1596 origMonth = inst.selectedMonth,
1597 origYear = inst.selectedYear,
1598 newDate = this._restrictMinMax( inst, this._determineDate( inst, date, new Date() ) );
1600 inst.selectedDay = inst.currentDay = newDate.getDate();
1601 inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
1602 inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
1603 if ( ( origMonth !== inst.selectedMonth || origYear !== inst.selectedYear ) && !noChange ) {
1604 this._notifyChange( inst );
1606 this._adjustInstDate( inst );
1608 inst.input.val( clear ? "" : this._formatDate( inst ) );
1612 /* Retrieve the date(s) directly. */
1613 _getDate: function( inst ) {
1614 var startDate = ( !inst.currentYear || ( inst.input && inst.input.val() === "" ) ? null :
1615 this._daylightSavingAdjust( new Date(
1616 inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
1620 /* Attach the onxxx handlers. These are declared statically so
1621 * they work with static code transformers like Caja.
1623 _attachHandlers: function( inst ) {
1624 var stepMonths = this._get( inst, "stepMonths" ),
1625 id = "#" + inst.id.replace( /\\\\/g, "\\" );
1626 inst.dpDiv.find( "[data-handler]" ).map( function() {
1629 $.datepicker._adjustDate( id, -stepMonths, "M" );
1632 $.datepicker._adjustDate( id, +stepMonths, "M" );
1635 $.datepicker._hideDatepicker();
1638 $.datepicker._gotoToday( id );
1640 selectDay: function() {
1641 $.datepicker._selectDay( id, +this.getAttribute( "data-month" ), +this.getAttribute( "data-year" ), this );
1644 selectMonth: function() {
1645 $.datepicker._selectMonthYear( id, this, "M" );
1648 selectYear: function() {
1649 $.datepicker._selectMonthYear( id, this, "Y" );
1653 $( this ).on( this.getAttribute( "data-event" ), handler[ this.getAttribute( "data-handler" ) ] );
1657 /* Generate the HTML for the current state of the date picker. */
1658 _generateHTML: function( inst ) {
1659 var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
1660 controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
1661 monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
1662 selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
1663 cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
1664 printDate, dRow, tbody, daySettings, otherMonth, unselectable,
1665 tempDate = new Date(),
1666 today = this._daylightSavingAdjust(
1667 new Date( tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() ) ), // clear time
1668 isRTL = this._get( inst, "isRTL" ),
1669 showButtonPanel = this._get( inst, "showButtonPanel" ),
1670 hideIfNoPrevNext = this._get( inst, "hideIfNoPrevNext" ),
1671 navigationAsDateFormat = this._get( inst, "navigationAsDateFormat" ),
1672 numMonths = this._getNumberOfMonths( inst ),
1673 showCurrentAtPos = this._get( inst, "showCurrentAtPos" ),
1674 stepMonths = this._get( inst, "stepMonths" ),
1675 isMultiMonth = ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ),
1676 currentDate = this._daylightSavingAdjust( ( !inst.currentDay ? new Date( 9999, 9, 9 ) :
1677 new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) ),
1678 minDate = this._getMinMaxDate( inst, "min" ),
1679 maxDate = this._getMinMaxDate( inst, "max" ),
1680 drawMonth = inst.drawMonth - showCurrentAtPos,
1681 drawYear = inst.drawYear;
1683 if ( drawMonth < 0 ) {
1688 maxDraw = this._daylightSavingAdjust( new Date( maxDate.getFullYear(),
1689 maxDate.getMonth() - ( numMonths[ 0 ] * numMonths[ 1 ] ) + 1, maxDate.getDate() ) );
1690 maxDraw = ( minDate && maxDraw < minDate ? minDate : maxDraw );
1691 while ( this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 ) ) > maxDraw ) {
1693 if ( drawMonth < 0 ) {
1699 inst.drawMonth = drawMonth;
1700 inst.drawYear = drawYear;
1702 prevText = this._get( inst, "prevText" );
1703 prevText = ( !navigationAsDateFormat ? prevText : this.formatDate( prevText,
1704 this._daylightSavingAdjust( new Date( drawYear, drawMonth - stepMonths, 1 ) ),
1705 this._getFormatConfig( inst ) ) );
1707 prev = ( this._canAdjustMonth( inst, -1, drawYear, drawMonth ) ?
1708 "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
1709 " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w" ) + "'>" + prevText + "</span></a>" :
1710 ( 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>" ) );
1712 nextText = this._get( inst, "nextText" );
1713 nextText = ( !navigationAsDateFormat ? nextText : this.formatDate( nextText,
1714 this._daylightSavingAdjust( new Date( drawYear, drawMonth + stepMonths, 1 ) ),
1715 this._getFormatConfig( inst ) ) );
1717 next = ( this._canAdjustMonth( inst, +1, drawYear, drawMonth ) ?
1718 "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
1719 " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e" ) + "'>" + nextText + "</span></a>" :
1720 ( 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>" ) );
1722 currentText = this._get( inst, "currentText" );
1723 gotoDate = ( this._get( inst, "gotoCurrent" ) && inst.currentDay ? currentDate : today );
1724 currentText = ( !navigationAsDateFormat ? currentText :
1725 this.formatDate( currentText, gotoDate, this._getFormatConfig( inst ) ) );
1727 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'>" +
1728 this._get( inst, "closeText" ) + "</button>" : "" );
1730 buttonPanel = ( showButtonPanel ) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + ( isRTL ? controls : "" ) +
1731 ( 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'" +
1732 ">" + currentText + "</button>" : "" ) + ( isRTL ? "" : controls ) + "</div>" : "";
1734 firstDay = parseInt( this._get( inst, "firstDay" ), 10 );
1735 firstDay = ( isNaN( firstDay ) ? 0 : firstDay );
1737 showWeek = this._get( inst, "showWeek" );
1738 dayNames = this._get( inst, "dayNames" );
1739 dayNamesMin = this._get( inst, "dayNamesMin" );
1740 monthNames = this._get( inst, "monthNames" );
1741 monthNamesShort = this._get( inst, "monthNamesShort" );
1742 beforeShowDay = this._get( inst, "beforeShowDay" );
1743 showOtherMonths = this._get( inst, "showOtherMonths" );
1744 selectOtherMonths = this._get( inst, "selectOtherMonths" );
1745 defaultDate = this._getDefaultDate( inst );
1748 for ( row = 0; row < numMonths[ 0 ]; row++ ) {
1751 for ( col = 0; col < numMonths[ 1 ]; col++ ) {
1752 selectedDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, inst.selectedDay ) );
1753 cornerClass = " ui-corner-all";
1755 if ( isMultiMonth ) {
1756 calender += "<div class='ui-datepicker-group";
1757 if ( numMonths[ 1 ] > 1 ) {
1759 case 0: calender += " ui-datepicker-group-first";
1760 cornerClass = " ui-corner-" + ( isRTL ? "right" : "left" ); break;
1761 case numMonths[ 1 ] - 1: calender += " ui-datepicker-group-last";
1762 cornerClass = " ui-corner-" + ( isRTL ? "left" : "right" ); break;
1763 default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
1768 calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
1769 ( /all|left/.test( cornerClass ) && row === 0 ? ( isRTL ? next : prev ) : "" ) +
1770 ( /all|right/.test( cornerClass ) && row === 0 ? ( isRTL ? prev : next ) : "" ) +
1771 this._generateMonthYearHeader( inst, drawMonth, drawYear, minDate, maxDate,
1772 row > 0 || col > 0, monthNames, monthNamesShort ) + // draw month headers
1773 "</div><table class='ui-datepicker-calendar'><thead>" +
1775 thead = ( showWeek ? "<th class='ui-datepicker-week-col'>" + this._get( inst, "weekHeader" ) + "</th>" : "" );
1776 for ( dow = 0; dow < 7; dow++ ) { // days of the week
1777 day = ( dow + firstDay ) % 7;
1778 thead += "<th scope='col'" + ( ( dow + firstDay + 6 ) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "" ) + ">" +
1779 "<span title='" + dayNames[ day ] + "'>" + dayNamesMin[ day ] + "</span></th>";
1781 calender += thead + "</tr></thead><tbody>";
1782 daysInMonth = this._getDaysInMonth( drawYear, drawMonth );
1783 if ( drawYear === inst.selectedYear && drawMonth === inst.selectedMonth ) {
1784 inst.selectedDay = Math.min( inst.selectedDay, daysInMonth );
1786 leadDays = ( this._getFirstDayOfMonth( drawYear, drawMonth ) - firstDay + 7 ) % 7;
1787 curRows = Math.ceil( ( leadDays + daysInMonth ) / 7 ); // calculate the number of rows to generate
1788 numRows = ( isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows ); //If multiple months, use the higher number of rows (see #7043)
1789 this.maxRows = numRows;
1790 printDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 - leadDays ) );
1791 for ( dRow = 0; dRow < numRows; dRow++ ) { // create date picker rows
1793 tbody = ( !showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
1794 this._get( inst, "calculateWeek" )( printDate ) + "</td>" );
1795 for ( dow = 0; dow < 7; dow++ ) { // create date picker days
1796 daySettings = ( beforeShowDay ?
1797 beforeShowDay.apply( ( inst.input ? inst.input[ 0 ] : null ), [ printDate ] ) : [ true, "" ] );
1798 otherMonth = ( printDate.getMonth() !== drawMonth );
1799 unselectable = ( otherMonth && !selectOtherMonths ) || !daySettings[ 0 ] ||
1800 ( minDate && printDate < minDate ) || ( maxDate && printDate > maxDate );
1801 tbody += "<td class='" +
1802 ( ( dow + firstDay + 6 ) % 7 >= 5 ? " ui-datepicker-week-end" : "" ) + // highlight weekends
1803 ( otherMonth ? " ui-datepicker-other-month" : "" ) + // highlight days from other months
1804 ( ( printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent ) || // user pressed key
1805 ( defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime() ) ?
1807 // or defaultDate is current printedDate and defaultDate is selectedDate
1808 " " + this._dayOverClass : "" ) + // highlight selected day
1809 ( unselectable ? " " + this._unselectableClass + " ui-state-disabled" : "" ) + // highlight unselectable days
1810 ( otherMonth && !showOtherMonths ? "" : " " + daySettings[ 1 ] + // highlight custom dates
1811 ( printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "" ) + // highlight selected day
1812 ( printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "" ) ) + "'" + // highlight today (if different)
1813 ( ( !otherMonth || showOtherMonths ) && daySettings[ 2 ] ? " title='" + daySettings[ 2 ].replace( /'/g, "'" ) + "'" : "" ) + // cell title
1814 ( unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'" ) + ">" + // actions
1815 ( otherMonth && !showOtherMonths ? " " : // display for other months
1816 ( unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
1817 ( printDate.getTime() === today.getTime() ? " ui-state-highlight" : "" ) +
1818 ( printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "" ) + // highlight selected day
1819 ( otherMonth ? " ui-priority-secondary" : "" ) + // distinguish dates from other months
1820 "' href='#'>" + printDate.getDate() + "</a>" ) ) + "</td>"; // display selectable date
1821 printDate.setDate( printDate.getDate() + 1 );
1822 printDate = this._daylightSavingAdjust( printDate );
1824 calender += tbody + "</tr>";
1827 if ( drawMonth > 11 ) {
1831 calender += "</tbody></table>" + ( isMultiMonth ? "</div>" +
1832 ( ( numMonths[ 0 ] > 0 && col === numMonths[ 1 ] - 1 ) ? "<div class='ui-datepicker-row-break'></div>" : "" ) : "" );
1837 html += buttonPanel;
1838 inst._keyEvent = false;
1842 /* Generate the month and year header. */
1843 _generateMonthYearHeader: function( inst, drawMonth, drawYear, minDate, maxDate,
1844 secondary, monthNames, monthNamesShort ) {
1846 var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
1847 changeMonth = this._get( inst, "changeMonth" ),
1848 changeYear = this._get( inst, "changeYear" ),
1849 showMonthAfterYear = this._get( inst, "showMonthAfterYear" ),
1850 html = "<div class='ui-datepicker-title'>",
1854 if ( secondary || !changeMonth ) {
1855 monthHtml += "<span class='ui-datepicker-month'>" + monthNames[ drawMonth ] + "</span>";
1857 inMinYear = ( minDate && minDate.getFullYear() === drawYear );
1858 inMaxYear = ( maxDate && maxDate.getFullYear() === drawYear );
1859 monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
1860 for ( month = 0; month < 12; month++ ) {
1861 if ( ( !inMinYear || month >= minDate.getMonth() ) && ( !inMaxYear || month <= maxDate.getMonth() ) ) {
1862 monthHtml += "<option value='" + month + "'" +
1863 ( month === drawMonth ? " selected='selected'" : "" ) +
1864 ">" + monthNamesShort[ month ] + "</option>";
1867 monthHtml += "</select>";
1870 if ( !showMonthAfterYear ) {
1871 html += monthHtml + ( secondary || !( changeMonth && changeYear ) ? " " : "" );
1875 if ( !inst.yearshtml ) {
1876 inst.yearshtml = "";
1877 if ( secondary || !changeYear ) {
1878 html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
1881 // determine range of years to display
1882 years = this._get( inst, "yearRange" ).split( ":" );
1883 thisYear = new Date().getFullYear();
1884 determineYear = function( value ) {
1885 var year = ( value.match( /c[+\-].*/ ) ? drawYear + parseInt( value.substring( 1 ), 10 ) :
1886 ( value.match( /[+\-].*/ ) ? thisYear + parseInt( value, 10 ) :
1887 parseInt( value, 10 ) ) );
1888 return ( isNaN( year ) ? thisYear : year );
1890 year = determineYear( years[ 0 ] );
1891 endYear = Math.max( year, determineYear( years[ 1 ] || "" ) );
1892 year = ( minDate ? Math.max( year, minDate.getFullYear() ) : year );
1893 endYear = ( maxDate ? Math.min( endYear, maxDate.getFullYear() ) : endYear );
1894 inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
1895 for ( ; year <= endYear; year++ ) {
1896 inst.yearshtml += "<option value='" + year + "'" +
1897 ( year === drawYear ? " selected='selected'" : "" ) +
1898 ">" + year + "</option>";
1900 inst.yearshtml += "</select>";
1902 html += inst.yearshtml;
1903 inst.yearshtml = null;
1907 html += this._get( inst, "yearSuffix" );
1908 if ( showMonthAfterYear ) {
1909 html += ( secondary || !( changeMonth && changeYear ) ? " " : "" ) + monthHtml;
1911 html += "</div>"; // Close datepicker_header
1915 /* Adjust one of the date sub-fields. */
1916 _adjustInstDate: function( inst, offset, period ) {
1917 var year = inst.selectedYear + ( period === "Y" ? offset : 0 ),
1918 month = inst.selectedMonth + ( period === "M" ? offset : 0 ),
1919 day = Math.min( inst.selectedDay, this._getDaysInMonth( year, month ) ) + ( period === "D" ? offset : 0 ),
1920 date = this._restrictMinMax( inst, this._daylightSavingAdjust( new Date( year, month, day ) ) );
1922 inst.selectedDay = date.getDate();
1923 inst.drawMonth = inst.selectedMonth = date.getMonth();
1924 inst.drawYear = inst.selectedYear = date.getFullYear();
1925 if ( period === "M" || period === "Y" ) {
1926 this._notifyChange( inst );
1930 /* Ensure a date is within any min/max bounds. */
1931 _restrictMinMax: function( inst, date ) {
1932 var minDate = this._getMinMaxDate( inst, "min" ),
1933 maxDate = this._getMinMaxDate( inst, "max" ),
1934 newDate = ( minDate && date < minDate ? minDate : date );
1935 return ( maxDate && newDate > maxDate ? maxDate : newDate );
1938 /* Notify change of month/year. */
1939 _notifyChange: function( inst ) {
1940 var onChange = this._get( inst, "onChangeMonthYear" );
1942 onChange.apply( ( inst.input ? inst.input[ 0 ] : null ),
1943 [ inst.selectedYear, inst.selectedMonth + 1, inst ] );
1947 /* Determine the number of months to show. */
1948 _getNumberOfMonths: function( inst ) {
1949 var numMonths = this._get( inst, "numberOfMonths" );
1950 return ( numMonths == null ? [ 1, 1 ] : ( typeof numMonths === "number" ? [ 1, numMonths ] : numMonths ) );
1953 /* Determine the current maximum date - ensure no time components are set. */
1954 _getMinMaxDate: function( inst, minMax ) {
1955 return this._determineDate( inst, this._get( inst, minMax + "Date" ), null );
1958 /* Find the number of days in a given month. */
1959 _getDaysInMonth: function( year, month ) {
1960 return 32 - this._daylightSavingAdjust( new Date( year, month, 32 ) ).getDate();
1963 /* Find the day of the week of the first of a month. */
1964 _getFirstDayOfMonth: function( year, month ) {
1965 return new Date( year, month, 1 ).getDay();
1968 /* Determines if we should allow a "next/prev" month display change. */
1969 _canAdjustMonth: function( inst, offset, curYear, curMonth ) {
1970 var numMonths = this._getNumberOfMonths( inst ),
1971 date = this._daylightSavingAdjust( new Date( curYear,
1972 curMonth + ( offset < 0 ? offset : numMonths[ 0 ] * numMonths[ 1 ] ), 1 ) );
1975 date.setDate( this._getDaysInMonth( date.getFullYear(), date.getMonth() ) );
1977 return this._isInRange( inst, date );
1980 /* Is the given date in the accepted range? */
1981 _isInRange: function( inst, date ) {
1982 var yearSplit, currentYear,
1983 minDate = this._getMinMaxDate( inst, "min" ),
1984 maxDate = this._getMinMaxDate( inst, "max" ),
1987 years = this._get( inst, "yearRange" );
1989 yearSplit = years.split( ":" );
1990 currentYear = new Date().getFullYear();
1991 minYear = parseInt( yearSplit[ 0 ], 10 );
1992 maxYear = parseInt( yearSplit[ 1 ], 10 );
1993 if ( yearSplit[ 0 ].match( /[+\-].*/ ) ) {
1994 minYear += currentYear;
1996 if ( yearSplit[ 1 ].match( /[+\-].*/ ) ) {
1997 maxYear += currentYear;
2001 return ( ( !minDate || date.getTime() >= minDate.getTime() ) &&
2002 ( !maxDate || date.getTime() <= maxDate.getTime() ) &&
2003 ( !minYear || date.getFullYear() >= minYear ) &&
2004 ( !maxYear || date.getFullYear() <= maxYear ) );
2007 /* Provide the configuration settings for formatting/parsing. */
2008 _getFormatConfig: function( inst ) {
2009 var shortYearCutoff = this._get( inst, "shortYearCutoff" );
2010 shortYearCutoff = ( typeof shortYearCutoff !== "string" ? shortYearCutoff :
2011 new Date().getFullYear() % 100 + parseInt( shortYearCutoff, 10 ) );
2012 return { shortYearCutoff: shortYearCutoff,
2013 dayNamesShort: this._get( inst, "dayNamesShort" ), dayNames: this._get( inst, "dayNames" ),
2014 monthNamesShort: this._get( inst, "monthNamesShort" ), monthNames: this._get( inst, "monthNames" ) };
2017 /* Format the given date for display. */
2018 _formatDate: function( inst, day, month, year ) {
2020 inst.currentDay = inst.selectedDay;
2021 inst.currentMonth = inst.selectedMonth;
2022 inst.currentYear = inst.selectedYear;
2024 var date = ( day ? ( typeof day === "object" ? day :
2025 this._daylightSavingAdjust( new Date( year, month, day ) ) ) :
2026 this._daylightSavingAdjust( new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
2027 return this.formatDate( this._get( inst, "dateFormat" ), date, this._getFormatConfig( inst ) );
2032 * Bind hover events for datepicker elements.
2033 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
2034 * Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
2036 function datepicker_bindHover( dpDiv ) {
2037 var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
2038 return dpDiv.on( "mouseout", selector, function() {
2039 $( this ).removeClass( "ui-state-hover" );
2040 if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) {
2041 $( this ).removeClass( "ui-datepicker-prev-hover" );
2043 if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) {
2044 $( this ).removeClass( "ui-datepicker-next-hover" );
2047 .on( "mouseover", selector, datepicker_handleMouseover );
2050 function datepicker_handleMouseover() {
2051 if ( !$.datepicker._isDisabledDatepicker( datepicker_instActive.inline ? datepicker_instActive.dpDiv.parent()[ 0 ] : datepicker_instActive.input[ 0 ] ) ) {
2052 $( this ).parents( ".ui-datepicker-calendar" ).find( "a" ).removeClass( "ui-state-hover" );
2053 $( this ).addClass( "ui-state-hover" );
2054 if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) {
2055 $( this ).addClass( "ui-datepicker-prev-hover" );
2057 if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) {
2058 $( this ).addClass( "ui-datepicker-next-hover" );
2063 /* jQuery extend now ignores nulls! */
2064 function datepicker_extendRemove( target, props ) {
2065 $.extend( target, props );
2066 for ( var name in props ) {
2067 if ( props[ name ] == null ) {
2068 target[ name ] = props[ name ];
2074 /* Invoke the datepicker functionality.
2075 @param options string - a command, optionally followed by additional parameters or
2076 Object - settings for attaching new datepicker functionality
2077 @return jQuery object */
2078 $.fn.datepicker = function( options ) {
2080 /* Verify an empty collection wasn't passed - Fixes #6976 */
2081 if ( !this.length ) {
2085 /* Initialise the date picker. */
2086 if ( !$.datepicker.initialized ) {
2087 $( document ).on( "mousedown", $.datepicker._checkExternalClick );
2088 $.datepicker.initialized = true;
2091 /* Append datepicker main container to body if not exist. */
2092 if ( $( "#" + $.datepicker._mainDivId ).length === 0 ) {
2093 $( "body" ).append( $.datepicker.dpDiv );
2096 var otherArgs = Array.prototype.slice.call( arguments, 1 );
2097 if ( typeof options === "string" && ( options === "isDisabled" || options === "getDate" || options === "widget" ) ) {
2098 return $.datepicker[ "_" + options + "Datepicker" ].
2099 apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) );
2101 if ( options === "option" && arguments.length === 2 && typeof arguments[ 1 ] === "string" ) {
2102 return $.datepicker[ "_" + options + "Datepicker" ].
2103 apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) );
2105 return this.each( function() {
2106 typeof options === "string" ?
2107 $.datepicker[ "_" + options + "Datepicker" ].
2108 apply( $.datepicker, [ this ].concat( otherArgs ) ) :
2109 $.datepicker._attachDatepicker( this, options );
2113 $.datepicker = new Datepicker(); // singleton instance
2114 $.datepicker.initialized = false;
2115 $.datepicker.uuid = new Date().getTime();
2116 $.datepicker.version = "1.12.1";
2118 return $.datepicker;