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