1/* jQuery UI Date Picker v3.3 - previously jQuery Calendar 2 Written by Marc Grabanski ([email protected]) and Keith Wood ([email protected]). 3 4 Copyright (c) 2007 Marc Grabanski (http://marcgrabanski.com/code/ui-datepicker) 5 Dual licensed under the MIT (MIT-LICENSE.txt) 6 and GPL (GPL-LICENSE.txt) licenses. 7 Date: 09-03-2007 */ 8 9/* Date picker manager. 10 Use the singleton instance of this class, $.datepicker, to interact with the date picker. 11 Settings for (groups of) date pickers are maintained in an instance object 12 (DatepickerInstance), allowing multiple different settings on the same page. */ 13 14(function($) { // hide the namespace 15 16function Datepicker() { 17 this.debug = false; // Change this to true to start debugging 18 this._nextId = 0; // Next ID for a date picker instance 19 this._inst = []; // List of instances indexed by ID 20 this._curInst = null; // The current instance in use 21 this._disabledInputs = []; // List of date picker inputs that have been disabled 22 this._datepickerShowing = false; // True if the popup picker is showing , false if not 23 this._inDialog = false; // True if showing within a "dialog", false if not 24 this.regional = []; // Available regional settings, indexed by language code 25 this.regional[''] = { // Default regional settings 26 clearText: 'Clear', // Display text for clear link 27 clearStatus: 'Erase the current date', // Status text for clear link 28 closeText: 'Close', // Display text for close link 29 closeStatus: 'Close without change', // Status text for close link 30 prevText: '<Prev', // Display text for previous month link 31 prevStatus: 'Show the previous month', // Status text for previous month link 32 nextText: 'Next>', // Display text for next month link 33 nextStatus: 'Show the next month', // Status text for next month link 34 currentText: 'Today', // Display text for current month link 35 currentStatus: 'Show the current month', // Status text for current month link 36 monthNames: ['January','February','March','April','May','June', 37 'July','August','September','October','November','December'], // Names of months for drop-down and formatting 38 monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting 39 monthStatus: 'Show a different month', // Status text for selecting a month 40 yearStatus: 'Show a different year', // Status text for selecting a year 41 weekHeader: 'Wk', // Header for the week of the year column 42 weekStatus: 'Week of the year', // Status text for the week of the year column 43 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting 44 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting 45 dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday 46 dayStatus: 'Set DD as first week day', // Status text for the day of the week selection 47 dateStatus: 'Select DD, M d', // Status text for the date selection 48 dateFormat: 'mm/dd/yy', // See format options on parseDate 49 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ... 50 initStatus: 'Select a date', // Initial Status text on opening 51 isRTL: false // True if right-to-left language, false if left-to-right 52 }; 53 this._defaults = { // Global defaults for all the date picker instances 54 showOn: 'focus', // 'focus' for popup on focus, 55 // 'button' for trigger button, or 'both' for either 56 showAnim: 'show', // Name of jQuery animation for popup 57 defaultDate: null, // Used when field is blank: actual date, 58 // +/-number for offset from today, null for today 59 appendText: '', // Display text following the input box, e.g. showing the format 60 buttonText: '...', // Text for trigger button 61 buttonImage: '', // URL for trigger button image 62 imageAsBg: '', 63 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button 64 closeAtTop: true, // True to have the clear/close at the top, 65 // false to have them at the bottom 66 mandatory: false, // True to hide the Clear link, false to include it 67 hideIfNoPrevNext: false, // True to hide next/previous month links 68 // if not applicable, false to just disable them 69 changeMonth: true, // True if month can be selected directly, false if only prev/next 70 changeYear: true, // True if year can be selected directly, false if only prev/next 71 yearRange: '-10:+10', // Range of years to display in drop-down, 72 // either relative to current year (-nn:+nn) or absolute (nnnn:nnnn) 73 changeFirstDay: true, // True to click on day name to change, false to remain as set
74 showOtherMonths: false, // True to show dates in other months, false to leave blank 75 showWeeks: false, // True to show week of the year, false to omit 76 calculateWeek: this.iso8601Week, // How to calculate the week of the year, 77 // takes a Date and returns the number of the week for it 78 shortYearCutoff: '+10', // Short year values < this are in the current century, 79 // > this are in the previous century, 80 // string value starting with '+' for current year + value 81 showStatus: false, // True to show status bar at bottom, false to not show it 82 statusForDate: this.dateStatus, // Function to provide status text for a date - 83 // takes date and instance as parameters, returns display text 84 minDate: null, // The earliest selectable date, or null for no limit 85 maxDate: null, // The latest selectable date, or null for no limit 86 speed: 'medium', // Speed of display/closure 87 beforeShowDay: null, // Function that takes a date and returns an array with 88 // [0] = true if selectable, false if not, 89 // [1] = custom CSS class name(s) or '', e.g. $.datepicker.noWeekends 90 beforeShow: null, // Function that takes an input field and 91 // returns a set of custom settings for the date picker 92 onSelect: null, // Define a callback function when a date is selected 93 numberOfMonths: 1, // Number of months to show at a time 94 stepMonths: 1, // Number of months to step back/forward 95 rangeSelect: false, // Allows for selecting a date range on one date picker 96 rangeSeparator: ' - ' // Text between two dates in a range 97 }; 98 $.extend(this._defaults, this.regional['']); 99 this._datepickerDiv = $('<div id="datepicker_div"></div>'); 100} 101 102$.extend(Datepicker.prototype, { 103 /* Class name added to elements to indicate already configured with a date picker. */ 104 markerClassName: 'hasDatepicker', 105 106 /* Debug logging (if enabled). */ 107 log: function () { 108 if (this.debug) { 109 console.log.apply('', arguments); 110 } 111 }, 112 113 /* Register a new date picker instance - with custom settings. */ 114 _register: function(inst) { 115 var id = this._nextId++; 116 this._inst[id] = inst; 117 return id; 118 }, 119 120 /* Retrieve a particular date picker instance based on its ID. */ 121 _getInst: function(id) { 122 return this._inst[id] || id; 123 }, 124 125 /* Override the default settings for all instances of the date picker. 126 @param settings object - the new settings to use as defaults (anonymous object) 127 @return the manager object */ 128 setDefaults: function(settings) { 129 extendRemove(this._defaults, settings || {}); 130 return this; 131 }, 132 133 /* Handle keystrokes. */ 134 _doKeyDown: function(e) { 135 var inst = $.datepicker._getInst(this._calId); 136 if ($.datepicker._datepickerShowing) { 137 switch (e.keyCode) { 138 case 9: $.datepicker.hideDatepicker(''); 139 break; // hide on tab out 140 case 13: $.datepicker._selectDay(inst, inst._selectedMonth, inst._selectedYear, 141 $('td.datepicker_daysCellOver', inst._datepickerDiv)[0]); 142 return false; // don't submit the form 143 break; // select the value on enter 144 case 27: $.datepicker.hideDatepicker(inst._get('speed')); 145 break; // hide on escape 146 case 33: $.datepicker._adjustDate(inst, 147 (e.ctrlKey ? -1 : -inst._get('stepMonths')), (e.ctrlKey ? 'Y' : 'M')); 148 break; // previous month/year on page up/+ ctrl 149 case 34: $.datepicker._adjustDate(inst, 150 (e.ctrlKey ? +1 : +inst._get('stepMonths')), (e.ctrlKey ? 'Y' : 'M')); 151 break; // next month/year on page down/+ ctrl 152 case 35: if (e.ctrlKey) $.datepicker._clearDate(inst); 153 break; // clear on ctrl+end 154 case 36: if (e.ctrlKey) $.datepicker._gotoToday(inst); 155 break; // current on ctrl+home 156 case 37: if (e.ctrlKey) $.datepicker._adjustDate(inst, -1, 'D'); 157 break; // -1 day on ctrl+left 158 case 38: if (e.ctrlKey) $.datepicker._adjustDate(inst, -7, 'D'); 159 break; // -1 week on ctrl+up 160 case 39: if (e.ctrlKey) $.datepicker._adjustDate(inst, +1, 'D'); 161 break; // +1 day on ctrl+right 162 case 40: if (e.ctrlKey) $.datepicker._adjustDate(inst, +7, 'D'); 163 break; // +1 week on ctrl+down 164 } 165 } 166 else if (e.keyCode == 36 && e.ctrlKey) { // display the date picker on ctrl+home 167 $.datepicker.showFor(this); 168 } 169 }, 170 171 /* Filter entered characters - based on date format. */ 172 _doKeyPress: function(e) { 173 var inst = $.datepicker._getInst(this._calId); 174 var chars = $.datepicker._possibleChars(inst._get('dateFormat')); 175 var chr = String.fromCharCode(e.charCode == undefined ? e.keyCode : e.charCode); 176 return (chr < ' ' || !chars || chars.indexOf(chr) > -1); 177 }, 178 179 /* Attach the date picker to an input field. */ 180 _connectDatepicker: function(target, inst) { 181 var input = $(target); 182 if (this._hasClass(input, this.markerClassName)) { 183 return; 184 } 185 var appendText = inst._get('appendText'); 186 var isRTL = inst._get('isRTL'); 187 if (appendText) { 188 if (isRTL) { 189 input.before('<span class="datepicker_append">' + appendText + '</span>'); 190 } 191 else { 192 input.after('<span class="datepicker_append">' + appendText + '</span>'); 193 } 194 } 195 var showOn = inst._get('showOn'); 196 var buttonImage = inst._get('buttonImage'); 197 if ( showOn == 'focus' || showOn == 'both') { // pop-up date picker when in the marked field
198 input.focus(this.showFor); 199 } 200 if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked 201 var buttonText = inst._get('buttonText'); 202 var buttonImageOnly = inst._get('buttonImageOnly'); 203 var trigger = $(buttonImageOnly ? '<img class="datepicker_trigger showOn'+showOn+'" src="' + 204 buttonImage + '" alt="' + buttonText + '" title="' + buttonText + '"/>' : 205 '<button type="button" class="datepicker_trigger">' + (buttonImage != '' ? 206 '<img src="' + buttonImage + '" alt="' + buttonText + '" title="' + buttonText + '"/>' : 207 buttonText) + '</button>'); 208 input.wrap('<span class="datepicker_wrap"></span>'); 209 if (isRTL) { 210 input.before(trigger); 211 } 212 else { 213 input.after(trigger); 214 } 215 trigger.click(this.showFor); 216 } 217 var imageAsBg = inst._get('imageAsBg'); 218 if ( imageAsBg ) { 219 input.addClass('imageAsBg'); 220 } 221 222 input.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress); 223 input[0]._calId = inst._id; 224 }, 225 226 /* Attach an inline date picker to a div. */ 227 _inlineDatepicker: function(target, inst) { 228 var input = $(target); 229 if (this._hasClass(input, this.markerClassName)) { 230 return; 231 } 232 input.addClass(this.markerClassName).append(inst._datepickerDiv); 233 input[0]._calId = inst._id; 234 this._updateDatepicker(inst); 235 /* @todo: fix _inlineShow automatic resizing 236 - Endless loop bug in IE6. 237 - inst._datepickerDiv.resize doesn't ever fire in firefox. */ 238 // inst._datepickerDiv.resize(function() { $.datepicker._inlineShow(inst); }); 239 }, 240 241 /* Tidy up after displaying the date picker. */ 242 _inlineShow: function(inst) { 243 var numMonths = inst._getNumberOfMonths(); // fix width for dynamic number of date pickers 244 inst._datepickerDiv.width(numMonths[1] * $('.datepicker', inst._datepickerDiv[0]).width()); 245 }, 246 247 /* Does this element have a particular class? */ 248 _hasClass: function(element, className) { 249 var classes = element.attr('class'); 250 return (classes && classes.indexOf(className) > -1); 251 }, 252 253 /* Pop-up the date picker in a "dialog" box. 254 @param dateText string - the initial date to display (in the current format) 255 @param onSelect function - the function(dateText) to call when a date is selected 256 @param settings object - update the dialog date picker instance's settings (anonymous object) 257 @param pos int[2] - coordinates for the dialog's position within the screen or 258 event - with x/y coordinates or 259 leave empty for default (screen centre) 260 @return the manager object */ 261 dialogDatepicker: function(dateText, onSelect, settings, pos) { 262 var inst = this._dialogInst; // internal instance 263 if (!inst) { 264 inst = this._dialogInst = new DatepickerInstance({}, false); 265 this._dialogInput = $('<input type="text" size="1" style="position: absolute; top: -100px;"/>'); 266 this._dialogInput.keydown(this._doKeyDown); 267 $('body').append(this._dialogInput); 268 this._dialogInput[0]._calId = inst._id; 269 } 270 extendRemove(inst._settings, settings || {}); 271 this._dialogInput.val(dateText); 272 273 this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null); 274 if (!this._pos) { 275 var browserWidth = window.innerWidth || document.documentElement.clientWidth || 276 document.body.clientWidth; 277 var browserHeight = window.innerHeight || document.documentElement.clientHeight || 278 document.body.clientHeight; 279 var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; 280 var scrollY = document.documentElement.scrollTop || document.body.scrollTop; 281 this._pos = // should use actual width/height below 282 [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY]; 283 } 284 285 // move input on screen for focus, but hidden behind dialog 286 this._dialogInput.css('left', this._pos[0] + 'px').css('top', this._pos[1] + 'px'); 287 inst._settings.onSelect = onSelect; 288 this._inDialog = true; 289 this._datepickerDiv.addClass('datepicker_dialog'); 290 this.showFor(this._dialogInput[0]); 291 if ($.blockUI) { 292 $.blockUI(this._datepickerDiv); 293 } 294 return this; 295 }, 296 297 /* Pop-up the date picker for a given input field. 298 @param control element - the input field attached to the date picker or
299 string - the ID or other jQuery selector of the input field or 300 object - jQuery object for input field 301 @return the manager object */ 302 showFor: function(control) { 303 control = (control.jquery ? control[0] : 304 (typeof control == 'string' ? $(control)[0] : control)); 305 var input = (control.nodeName && control.nodeName.toLowerCase() == 'input' ? control : this); 306 if (input.nodeName.toLowerCase() != 'input') { // find from button/image trigger 307 input = $('input', input.parentNode)[0]; 308 } 309 if ($.datepicker._lastInput == input) { // already here 310 return; 311 } 312 if ($(input).isDisabledDatepicker()) { 313 return; 314 } 315 var inst = $.datepicker._getInst(input._calId); 316 var beforeShow = inst._get('beforeShow'); 317 extendRemove(inst._settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {})); 318 $.datepicker.hideDatepicker(''); 319 $.datepicker._lastInput = input; 320 inst._setDateFromField(input); 321 if ($.datepicker._inDialog) { // hide cursor 322 input.value = ''; 323 } 324 if (!$.datepicker._pos) { // position below input 325 $.datepicker._pos = $.datepicker._findPos(input); 326 $.datepicker._pos[1] += input.offsetHeight; // add the height 327 } 328 var isFixed = false; 329 $(input).parents().each(function() { 330 isFixed |= $(this).css('position') == 'fixed'; 331 }); 332 if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled 333 $.datepicker._pos[0] -= document.documentElement.scrollLeft; 334 $.datepicker._pos[1] -= document.documentElement.scrollTop; 335 } 336 inst._datepickerDiv.css('position', ($.datepicker._inDialog && $.blockUI ? 337 'static' : (isFixed ? 'fixed' : 'absolute'))). 338 css('left', $.datepicker._pos[0] + 'px').css('top', $.datepicker._pos[1] + 'px'); 339 $.datepicker._pos = null; 340 $.datepicker._showDatepicker(inst); 341 return this; 342 }, 343 344 /* Construct and display the date picker. */ 345 _showDatepicker: function(id) { 346 var inst = this._getInst(id); 347 inst._rangeStart = null; 348 this._updateDatepicker(inst); 349 if (!inst._inline) { 350 var speed = inst._get('speed'); 351 var postProcess = function() { 352 $.datepicker._datepickerShowing = true; 353 $.datepicker._afterShow(inst); 354 }; 355 var showAnim = inst._get('showAnim') || 'show'; 356 inst._datepickerDiv[showAnim](speed, postProcess); 357 if (speed == '') { 358 postProcess(); 359 } 360 if (inst._input[0].type != 'hidden') { 361 inst._input[0].focus(); 362 } 363 this._curInst = inst; 364 } 365 }, 366 367 /* Generate the date picker content. */ 368 _updateDatepicker: function(inst) { 369 inst._datepickerDiv.empty().append(inst._generateDatepicker()); 370 var numMonths = inst._getNumberOfMonths(); 371 if (numMonths[0] != 1 || numMonths[1] != 1) { 372 inst._datepickerDiv.addClass('datepicker_multi'); 373 } 374 else { 375 inst._datepickerDiv.removeClass('datepicker_multi'); 376 } 377 if (inst._get('isRTL')) { 378 inst._datepickerDiv.addClass('datepicker_rtl'); 379 } 380 else { 381 inst._datepickerDiv.removeClass('datepicker_rtl'); 382 } 383 if (inst._input && inst._input[0].type != 'hidden') { 384 inst._input[0].focus(); 385 } 386 }, 387 388 /* Tidy up after displaying the date picker. */ 389 _afterShow: function(inst) { 390 var numMonths = inst._getNumberOfMonths(); // fix width for dynamic number of date pickers 391 inst._datepickerDiv.width(numMonths[1] * $('.datepicker', inst._datepickerDiv[0]).width()); 392 if ($.browser.msie && parseInt($.browser.version) < 7) { // fix IE < 7 select problems 393 $('#datepicker_cover').css({width: inst._datepickerDiv.width() + 4, 394 height: inst._datepickerDiv.height() + 4}); 395 } 396 // re-position on screen if necessary 397 var isFixed = inst._datepickerDiv.css('position') == 'fixed'; 398 var pos = inst._input ? $.datepicker._findPos(inst._input[0]) : null; 399 var browserWidth = window.innerWidth || document.documentElement.clientWidth || 400 document.body.clientWidth; 401 var browserHeight = window.innerHeight || document.documentElement.clientHeight || 402 document.body.clientHeight; 403 var scrollX = (isFixed ? 0 : document.documentElement.scrollLeft || document.body.scrollLeft); 404 var scrollY = (isFixed ? 0 : document.documentElement.scrollTop || document.body.scrollTop); 405 // reposition date picker horizontally if outside the browser window 406 if ((inst._datepickerDiv.offset().left + inst._datepickerDiv.width() - 407 (isFixed && $.browser.msie ? document.documentElement.scrollLeft : 0)) > 408 (browserWidth + scrollX)) { 409 inst._datepickerDiv.css('left', Math.max(scrollX, 410 pos[0] + (inst._input ? $(inst._input[0]).width() : null) - inst._datepickerDiv.width() - 411 (isFixed && $.browser.opera ? document.documentElement.scrollLeft : 0)) + 'px'); 412 } 413 // reposition date picker vertically if outside the browser window 414 if ((inst._datepickerDiv.offset().top + inst._datepickerDiv.height() - 415 (isFixed && $.browser.msie ? document.documentElement.scrollTop : 0)) > 416 (browserHeight + scrollY) ) { 417 inst._datepickerDiv.css('top', Math.max(scrollY, 418 pos[1] - (this._inDialog ? 0 : inst._datepickerDiv.height()) - 419 (isFixed && $.browser.opera ? document.documentElement.scrollTop : 0)) + 'px'); 420 } 421 }, 422 423 /* Find an object's position on the screen. */ 424 _findPos: function(obj) { 425 while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) { 426 obj = obj.nextSibling; 427 } 428 var curleft = curtop = 0; 429 if (obj && obj.offsetParent) { 430 curleft = obj.offsetLeft; 431 curtop = obj.offsetTop; 432 while (obj = obj.offsetParent) { 433 var origcurleft = curleft; 434 curleft += obj.offsetLeft; 435 if (curleft < 0) { 436 curleft = origcurleft; 437 } 438 curtop += obj.offsetTop; 439 } 440 } 441 return [curleft,curtop]; 442 }, 443 444 /* Hide the date picker from view. 445 @param speed string - the speed at which to close the date picker 446 @return void */ 447 hideDatepicker: function(speed) { 448 var inst = this._curInst; 449 if (!inst) { 450 return; 451 } 452 var rangeSelect = inst._get('rangeSelect'); 453 if (rangeSelect && this._stayOpen) { 454 this._selectDate(inst, inst._formatDate( 455 inst._currentDay, inst._currentMonth, inst._currentYear)); 456 } 457 this._stayOpen = false;
458 if (this._datepickerShowing) { 459 speed = (speed != null ? speed : inst._get('speed')); 460 inst._datepickerDiv.hide(speed, function() { 461 $.datepicker._tidyDialog(inst); 462 }); 463 if (speed == '') { 464 this._tidyDialog(inst); 465 } 466 this._datepickerShowing = false; 467 this._lastInput = null; 468 inst._settings.prompt = null; 469 if (this._inDialog) { 470 this._dialogInput.css('position', 'absolute'). 471 css('left', '0px').css('top', '-100px'); 472 if ($.blockUI) { 473 $.unblockUI(); 474 $('body').append(this._datepickerDiv); 475 } 476 } 477 this._inDialog = false; 478 } 479 this._curInst = null; 480 }, 481 482 /* Tidy up after a dialog display. */ 483 _tidyDialog: function(inst) { 484 inst._datepickerDiv.removeClass('datepicker_dialog'); 485 $('.datepicker_prompt', inst._datepickerDiv).remove(); 486 }, 487 488 /* Close date picker if clicked elsewhere. */ 489 _checkExternalClick: function(event) { 490 if (!$.datepicker._curInst) { 491 return; 492 } 493 var target = $(event.target); 494 if ((target.parents("#datepicker_div").length == 0) && 495 (target.attr('class') != 'datepicker_trigger') && 496 $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI)) { 497 $.datepicker.hideDatepicker(''); 498 } 499 }, 500 501 /* Adjust one of the date sub-fields. */ 502 _adjustDate: function(id, offset, period) { 503 var inst = this._getInst(id); 504 inst._adjustDate(offset, period); 505 this._updateDatepicker(inst); 506 }, 507 508 /* Action for current link. */ 509 _gotoToday: function(id) { 510 var date = new Date(); 511 var inst = this._getInst(id); 512 inst._selectedDay = date.getDate(); 513 inst._selectedMonth = date.getMonth(); 514 inst._selectedYear = date.getFullYear(); 515 this._adjustDate(inst); 516 }, 517 518 /* Action for selecting a new month/year. */ 519 _selectMonthYear: function(id, select, period) { 520 var inst = this._getInst(id); 521 inst._selectingMonthYear = false; 522 inst[period == 'M' ? '_selectedMonth' : '_selectedYear'] = 523 select.options[select.selectedIndex].value - 0; 524 this._adjustDate(inst); 525 }, 526 527 /* Restore input focus after not changing month/year. */ 528 _clickMonthYear: function(id) { 529 var inst = this._getInst(id); 530 if (inst._input && inst._selectingMonthYear && !$.browser.msie) { 531 inst._input[0].focus(); 532 } 533 inst._selectingMonthYear = !inst._selectingMonthYear; 534 }, 535 536 /* Action for changing the first week day. */ 537 _changeFirstDay: function(id, day) { 538 var inst = this._getInst(id); 539 inst._settings.firstDay = day; 540 this._updateDatepicker(inst); 541 }, 542 543 /* Action for selecting a day. */ 544 _selectDay: function(id, month, year, td) { 545 if (this._hasClass($(td), 'datepicker_unselectable')) { 546 return; 547 } 548 var inst = this._getInst(id); 549 var rangeSelect = inst._get('rangeSelect'); 550 if (rangeSelect) { 551 if (!this._stayOpen) { 552 $('.datepicker td').removeClass('datepicker_currentDay'); 553 $(td).addClass('datepicker_currentDay'); 554 } 555 this._stayOpen = !this._stayOpen; 556 } 557 inst._currentDay = $('a', td).html(); 558 inst._currentMonth = month; 559 inst._currentYear = year; 560 this._selectDate(id, inst._formatDate( 561 inst._currentDay, inst._currentMonth, inst._currentYear)); 562 if (this._stayOpen) { 563 inst._endDay = inst._endMonth = inst._endYear = null; 564 inst._rangeStart = new Date(inst._currentYear, inst._currentMonth, inst._currentDay); 565 this._updateDatepicker(inst); 566 } 567 else if (rangeSelect) { 568 inst._endDay = inst._currentDay; 569 inst._endMonth = inst._currentMonth; 570 inst._endYear = inst._currentYear; 571 inst._selectedDay = inst._currentDay = inst._rangeStart.getDate(); 572 inst._selectedMonth = inst._currentMonth = inst._rangeStart.getMonth(); 573 inst._selectedYear = inst._currentYear = inst._rangeStart.getFullYear(); 574 inst._rangeStart = null; 575 if (inst._inline) { 576 this._updateDatepicker(inst); 577 } 578 } 579 }, 580 581 /* Erase the input field and hide the date picker. */ 582 _clearDate: function(id) { 583 var inst = this._getInst(id); 584 this._stayOpen = false; 585 inst._endDay = inst._endMonth = inst._endYear = inst._rangeStart = null; 586 this._selectDate(inst, ''); 587 }, 588 589 /* Update the input field with the selected date. */ 590 _selectDate: function(id, dateStr) { 591 var inst = this._getInst(id); 592 dateStr = (dateStr != null ? dateStr : inst._formatDate()); 593 if (inst._rangeStart) { 594 dateStr = inst._formatDate(inst._rangeStart) + inst._get('rangeSeparator') + dateStr; 595 } 596 if (inst._input) { 597 inst._input.val(dateStr); 598 } 599 var onSelect = inst._get('onSelect'); 600 if (onSelect) { 601 onSelect.apply((inst._input ? inst._input[0] : null), [dateStr, inst]); // trigger custom callback 602 } 603 else { 604 if (inst._input) { 605 inst._input.trigger('change'); // fire the change event 606 } 607 } 608 if (inst._inline) { 609 this._updateDatepicker(inst); 610 } 611 else { 612 if (!this._stayOpen) { 613 this.hideDatepicker(inst._get('speed')); 614 this._lastInput = inst._input[0]; 615 if (typeof(inst._input[0]) != 'object') { 616 inst._input[0].focus(); // restore focus 617 } 618 this._lastInput = null; 619 } 620 } 621 }, 622 623 /* Set as beforeShowDay function to prevent selection of weekends. 624 @param date Date - the date to customise 625 @return [boolean, string] - is this date selectable?, what is its CSS class? */ 626 noWeekends: function(date) { 627 var day = date.getDay(); 628 return [(day > 0 && day < 6), '']; 629 }, 630 631 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition. 632 @param date Date - the date to get the week for 633 @return number - the number of the week within the year that contains this date */ 634 iso8601Week: function(date) { 635 var checkDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()); 636 var firstMon = new Date(checkDate.getFullYear(), 1 - 1, 4); // First week always contains 4 Jan 637 var firstDay = firstMon.getDay() || 7; // Day of week: Mon = 1, ..., Sun = 7 638 firstMon.setDate(firstMon.getDate() + 1 - firstDay); // Preceding Monday 639 if (firstDay < 4 && checkDate < firstMon) { // Adjust first three days in year if necessary 640 checkDate.setDate(checkDate.getDate() - 3); // Generate for previous year 641 return $.datepicker.iso8601Week(checkDate); 642 } 643 else if (checkDate > new Date(checkDate.getFullYear(), 12 - 1, 28)) { // Check last three days in year 644 firstDay = new Date(checkDate.getFullYear() + 1, 1 - 1, 4).getDay() || 7; 645 if (firstDay >
645 4 && (checkDate.getDay() || 7) < firstDay - 3) { // Adjust if necessary 646 checkDate.setDate(checkDate.getDate() + 3); // Generate for next year 647 return $.datepicker.iso8601Week(checkDate); 648 } 649 } 650 return Math.floor(((checkDate - firstMon) / 86400000) / 7) + 1; // Weeks to given date 651 }, 652 653 /* Provide status text for a particular date. 654 @param date the date to get the status for 655 @param inst the current datepicker instance 656 @return the status display text for this date */ 657 dateStatus: function(date, inst) { 658 return $.datepicker.formatDate(inst._get('dateStatus'), date, inst._getFormatConfig()); 659 }, 660 661 /* Parse a string value into a date object. 662 The format can be combinations of the following: 663 d - day of month (no leading zero) 664 dd - day of month (two digit) 665 D - day name short 666 DD - day name long 667 m - month of year (no leading zero) 668 mm - month of year (two digit) 669 M - month name short 670 MM - month name long 671 y - year (two digit) 672 yy - year (four digit) 673 '...' - literal text 674 '' - single quote 675 676 @param format String - the expected format of the date 677 @param value String - the date in the above format 678 @param settings Object - attributes include: 679 shortYearCutoff Number - the cutoff year for determining the century (optional) 680 dayNamesShort String[7] - abbreviated names of the days from Sunday (optional) 681 dayNames String[7] - names of the days from Sunday (optional) 682 monthNamesShort String[12] - abbreviated names of the months (optional) 683 monthNames String[12] - names of the months (optional) 684 @return Date - the extracted date value or null if value is blank */ 685 parseDate: function (format, value, settings) { 686 if (format == null || value == null) { 687 throw 'Invalid arguments'; 688 } 689// format = dateFormats[format] || format; 690 value = (typeof value == 'object' ? value.toString() : value + ''); 691 if (value == '') { 692 return null; 693 } 694 var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff; 695 var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort; 696 var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames; 697 var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort; 698 var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames; 699 var year = -1; 700 var month = -1; 701 var day = -1; 702 var literal = false; 703 // Check whether a format character is doubled 704 var lookAhead = function(match) { 705 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match); 706 if (matches) { 707 iFormat++; 708 } 709 return matches; 710 }; 711 // Extract a number from the string value 712 var getNumber = function(match) { 713 lookAhead(match); 714 var size = (match == 'y' ? 4 : 2); 715 var num = 0; 716 while (size > 0 && iValue < value.length && 717 value.charAt(iValue) >= '0' && value.charAt(iValue) <= '9') { 718 num = num * 10 + (value.charAt(iValue++) - 0); 719 size--; 720 } 721 if (size == (match == 'y' ? 4 : 2)) { 722 throw 'Missing number at position ' + iValue; 723 } 724 return num; 725 }; 726 // Extract a name from the string value and convert to an index 727 var getName = function(match, shortNames, longNames) { 728 var names = (lookAhead(match) ? longNames : shortNames); 729 var size = 0; 730 for (var j = 0; j < names.length; j++) { 731 size = Math.max(size, names[j].length); 732 } 733 var name = ''; 734 var iInit = iValue; 735 while (size > 0 && iValue < value.length) { 736 name += value.charAt(iValue++); 737 for (var i = 0; i < names.length; i++) { 738 if (name == names[i]) { 739 return i + 1; 740 } 741 } 742 size--; 743 } 744 throw 'Unknown name at position ' + iInit; 745 }; 746 // Confirm that a literal character matches the string value 747 var checkLiteral = function() { 748 if (value.charAt(iValue) != format.charAt(iFormat)) { 749 throw 'Unexpected literal at position ' + iValue; 750 } 751 iValue++; 752 }; 753 var iValue = 0; 754 for (var iFormat = 0; iFormat < format.length; iFormat++) { 755 if (literal) { 756 if (format.charAt(iFormat) == '\'' && !lookAhead('\'')) { 757 literal = false;
758 } 759 else { 760 checkLiteral(); 761 } 762 } 763 else { 764 switch (format.charAt(iFormat)) { 765 case 'd': 766 day = getNumber('d'); 767 break; 768 case 'D': 769 getName('D', dayNamesShort, dayNames); 770 break; 771 case 'm': 772 month = getNumber('m'); 773 break; 774 case 'M': 775 month = getName('M', monthNamesShort, monthNames); 776 break; 777 case 'y': 778 year = getNumber('y'); 779 break; 780 case '\'': 781 if (lookAhead('\'')) { 782 checkLiteral(); 783 } 784 else { 785 literal = true; 786 } 787 break; 788 default: 789 checkLiteral(); 790 } 791 } 792 } 793 if (year < 100) { 794 year += new Date().getFullYear() - new Date().getFullYear() % 100 + 795 (year <= shortYearCutoff ? 0 : -100); 796 } 797 var date = new Date(year, month - 1, day); 798 if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day) { 799 throw 'Invalid date'; // E.g. 31/02/* 800 } 801 return date; 802 }, 803 804 /* Format a date object into a string value. 805 The format can be combinations of the following: 806 d - day of month (no leading zero) 807 dd - day of month (two digit) 808 D - day name short 809 DD - day name long 810 m - month of year (no leading zero) 811 mm - month of year (two digit) 812 M - month name short 813 MM - month name long 814 y - year (two digit) 815 yy - year (four digit) 816 '...' - literal text 817 '' - single quote 818 819 @param format String - the desired format of the date 820 @param date Date - the date value to format 821 @param settings Object - attributes include: 822 dayNamesShort String[7] - abbreviated names of the days from Sunday (optional) 823 dayNames String[7] - names of the days from Sunday (optional) 824 monthNamesShort String[12] - abbreviated names of the months (optional) 825 monthNames String[12] - names of the months (optional) 826 @return String - the date in the above format */ 827 formatDate: function (format, date, settings) { 828 if (!date) { 829 return ''; 830 } 831// format = dateFormats[format] || format; 832 var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort; 833 var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames; 834 var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort; 835 var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames; 836 // Check whether a format character is doubled 837 var lookAhead = function(match) { 838 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match); 839 if (matches) { 840 iFormat++; 841 } 842 return matches; 843 }; 844 // Format a number, with leading zero if necessary 845 var formatNumber = function(match, value) { 846 return (lookAhead(match) && value < 10 ? '0' : '') + value; 847 }; 848 // Format a name, short or long as requested 849 var formatName = function(match, value, shortNames, longNames) { 850 return (lookAhead(match) ? longNames[value] : shortNames[value]); 851 }; 852 var output = ''; 853 var literal = false; 854 if (date) { 855 for (var iFormat = 0; iFormat < format.length; iFormat++) { 856 if (literal) { 857 if (format.charAt(iFormat) == '\'' && !lookAhead('\'')) { 858 literal = false; 859 } 860 else { 861 output += format.charAt(iFormat); 862 } 863 } 864 else { 865 switch (format.charAt(iFormat)) { 866 case 'd': 867 output += formatNumber('d', date.getDate()); 868 break; 869 case 'D': 870 output += formatName('D', date.getDay(), dayNamesShort, dayNames); 871 break; 872 case 'm': 873 output += formatNumber('m', date.getMonth() + 1); 874 break; 875 case 'M': 876 output += formatName('M', date.getMonth(), monthNamesShort, monthNames); 877 break; 878 case 'y': 879 output += (lookAhead('y') ? date.getFullYear() : 880 (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100); 881 break; 882 case '\'': 883 if (lookAhead('\'')) { 884 output += '\''; 885 } 886 else { 887 literal = true; 888 } 889 break; 890 default: 891 output += format.charAt(iFormat); 892 } 893 } 894 } 895 } 896 return output; 897 }, 898 899 /* Extract all possible characters from the date format. */ 900 _possibleChars: function (format) { 901// format = dateFormats[format] || format; 902 var chars = ''; 903 var literal = false;
904 for (var iFormat = 0; iFormat < format.length; iFormat++) { 905 if (literal) { 906 if (format.charAt(iFormat) == '\'' && !lookAhead('\'')) { 907 literal = false; 908 } 909 else { 910 chars += format.charAt(iFormat); 911 } 912 } 913 else { 914 switch (format.charAt(iFormat)) { 915 case 'd': 916 case 'm': 917 case 'y': 918 chars += '0123456789'; 919 break; 920 case 'D': 921 case 'M': 922 return null; // Accept anything 923 case '\'': 924 if (lookAhead('\'')) { 925 chars += '\''; 926 } 927 else { 928 literal = true; 929 } 930 break; 931 default: 932 chars += format.charAt(iFormat); 933 } 934 } 935 } 936 return chars; 937 } 938}); 939 940/* Individualised settings for date picker functionality applied to one or more related inputs. 941 Instances are managed and manipulated through the Datepicker manager. */ 942function DatepickerInstance(settings, inline) { 943 this._id = $.datepicker._register(this); 944 this._selectedDay = 0; 945 this._selectedMonth = 0; // 0-11 946 this._selectedYear = 0; // 4-digit year 947 this._input = null; // The attached input field 948 this._inline = inline; // True if showing inline, false if used in a popup 949 this._datepickerDiv = (!inline ? $.datepicker._datepickerDiv : 950 $('<div id="datepicker_div_' + this._id + '" class="datepicker_inline"></div>')); 951 // customise the date picker object - uses manager defaults if not overridden 952 this._settings = extendRemove({}, settings || {}); // clone 953 if (inline) { 954 this._setDate(this._getDefaultDate()); 955 } 956} 957 958$.extend(DatepickerInstance.prototype, { 959 /* Get a setting value, defaulting if necessary. */ 960 _get: function(name) { 961 return (this._settings[name] != null ? this._settings[name] : $.datepicker._defaults[name]); 962 }, 963 964 /* Parse existing date and initialise date picker. */ 965 _setDateFromField: function(input) { 966 this._input = $(input); 967 var dateFormat = this._get('dateFormat'); 968 var dates = this._input ? this._input.val().split(this._get('rangeSeparator')) : null; 969 this._endDay = this._endMonth = this._endYear = null; 970 var date = defaultDate = this._getDefaultDate(); 971 if (dates.length > 0) { 972 var settings = this._getFormatConfig(); 973 if (dates.length > 1) { 974 date = $.datepicker.parseDate(dateFormat, dates[1], settings) || defaultDate; 975 this._endDay = date.getDate(); 976 this._endMonth = date.getMonth(); 977 this._endYear = date.getFullYear(); 978 } 979 try { 980 date = $.datepicker.parseDate(dateFormat, dates[0], settings) || defaultDate; 981 } 982 catch (e) { 983 $.datepicker.log(e); 984 date = defaultDate; 985 } 986 } 987 this._selectedDay = this._currentDay = date.getDate(); 988 this._selectedMonth = this._currentMonth = date.getMonth(); 989 this._selectedYear = this._currentYear = date.getFullYear(); 990 this._adjustDate(); 991 }, 992 993 /* Retrieve the default date shown on opening. */ 994 _getDefaultDate: function() { 995 return this._determineDate('defaultDate', new Date()); 996 }, 997 998 /* A date may be specified as an exact value or a relative one. */ 999 _determineDate: function(name, defaultDate) { 1000 var offsetNumeric = function(offset) { 1001 var date = new Date(); 1002 date.setDate(date.getDate() + offset); 1003 return date; 1004 }; 1005 var offsetString = function(offset, getDaysInMonth) { 1006 var date = new Date(); 1007 var matches = /^([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?$/.exec(offset); 1008 if (matches) { 1009 var year = date.getFullYear(); 1010 var month = date.getMonth(); 1011 var day = date.getDate(); 1012 switch (matches[2] || 'd') { 1013 case 'd' : case 'D' : 1014 day += (matches[1] - 0); break; 1015 case 'w' : case 'W' : 1016 day += (matches[1] * 7); break; 1017 case 'm' : case 'M' : 1018 month += (matches[1] - 0); 1019 day = Math.min(day, getDaysInMonth(year, month)); 1020 break; 1021 case 'y': case 'Y' : 1022 year += (matches[1] - 0); 1023 day = Math.min(day, getDaysInMonth(year, month)); 1024 break; 1025 } 1026 date = new Date(year, month, day); 1027 } 1028 return date; 1029 }; 1030 var date = this._get(name); 1031 return (date == null ? defaultDate : 1032 (typeof date == 'string' ? offsetString(date, this._getDaysInMonth) : 1033 (typeof date == 'number' ? offsetNumeric(date) : date))); 1034 }, 1035 1036 /* Set the date(s) directly. */ 1037 _setDate: function(date, endDate) { 1038 this._selectedDay = this._currentDay = date.getDate(); 1039 this._selectedMonth = this._currentMonth = date.getMonth(); 1040 this._selectedYear = this._currentYear = date.getFullYear(); 1041 if (this._get('rangeSelect')) { 1042 if (endDate) { 1043 this._endDay = endDate.getDate(); 1044 this._endMonth = endDate.getMonth(); 1045 this._endYear = endDate.getFullYear(); 1046 } 1047 else { 1048 this._endDay = this._currentDay; 1049 this._endMonth = this._currentMonth; 1050 this._endYear = this._currentYear; 1051 } 1052 } 1053 this._adjustDate(); 1054 }, 1055 1056 /* Retrieve the date(s) directly. */ 1057 _getDate: function() { 1058 var startDate = (!this._currentYear || (this._input && this._input.val() == '') ? null : 1059 new Date(this._currentYear, this._currentMonth, this._currentDay)); 1060 if (this._get('rangeSelect')) { 1061 return [startDate, (!this._endYear ? null : 1062 new Date(this._endYear, this._endMonth, this._endDay))]; 1063 } 1064 else { 1065 return startDate; 1066 } 1067 }, 1068 1069 /* Generate the HTML for the current state of the date picker. */ 1070 _generateDatepicker: function() { 1071 var today = new Date(); 1072 today = new Date(today.getFullYear(), today.getMonth(), today.getDate()); // clear time 1073 var showStatus = this._get('showStatus'); 1074 var isRTL = this._get('isRTL'); 1075 // build the date picker HTML 1076 var clear = (this._get('mandatory') ? '' : 1077 '<div class="datepicker_clear">
1077<a onclick="jQuery.datepicker._clearDate(' + this._id + ');"' + 1078 (showStatus ? this._addStatus(this._get('clearStatus') || ' ') : '') + '>' + 1079 this._get('clearText') + '</a></div>'); 1080 var controls = '<div class="datepicker_control">' + (isRTL ? '' : clear) + 1081 '<div class="datepicker_close"><a onclick="jQuery.datepicker.hideDatepicker();"' + 1082 (showStatus ? this._addStatus(this._get('closeStatus') || ' ') : '') + '>' + 1083 this._get('closeText') + '</a></div>' + (isRTL ? clear : '') + '</div>'; 1084 var prompt = this._get('prompt'); 1085 var closeAtTop = this._get('closeAtTop'); 1086 var hideIfNoPrevNext = this._get('hideIfNoPrevNext'); 1087 var numMonths = this._getNumberOfMonths(); 1088 var stepMonths = this._get('stepMonths'); 1089 var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1); 1090 var minDate = this._getMinMaxDate('min', true); 1091 var maxDate = this._getMinMaxDate('max'); 1092 var drawMonth = this._selectedMonth; 1093 var drawYear = this._selectedYear; 1094 if (maxDate) { 1095 var maxDraw = new Date(maxDate.getFullYear(), 1096 maxDate.getMonth() - numMonths[1] + 1, maxDate.getDate()); 1097 maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw); 1098 while (new Date(drawYear, drawMonth, 1) > maxDraw) { 1099 drawMonth--; 1100 if (drawMonth < 0) { 1101 drawMonth = 11; 1102 drawYear--; 1103 } 1104 } 1105 } 1106 // controls and links 1107 var prev = '<div class="datepicker_prev">' + (this._canAdjustMonth(-1, drawYear, drawMonth) ? 1108 '<a onclick="jQuery.datepicker._adjustDate(' + this._id + ', -' + stepMonths + ', \'M\');"' + 1109 (showStatus ? this._addStatus(this._get('prevStatus') || ' ') : '') + '>' + 1110 this._get('prevText') + '</a>' : 1111 (hideIfNoPrevNext ? '' : '<label>' + this._get('prevText') + '</label>')) + '</div>'; 1112 var next = '<div class="datepicker_next">' + (this._canAdjustMonth(+1, drawYear, drawMonth) ? 1113 '<a onclick="jQuery.datepicker._adjustDate(' + this._id + ', +' + stepMonths + ', \'M\');"' + 1114 (showStatus ? this._addStatus(this._get('nextStatus') || ' ') : '') + '>' + 1115 this._get('nextText') + '</a>' : 1116 (hideIfNoPrevNext ? '>' : '<label>' + this._get('nextText') + '</label>')) + '</div>'; 1117 var html = (prompt ? '<div class="datepicker_prompt">' + prompt + '</div>' : '') + 1118 (closeAtTop && !this._inline ? controls : '') + 1119 '<div class="datepicker_links">' + (isRTL ? next : prev) + 1120 (this._isInRange(today) ? '<div class="datepicker_current">' + 1121 '<a onclick="jQuery.datepicker._gotoToday(' + this._id + ');"' + 1122 (showStatus ? this._addStatus(this._get('currentStatus') || ' ') : '') + '>' + 1123 this._get('currentText') + '</a></div>' : '') + (isRTL ? prev : next) + '</div>'; 1124 var showWeeks = this._get('showWeeks'); 1125 for (var row = 0; row < numMonths[0]; row++) { 1126 for (var col = 0; col < numMonths[1]; col++) { 1127 var selectedDate = new Date(drawYear, drawMonth, this._selectedDay); 1128 html += '<div class="datepicker_oneMonth' + (col == 0 ? ' datepicker_newRow' : '') + '">' + 1129 this._generateMonthYearHeader(drawMonth, drawYear, minDate, maxDate, 1130 selectedDate, row > 0 || col > 0) + // draw month headers 1131 '<table class="datepicker" cellpadding="0" cellspacing="0"><thead>' + 1132 '<tr class="datepicker_titleRow">' + 1133 (showWeeks ? '<td>' + this._get('weekHeader') + '</td>' : ''); 1134 var firstDay = this._get('firstDay'); 1135 var changeFirstDay = this._get('changeFirstDay'); 1136 var dayNames = this._get('dayNames'); 1137 var dayNamesShort = this._get('dayNamesShort'); 1138 var dayNamesMin = this._get('dayNamesMin'); 1139 for (var dow = 0; dow < 7; dow++) { // days of the week 1140 var day = (dow + firstDay) % 7; 1141 var status = this._get('dayStatus') || ' '; 1142 status = (status.indexOf('DD') > -1 ? status.replace(/DD/, dayNames[day]) : 1143 status.replace(/D/, dayNamesShort[day])); 1144 html += '<td' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="datepicker_weekEndCell"' : '') + '>' + 1145 (!changeFirstDay ? '<span' : 1146 '<a onclick="jQuery.datepicker._changeFirstDay(' + this._id + ', ' + day + ');"') + 1147 (showStatus ? this._addStatus(status) : '') + ' title="' + dayNames[day] + '">' + 1148 dayNamesMin[day] + (changeFirstDay ? '</a>' : '</span>') + '</td>'; 1149 } 1150 html += '</tr></thead><tbody>'; 1151 var daysInMonth = this._getDaysInMonth(drawYear, drawMonth); 1152 if (drawYear == this._selectedYear && drawMonth == this._selectedMonth) { 1153 this._selectedDay = Math.min(this._selectedDay, daysInMonth); 1154 } 1155 var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7; 1156 var currentDate = new Date(this._currentYear, this._currentMonth, this._currentDay); 1157 var endDate = this._endDay ? new Date(this._endYear, this._endMonth, this._endDay) : currentDate; 1158 var printDate = new Date(drawYear, drawMonth, 1 - leadDays); 1159 var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // calculate the number of rows to generate 1160 var beforeShowDay = this._get('beforeShowDay'); 1161 var showOtherMonths = this._get('showOtherMonths'); 1162 var calculateWeek = this._get('calculateWeek') || $.datepicker.iso8601Week; 1163 var dateStatus = this._get('statusForDate') || $.datepicker.dateStatus; 1164 for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows 1165 html += '<tr class="datepicker_daysRow">' + 1166 (showWeeks ? '<td class="datepicker_weekCol">' + calculateWeek(printDate) + '</td>' : ''); 1167 for (var dow = 0; dow < 7; dow++) { // create date picker days 1168 var daySettings = (beforeShowDay ? 1169 beforeShowDay.apply((this._input ? this._input[0] : null), [printDate]) : [true, '']); 1170 var otherMonth = (printDate.getMonth() != drawMonth); 1171 var unselectable = otherMonth || !daySettings[0] || 1172 (minDate && printDate < minDate) || (maxDate && printDate > maxDate); 1173 html += '<td class="datepicker_daysCell' + 1174 ((dow + firstDay + 6) % 7 >= 5 ? ' datepicker_weekEndCell' : '') + // highlight weekends 1175 (otherMonth ? ' datepicker_otherMonth' : '') + // highlight days from other months 1176 (printDate.getTime() == selectedDate.getTime() && drawMonth == this._selectedMonth ? 1177 ' datepicker_daysCellOver' : '') + // highlight selected day 1178 (unselectable ? ' datepicker_unselectable' : '') + // highlight unselectable days 1179 (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates 1180 (printDate.getTime() >= currentDate.getTime() && printDate.getTime() <= endDate.getTime() ? // in current range 1181 ' datepicker_currentDay' : // highlight selected day 1182 (printDate.getTime() == today.getTime() ? ' datepicker_today' : ''))) + '"' + // highlight today (if different) 1183 (unselectable ? '' : ' onmouseover="jQuery(this).addClass(\'datepicker_daysCellOver\');' + 1184 (!showStatus || (otherMonth && !showOtherMonths) ? '' : 'jQuery(\'#datepicker_status_' +
1185 this._id + '\').html(\'' + (dateStatus.apply((this._input ? this._input[0] : null), 1186 [printDate, this]) || ' ') +'\');') + '"' + 1187 ' onmouseout="jQuery(this).removeClass(\'datepicker_daysCellOver\');' + 1188 (!showStatus || (otherMonth && !showOtherMonths) ? '' : 'jQuery(\'#datepicker_status_' + 1189 this._id + '\').html(\' \');') + '" onclick="jQuery.datepicker._selectDay(' + 1190 this._id + ',' + drawMonth + ',' + drawYear + ', this);"') + '>' + // actions 1191 (otherMonth ? (showOtherMonths ? printDate.getDate() : ' ') : // display for other months 1192 (unselectable ? printDate.getDate() : '<a>' + printDate.getDate() + '</a>')) + '</td>'; // display for this month 1193 printDate.setDate(printDate.getDate() + 1); 1194 } 1195 html += '</tr>'; 1196 } 1197 drawMonth++; 1198 if (drawMonth > 11) { 1199 drawMonth = 0; 1200 drawYear++; 1201 } 1202 html += '</tbody></table></div>'; 1203 } 1204 } 1205 html += (showStatus ? '<div id="datepicker_status_' + this._id + 1206 '" class="datepicker_status">' + (this._get('initStatus') || ' ') + '</div>' : '') + 1207 (!closeAtTop && !this._inline ? controls : '') + 1208 '<div style="clear: both;"></div>' + 1209 ($.browser.msie && parseInt($.browser.version) < 7 && !this._inline ? 1210 '<iframe src="javascript:false;" class="datepicker_cover"></iframe>' : ''); 1211 return html; 1212 }, 1213 1214 /* Generate the month and year header. */ 1215 _generateMonthYearHeader: function(drawMonth, drawYear, minDate, maxDate, selectedDate, secondary) { 1216 minDate = (this._rangeStart && minDate && selectedDate < minDate ? selectedDate : minDate); 1217 var showStatus = this._get('showStatus'); 1218 var html = '<div class="datepicker_header">'; 1219 // month selection 1220 var monthNames = this._get('monthNames'); 1221 if (secondary || !this._get('changeMonth')) { 1222 html += monthNames[drawMonth] + ' '; 1223 } 1224 else { 1225 var inMinYear = (minDate && minDate.getFullYear() == drawYear); 1226 var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear); 1227 html += '<select class="datepicker_newMonth" ' + 1228 'onchange="jQuery.datepicker._selectMonthYear(' + this._id + ', this, \'M\');" ' + 1229 'onclick="jQuery.datepicker._clickMonthYear(' + this._id + ');"' + 1230 (showStatus ? this._addStatus(this._get('monthStatus') || ' ') : '') + '>'; 1231 for (var month = 0; month < 12; month++) { 1232 if ((!inMinYear || month >= minDate.getMonth()) && 1233 (!inMaxYear || month <= maxDate.getMonth())) { 1234 html += '<option value="' + month + '"' + 1235 (month == drawMonth ? ' selected="selected"' : '') + 1236 '>' + monthNames[month] + '</option>'; 1237 } 1238 } 1239 html += '</select>'; 1240 } 1241 // year selection 1242 if (secondary || !this._get('changeYear')) { 1243 html += drawYear; 1244 } 1245 else { 1246 // determine range of years to display 1247 var years = this._get('yearRange').split(':'); 1248 var year = 0; 1249 var endYear = 0; 1250 if (years.length != 2) { 1251 year = drawYear - 10; 1252 endYear = drawYear + 10; 1253 } 1254 else if (years[0].charAt(0) == '+' || years[0].charAt(0) == '-') { 1255 year = drawYear + parseInt(years[0], 10); 1256 endYear = drawYear + parseInt(years[1], 10); 1257 } 1258 else { 1259 year = parseInt(years[0], 10); 1260 endYear = parseInt(years[1], 10); 1261 } 1262 year = (minDate ? Math.max(year, minDate.getFullYear()) : year); 1263 endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear); 1264 html += '<select class="datepicker_newYear" ' + 1265 'onchange="jQuery.datepicker._selectMonthYear(' + this._id + ', this, \'Y\');" ' + 1266 'onclick="jQuery.datepicker._clickMonthYear(' + this._id + ');"' + 1267 (showStatus ? this._addStatus(this._get('yearStatus') || ' ') : '') + '>'; 1268 for (; year <= endYear; year++) { 1269 html += '<option value="' + year + '"' + 1270 (year == drawYear ? ' selected="selected"' : '') + 1271 '>' + year + '</option>'; 1272 } 1273 html += '</select>'; 1274 } 1275 html += '</div>'; // Close datepicker_header 1276 return html; 1277 }, 1278 1279 /* Provide code to set and clear the status panel. */ 1280 _addStatus: function(text) { 1281 return ' onmouseover="jQuery(\'#datepicker_status_' + this._id + '\').html(\'' + text + '\');" ' + 1282 'onmouseout="jQuery(\'#datepicker_status_' + this._id + '\').html(\' \');"'; 1283 }, 1284 1285 /* Adjust one of the date sub-fields. */ 1286 _adjustDate: function(offset, period) { 1287 var year = this._selectedYear + (period == 'Y' ? offset : 0);
1288 var month = this._selectedMonth + (period == 'M' ? offset : 0); 1289 var day = Math.min(this._selectedDay, this._getDaysInMonth(year, month)) + 1290 (period == 'D' ? offset : 0); 1291 var date = new Date(year, month, day); 1292 // ensure it is within the bounds set 1293 var minDate = this._getMinMaxDate('min', true); 1294 var maxDate = this._getMinMaxDate('max'); 1295 date = (minDate && date < minDate ? minDate : date); 1296 date = (maxDate && date > maxDate ? maxDate : date); 1297 this._selectedDay = date.getDate(); 1298 this._selectedMonth = date.getMonth(); 1299 this._selectedYear = date.getFullYear(); 1300 }, 1301 1302 /* Determine the number of months to show. */ 1303 _getNumberOfMonths: function() { 1304 var numMonths = this._get('numberOfMonths'); 1305 return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths)); 1306 }, 1307 1308 /* Determine the current maximum date - ensure no time components are set - may be overridden for a range. */ 1309 _getMinMaxDate: function(minMax, checkRange) { 1310 var date = this._determineDate(minMax + 'Date', null); 1311 if (date) { 1312 date.setHours(0); 1313 date.setMinutes(0); 1314 date.setSeconds(0); 1315 date.setMilliseconds(0); 1316 } 1317 return date || (checkRange ? this._rangeStart : null); 1318 }, 1319 1320 /* Find the number of days in a given month. */ 1321 _getDaysInMonth: function(year, month) { 1322 return 32 - new Date(year, month, 32).getDate(); 1323 }, 1324 1325 /* Find the day of the week of the first of a month. */ 1326 _getFirstDayOfMonth: function(year, month) { 1327 return new Date(year, month, 1).getDay(); 1328 }, 1329 1330 /* Determines if we should allow a "next/prev" month display change. */ 1331 _canAdjustMonth: function(offset, curYear, curMonth) { 1332 var numMonths = this._getNumberOfMonths(); 1333 var date = new Date(curYear, curMonth + (offset < 0 ? offset : numMonths[1]), 1); 1334 if (offset < 0) { 1335 date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth())); 1336 } 1337 return this._isInRange(date); 1338 }, 1339 1340 /* Is the given date in the accepted range? */ 1341 _isInRange: function(date) { 1342 // during range selection, use minimum of selected date and range start 1343 var newMinDate = (!this._rangeStart ? null : 1344 new Date(this._selectedYear, this._selectedMonth, this._selectedDay)); 1345 newMinDate = (newMinDate && this._rangeStart < newMinDate ? this._rangeStart : newMinDate); 1346 var minDate = newMinDate || this._getMinMaxDate('min'); 1347 var maxDate = this._getMinMaxDate('max'); 1348 return ((!minDate || date >= minDate) && (!maxDate || date <= maxDate)); 1349 }, 1350 1351 /* Provide the configuration settings for formatting/parsing. */ 1352 _getFormatConfig: function() { 1353 var shortYearCutoff = this._get('shortYearCutoff'); 1354 shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff : 1355 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10)); 1356 return {shortYearCutoff: shortYearCutoff, 1357 dayNamesShort: this._get('dayNamesShort'), dayNames: this._get('dayNames'), 1358 monthNamesShort: this._get('monthNamesShort'), monthNames: this._get('monthNames')}; 1359 }, 1360 1361 /* Format the given date for display. */ 1362 _formatDate: function(day, month, year) { 1363 if (!day) { 1364 this._currentDay = this._selectedDay; 1365 this._currentMonth = this._selectedMonth; 1366 this._currentYear = this._selectedYear; 1367 } 1368 var date = (day ? (typeof day == 'object' ? day : new Date(year, month, day)) : 1369 new Date(this._currentYear, this._currentMonth, this._currentDay)); 1370 return $.datepicker.formatDate(this._get('dateFormat'), date, this._getFormatConfig()); 1371 } 1372}); 1373 1374/* jQuery extend now ignores nulls! */ 1375function extendRemove(target, props) { 1376 $.extend(target, props); 1377 for (var name in props) { 1378 if (props[name] == null) { 1379 target[name] = null; 1380 } 1381 } 1382 return target; 1383}; 1384 1385/* Attach the date picker to a jQuery selection. 1386 @param settings object - the new settings to use for this date picker instance (anonymous) 1387 @return jQuery object - for chaining further calls */ 1388$.fn.attachDatepicker = function(settings) { 1389 return this.each(function() { 1390 // check for settings on the control itself - in namespace 'date:' 1391 var inlineSettings = null; 1392 for (attrName in $.datepicker._defaults) { 1393 var attrValue = this.getAttribute('date:' + attrName); 1394 if (attrValue) { 1395 inlineSettings = inlineSettings || {};
1396 try { 1397 inlineSettings[attrName] = eval(attrValue); 1398 } 1399 catch (err) { 1400 inlineSettings[attrName] = attrValue; 1401 } 1402 } 1403 } 1404 var nodeName = this.nodeName.toLowerCase(); 1405 if (nodeName == 'input') { 1406 var instSettings = (inlineSettings ? $.extend($.extend({}, settings || {}), 1407 inlineSettings || {}) : settings); // clone and customise 1408 var inst = (inst && !inlineSettings ? inst : 1409 new DatepickerInstance(instSettings, false)); 1410 $.datepicker._connectDatepicker(this, inst); 1411 } 1412 else if (nodeName == 'div' || nodeName == 'span') { 1413 var instSettings = $.extend($.extend({}, settings || {}), 1414 inlineSettings || {}); // clone and customise 1415 var inst = new DatepickerInstance(instSettings, true); 1416 $.datepicker._inlineDatepicker(this, inst); 1417 } 1418 }); 1419}; 1420 1421/* Detach a datepicker from its control. 1422 @return jQuery object - for chaining further calls */ 1423$.fn.removeDatepicker = function() { 1424 var jq = this.each(function() { 1425 var $this = $(this); 1426 var nodeName = this.nodeName.toLowerCase(); 1427 var calId = this._calId; 1428 this._calId = null; 1429 if (nodeName == 'input') { 1430 $this.siblings('.datepicker_append').replaceWith(''); 1431 $this.siblings('.datepicker_trigger').replaceWith(''); 1432 $this.removeClass($.datepicker.markerClassName). 1433 unbind('focus', $.datepicker.showFor). 1434 unbind('keydown', $.datepicker._doKeyDown). 1435 unbind('keypress', $.datepicker._doKeyPress); 1436 var wrapper = $this.parents('.datepicker_wrap'); 1437 if (wrapper) { 1438 wrapper.replaceWith(wrapper.html()); 1439 } 1440 } 1441 else if (nodeName == 'div' || nodeName == 'span') { 1442 $this.removeClass($.datepicker.markerClassName).empty(); 1443 } 1444 if ($('input[_calId=' + calId + ']').length == 0) { 1445 // clean up if last for this ID 1446 $.datepicker._inst[calId] = null; 1447 } 1448 }); 1449 if ($('input.hasDatepicker').length == 0) { 1450 // clean up if last input 1451 $.datepicker._datepickerDiv.replaceWith(''); 1452 } 1453 return jq; 1454}; 1455 1456/* Enable the date picker to a jQuery selection. 1457 @return jQuery object - for chaining further calls */ 1458$.fn.enableDatepicker = function() { 1459 return this.each(function() { 1460 this.disabled = false; 1461 $(this).siblings('button.datepicker_trigger').each(function() { this.disabled = false; }); 1462 $(this).siblings('img.datepicker_trigger').css({opacity: '1.0', cursor: ''}); 1463 var $this = this; 1464 $.datepicker._disabledInputs = $.map($.datepicker._disabledInputs, 1465 function(value) { return (value == $this ? null : value); }); // delete entry 1466 }); 1467}; 1468 1469/* Disable the date picker to a jQuery selection. 1470 @return jQuery object - for chaining further calls */ 1471$.fn.disableDatepicker = function() { 1472 return this.each(function() { 1473 this.disabled = true; 1474 $(this).siblings('button.datepicker_trigger').each(function() { this.disabled = true; }); 1475 $(this).siblings('img.datepicker_trigger').css({opacity: '0.5', cursor: 'default'}); 1476 var $this = this; 1477 $.datepicker._disabledInputs = $.map($.datepicker._disabledInputs, 1478 function(value) { return (value == $this ? null : value); }); // delete entry 1479 $.datepicker._disabledInputs[$.datepicker._disabledInputs.length] = this; 1480 }); 1481}; 1482 1483/* Is the first field in a jQuery collection disabled as a datepicker? 1484 @return boolean - true if disabled, false if enabled */ 1485$.fn.isDisabledDatepicker = function() { 1486 if (this.length == 0) { 1487 return false; 1488 } 1489 for (var i = 0; i < $.datepicker._disabledInputs.length; i++) { 1490 if ($.datepicker._disabledInputs[i] == this[0]) { 1491 return true; 1492 } 1493 } 1494 return false; 1495}; 1496 1497/* Update the settings for a date picker attached to an input field or division. 1498 @param name string - the name of the setting to change 1499 object - the new settings to update 1500 @param value any - the new value for the setting (omit if above is a map) 1501 @return jQuery object - for chaining further calls */ 1502$.fn.changeDatepicker = function(name, value) { 1503 var settings = name || {}; 1504 if (typeof name == 'string') { 1505 settings = {}; 1506 settings[name] = value; 1507 } 1508 return this.each(function() { 1509 var inst = $.datepicker._getInst(this._calId); 1510 if (inst) { 1511 extendRemove(inst._settings, settings); 1512 $.datepicker._updateDatepicker(inst); 1513 } 1514 }); 1515}; 1516 1517/* Show the date picker attached to the first entry in a jQuery selection. 1518 @return jQuery object - for chaining further calls */ 1519$.fn.showDatepicker = function() { 1520 $.datepicker.showFor(this); 1521 return this; 1522}; 1523 1524/* Set the dates for a jQuery selection. 1525 @param date Date - the new date 1526 @param endDate Date - the new end date for a range (optional) 1527 @return jQuery object - for chaining further calls */ 1528$.fn.setDatepickerDate = function(date, endDate) { 1529 return this.each(function() { 1530 var inst = $.datepicker._getInst(this._calId); 1531 if (inst) { 1532 inst._setDate(date, endDate); 1533 $.datepicker._updateDatepicker(inst); 1534 } 1535 }); 1536}; 1537 1538/* Get the date(s) for the first entry in a jQuery selection. 1539 @return Date - the current date or 1540 Date[2] - the current dates for a range*/ 1541$.fn.getDatepickerDate = function() { 1542 var inst = (this.length > 0 ? $.datepicker._getInst(this[0]._calId) : null); 1543 return (inst ? inst._getDate() : null); 1544}; 1545 1546/* Initialise the date picker. */ 1547$(document).ready(function() { 1548 $.datepicker = new Datepicker(); // singleton instance 1549 $(document.body).append($.datepicker._datepickerDiv). 1550 mousedown($.datepicker._checkExternalClick); 1551}); 1552 1553})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.