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