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