1/** 2 * Copyright (c) 2008 Kelvin Luck (http://www.kelvinluck.com/) 3 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 4 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. 5 * . 6 * $Id$ 7 **/ 8 9(function ($) { 10 11 $.fn.extend({ 12 /** 13 * Render a calendar table into any matched elements. 14 * 15 * @param Object s (optional) Customize your calendars. 16 * @option Number month The month to render (NOTE that months are zero based). Default is today's month. 17 * @option Number year The year to render. Default is today's year. 18 * @option Function renderCallback A reference to a function that is called as each cell is rendered and which can add classes and event listeners to the created nodes. Default is no callback. 19 * @option Number showHeader Whether or not to show the header row, possible values are: $.dpConst.SHOW_HEADER_NONE (no header), $.dpConst.SHOW_HEADER_SHORT (first letter of each day) and $.dpConst.SHOW_HEADER_LONG (full name of each day). Default is $.dpConst.SHOW_HEADER_SHORT. 20 * @option String hoverClass The class to attach to each cell when you hover over it (to allow you to use hover effects in IE6 which doesn't support the :hover pseudo-class on elements other than links). Default is dp-hover. Pass false if you don't want a hover class. 21 * @type jQuery 22 * @name renderCalendar 23 * @cat plugins/datePicker 24 * @author Kelvin Luck (http://www.kelvinluck.com/) 25 * 26 * @example $('#calendar-me').renderCalendar({month:0, year:2007}); 27 * @desc Renders a calendar displaying January 2007 into the element with an id of calendar-me. 28 * 29 * @example 30 * var testCallback = function($td, thisDate, month, year) 31 * { 32 * if ($td.is('.current-month') && thisDate.getDay() == 4) { 33 * var d = thisDate.getDate(); 34 * $td.on( 35 * 'click', 36 * function() 37 * { 38 * alert('You clicked on ' + d + '/' + (Number(month)+1) + '/' + year); 39 * } 40 * ).addClass('thursday'); 41 * } else if (thisDate.getDay() == 5) { 42 * $td.html('Friday the ' + $td.html() + 'th'); 43 * } 44 * } 45 * $('#calendar-me').renderCalendar({month:0, year:2007, renderCallback:testCallback}); 46 * 47 * @desc Renders a calendar displaying January 2007 into the element with an id of calendar-me. Every Thursday in the current month has a class of "thursday" applied to it, is clickable and shows an alert when clicked. Every Friday on the calendar has the number inside replaced with text. 48 **/ 49 renderCalendar: function (s) { 50 var dc = function (a) { 51 return document.createElement(a); 52 }; 53 54 s = $.extend({}, $.fn.datePicker.defaults, s); 55 56 if (s.showHeader != $.dpConst.SHOW_HEADER_NONE) { 57 var headRow = $(dc('tr')); 58 for (var i = Date.firstDayOfWeek; i < Date.firstDayOfWeek + 7; i++) { 59 var weekday = i % 7; 60 var day = Date.dayNames[weekday]; 61 var dayShort = Date.abbrDayNames[weekday]; 62 headRow.append( 63 jQuery(dc('th')).attr({ 'scope': 'col', 'abbr': day, 'title': day, 'class': (weekday == 0 || weekday == 6 ? 'weekend' : 'weekday') }).html(s.showHeader == $.dpConst.SHOW_HEADER_SHORT ? dayShort : day) 64 ); 65 } 66 }; 67 68 var calendarTable = $(dc('table')) 69 .attr( 70 { 71 'cellspacing': 2 72 } 73 ) 74 .addClass('jCalendar') 75 .append( 76 (s.showHeader != $.dpConst.SHOW_HEADER_NONE ? 77 $(dc('thead')) 78 .append(headRow) 79 : 80 dc('thead') 81 ) 82 ); 83 var tbody = $(dc('tbody')); 84 85 var today = (new Date()).zeroTime(); 86 today.setHours(12); 87
88 var month = s.month == undefined ? today.getMonth() : s.month; 89 var year = s.year || today.getFullYear(); 90 91 var currentDate = (new Date(year, month, 1, 12, 0, 0)); 92 93 var firstDayOffset = Date.firstDayOfWeek - currentDate.getDay() + 1; 94 if (firstDayOffset > 1) firstDayOffset -= 7; 95 var weeksToDraw = Math.ceil(((-1 * firstDayOffset + 1) + currentDate.getDaysInMonth()) / 7); 96 currentDate.addDays(firstDayOffset - 1); 97 98 var doHover = function (firstDayInBounds) { 99 return function () { 100 if (s.hoverClass) { 101 var $this = $(this); 102 if (!s.selectWeek) { 103 $this.addClass(s.hoverClass); 104 } else if (firstDayInBounds && !$this.is('.disabled')) { 105 $this.parent().addClass('activeWeekHover'); 106 } 107 } 108 } 109 }; 110 var unHover = function () { 111 if (s.hoverClass) { 112 var $this = $(this); 113 $this.removeClass(s.hoverClass); 114 $this.parent().removeClass('activeWeekHover'); 115 } 116 }; 117 118 var w = 0; 119 while (w++ < weeksToDraw) { 120 var r = jQuery(dc('tr')); 121 var firstDayInBounds = s.dpController ? currentDate > s.dpController.startDate : false; 122 for (var i = 0; i < 7; i++) { 123 var thisMonth = currentDate.getMonth() == month; 124 var d = $(dc('td')) 125 .text(currentDate.getDate() + '') 126 .addClass((thisMonth ? 'current-month ' : 'other-month ') + 127 (currentDate.isWeekend() ? 'weekend ' : 'weekday ') + 128 (thisMonth && currentDate.getTime() == today.getTime() ? 'today ' : '') 129 ) 130 .data('datePickerDate', currentDate.asString()) 131 .hover(doHover(firstDayInBounds), unHover) 132 ; 133 r.append(d); 134 if (s.renderCallback) { 135 s.renderCallback(d, currentDate, month, year); 136 } 137 // addDays(1) fails in some locales due to daylight savings. See issue 39. 138 //currentDate.addDays(1); 139 // set the time to midday to avoid any weird timezone issues?? 140 currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() + 1, 12, 0, 0); 141 } 142 tbody.append(r); 143 } 144 calendarTable.append(tbody); 145 146 return this.each( 147 function () { 148 $(this).empty().append(calendarTable); 149 } 150 ); 151 }, 152 /** 153 * Create a datePicker associated with each of the matched elements. 154 * 155 * The matched element will receive a few custom events with the following signatures: 156 * 157 * dateSelected(event, date, $td, status) 158 * Triggered when a date is selected. event is a reference to the event, date is the Date selected, $td is a jquery object wrapped around the TD that was clicked on and status is whether the date was selected (true) or deselected (false) 159 * 160 * dpClosed(event, selected) 161 * Triggered when the date picker is closed. event is a reference to the event and selected is an Array containing Date objects. 162 * 163 * dpMonthChanged(event, displayedMonth, displayedYear) 164 * Triggered when the month of the popped up calendar is changed. event is a reference to the event, displayedMonth is the number of the month now displayed (zero based) and displayedYear is the year of the month. 165 * 166 * dpDisplayed(event, $datePickerDiv) 167 * Triggered when the date picker is created. $datePickerDiv is the div containing the date picker. Use this event to add custom content/ listeners to the popped up date picker. 168 * 169 * @param Object s (optional) Customize your date pickers. 170 * @option Number month The month to render when the date picker is opened (NOTE that months are zero based). Default is today's month. 171 * @option Number year The year to render when the date picker is opened. Default is today's year. 172 * @option String|Date startDate The first date date can be selected. 173 * @option String|Date endDate The last date that can be selected. 174 * @option Boolean inline Whether to create the datePicker as inline (e.g. always on the page) or as a model popup. Default is false (== modal popup) 175 * @option Boolean createButton Whether to create a .dp-choose-date anchor directly after the matched element which when clicked will trigger the showing of the date picker. Default is true. 176 * @option Boolean showYearNavigation Whether to display buttons which allow the user to navigate through the months a year at a time. Default is true. 177 * @option Boolean closeOnSelect Whether to close the date picker when a date is selected. Default is true. 178 * @option Boolean displayClose Whether to create a "Close" button within the date picker popup. Default is false. 179 * @option Boolean selectMultiple Whether a user should be able to select multiple dates with this date picker. Default is false. 180 * @option Number numSelectable The maximum number of dates that can be selected where selectMultiple is true. Default is a very high number. 181 * @option Boolean clickInput If the matched element is an input type="text" and this option is true then clicking on the input will cause the date picker to appear. 182 * @option Boolean rememberViewedMonth Whether the datePicker should remember the last viewed month and open on it. If false then the date picker will always open with the month for the first selected date visible. 183 * @option Boolean selectWeek Whether to select a complete week at a time... 184 * @option Number verticalPosition The vertical alignment of the popped up date picker to the matched element. One of $.dpConst.POS_TOP and $.dpConst.POS_BOTTOM. Default is $.dpConst.POS_TOP. 185 * @option Number horizontalPosition The horizontal alignment of the popped up date picker to the matched element. One of $.dpConst.POS_LEFT and $.dpConst.POS_RIGHT. 186 * @option Number verticalOffset The number of pixels offset from the defined verticalPosition of this date picker that it should pop up in. Default in 0. 187 * @option Number horizontalOffset The number of pixels offset from the defined horizontalPosition of this date picker that it should pop up in. Default in 0. 188 * @option (Function|Array) renderCallback A reference to a function (or an array of separate functions) that is called as each cell is rendered and which can add classes and event listeners to the created nodes. Each callback function will receive four arguments;
188 a jquery object wrapping the created TD, a Date object containing the date this TD represents, a number giving the currently rendered month and a number giving the currently rendered year. Default is no callback. 189 * @option String hoverClass The class to attach to each cell when you hover over it (to allow you to use hover effects in IE6 which doesn't support the :hover pseudo-class on elements other than links). Default is dp-hover. Pass false if you don't want a hover class. 190 * @option String autoFocusNextInput Whether focus should be passed onto the next input in the form (true) or remain on this input (false) when a date is selected and the calendar closes 191 * @type jQuery 192 * @name datePicker 193 * @cat plugins/datePicker 194 * @author Kelvin Luck (http://www.kelvinluck.com/) 195 * 196 * @example $('input.date-picker').datePicker(); 197 * @desc Creates a date picker button next to all matched input elements. When the button is clicked on the value of the selected date will be placed in the corresponding input (formatted according to Date.format). 198 * 199 * @example demo/index.html 200 * @desc See the projects homepage for many more complex examples... 201 **/ 202 datePicker: function (s) { 203 if (!$.event._dpCache) $.event._dpCache = []; 204 205 // initialise the date picker controller with the relevant settings... 206 s = $.extend({}, $.fn.datePicker.defaults, s); 207 return this.each( 208 function () { 209 var $this = $(this); 210 var alreadyExists = true; 211 212 if (!this._dpId) { 213 this._dpId = $.guid++; 214 $.event._dpCache[this._dpId] = new DatePicker(this); 215 alreadyExists = false; 216 } 217 218 if (s.inline) { 219 s.createButton = false; 220 s.displayClose = false; 221 s.closeOnSelect = false; 222 $this.empty(); 223 } 224 225 var controller = $.event._dpCache[this._dpId]; 226 227 controller.init(s); 228 229 if (!alreadyExists && s.createButton) { 230 // create it! 231 controller.button = $('<a href="#" class="dp-choose-date" title="' + $.dpText.TEXT_CHOOSE_DATE + '">' + $.dpText.TEXT_CHOOSE_DATE + '</a>') 232 .on( 233 'click', 234 function () { 235 $this.dpDisplay(this); 236 this.blur(); 237 return false; 238 } 239 ); 240 $this.after(controller.button); 241 } 242 243 if (!alreadyExists && $this.is(':text')) { 244 $this 245 .on( 246 'dateSelected', 247 function (e, selectedDate, $td) { 248 this.value = selectedDate.asString(); 249 } 250 ).on( 251 'change', 252 function () { 253 if (this.value == '') { 254 controller.clearSelected(); 255 } else { 256 var d = Date.fromString(this.value); 257 if (d) { 258 controller.setSelected(d, true, true); 259 } 260 } 261 } 262 ); 263 264 if (s.clickInput) { 265 $this.on( 266 'click', 267 function () { 268 // The change event doesn't happen until the input loses focus so we need to manually trigger it... 269 $this.trigger('change'); 270 $this.dpDisplay(); 271 } 272 ); 273 } 274 var d = Date.fromString(this.value); 275 if (this.value != '' && d) { 276 controller.setSelected(d, true, true); 277 } 278 } 279 280 $this.addClass('dp-applied'); 281 282 } 283 ) 284 }, 285 /** 286 * Disables or enables this date picker 287 * 288 * @param Boolean s Whether to disable (true) or enable (false) this datePicker 289 * @type jQuery 290 * @name dpSetDisabled 291 * @cat plugins/datePicker 292 * @author Kelvin Luck (http://www.kelvinluck.com/) 293 * 294 * @example $('.date-picker').datePicker(); 295 * $('.date-picker').dpSetDisabled(true);
296 * @desc Prevents this date picker from displaying and adds a class of dp-disabled to it (and it's associated button if it has one) for styling purposes. If the matched element is an input field then it will also set the disabled attribute to stop people directly editing the field. 297 **/ 298 dpSetDisabled: function (s) { 299 return _w.call(this, 'setDisabled', s); 300 }, 301 /** 302 * Updates the first selectable date for any date pickers on any matched elements. 303 * 304 * @param String|Date d A Date object or string representing the first selectable date (formatted according to Date.format). 305 * @type jQuery 306 * @name dpSetStartDate 307 * @cat plugins/datePicker 308 * @author Kelvin Luck (http://www.kelvinluck.com/) 309 * 310 * @example $('.date-picker').datePicker(); 311 * $('.date-picker').dpSetStartDate('01/01/2000'); 312 * @desc Creates a date picker associated with all elements with a class of "date-picker" then sets the first selectable date for each of these to the first day of the millenium. 313 **/ 314 dpSetStartDate: function (d) { 315 return _w.call(this, 'setStartDate', d); 316 }, 317 /** 318 * Updates the last selectable date for any date pickers on any matched elements. 319 * 320 * @param String|Date d A Date object or string representing the last selectable date (formatted according to Date.format). 321 * @type jQuery 322 * @name dpSetEndDate 323 * @cat plugins/datePicker 324 * @author Kelvin Luck (http://www.kelvinluck.com/) 325 * 326 * @example $('.date-picker').datePicker(); 327 * $('.date-picker').dpSetEndDate('01/01/2010'); 328 * @desc Creates a date picker associated with all elements with a class of "date-picker" then sets the last selectable date for each of these to the first Janurary 2010. 329 **/ 330 dpSetEndDate: function (d) { 331 return _w.call(this, 'setEndDate', d); 332 }, 333 /** 334 * Gets a list of Dates currently selected by this datePicker. This will be an empty array if no dates are currently selected or NULL if there is no datePicker associated with the matched element. 335 * 336 * @type Array 337 * @name dpGetSelected 338 * @cat plugins/datePicker 339 * @author Kelvin Luck (http://www.kelvinluck.com/) 340 * 341 * @example $('.date-picker').datePicker(); 342 * alert($('.date-picker').dpGetSelected()); 343 * @desc Will alert an empty array (as nothing is selected yet) 344 **/ 345 dpGetSelected: function () { 346 var c = _getController(this[0]); 347 if (c) { 348 return c.getSelected(); 349 } 350 return null; 351 }, 352 dpGetDisplayedMonthYear: function () { 353 var c = _getController(this[0]); 354 355 return [c.displayedMonth, c.displayedYear]; 356 }, 357 /** 358 * Selects or deselects a date on any matched element's date pickers. Deselcting is only useful on date pickers where selectMultiple==true. Selecting will only work if the passed date is within the startDate and endDate boundries for a given date picker. 359 * 360 * @param String|Date d A Date object or string representing the date you want to select (formatted according to Date.format). 361 * @param Boolean v Whether you want to select (true) or deselect (false) this date. Optional - default = true. 362 * @param Boolean m Whether you want the date picker to open up on the month of this date when it is next opened. Optional - default = true. 363 * @param Boolean e Whether you want the date picker to dispatch events related to this change of selection. Optional - default = true. 364 * @type jQuery 365 * @name dpSetSelected 366 * @cat plugins/datePicker 367 * @author Kelvin Luck (http://www.kelvinluck.com/) 368 * 369 * @example $('.date-picker').datePicker(); 370 * $('.date-picker').dpSetSelected('01/01/2010');
371 * @desc Creates a date picker associated with all elements with a class of "date-picker" then sets the selected date on these date pickers to the first Janurary 2010. When the date picker is next opened it will display Janurary 2010. 372 **/ 373 dpSetSelected: function (d, v, m, e) { 374 if (v == undefined) v = true; 375 if (m == undefined) m = true; 376 if (e == undefined) e = true; 377 return _w.call(this, 'setSelected', Date.fromString(d), v, m, e); 378 }, 379 /** 380 * Sets the month that will be displayed when the date picker is next opened. If the passed month is before startDate then the month containing startDate will be displayed instead. If the passed month is after endDate then the month containing the endDate will be displayed instead. 381 * 382 * @param Number m The month you want the date picker to display. Optional - defaults to the currently displayed month. 383 * @param Number y The year you want the date picker to display. Optional - defaults to the currently displayed year. 384 * @type jQuery 385 * @name dpSetDisplayedMonth 386 * @cat plugins/datePicker 387 * @author Kelvin Luck (http://www.kelvinluck.com/) 388 * 389 * @example $('.date-picker').datePicker(); 390 * $('.date-picker').dpSetDisplayedMonth(10, 2008); 391 * @desc Creates a date picker associated with all elements with a class of "date-picker" then sets the selected date on these date pickers to the first Janurary 2010. When the date picker is next opened it will display Janurary 2010. 392 **/ 393 dpSetDisplayedMonth: function (m, y) { 394 return _w.call(this, 'setDisplayedMonth', Number(m), Number(y), true); 395 }, 396 /** 397 * Displays the date picker associated with the matched elements. Since only one date picker can be displayed at once then the date picker associated with the last matched element will be the one that is displayed. 398 * 399 * @param HTMLElement e An element that you want the date picker to pop up relative in position to. Optional - default behaviour is to pop up next to the element associated with this date picker. 400 * @type jQuery 401 * @name dpDisplay 402 * @cat plugins/datePicker 403 * @author Kelvin Luck (http://www.kelvinluck.com/) 404 * 405 * @example $('#date-picker').datePicker(); 406 * $('#date-picker').dpDisplay(); 407 * @desc Creates a date picker associated with the element with an id of date-picker and then causes it to pop up. 408 **/ 409 dpDisplay: function (e) { 410 return _w.call(this, 'display', e); 411 }, 412 /** 413 * Sets a function or array of functions that is called when each TD of the date picker popup is rendered to the page 414 * 415 * @param (Function|Array) a A function or an array of functions that are called when each td is rendered. Each function will receive four arguments; a jquery object wrapping the created TD, a Date object containing the date this TD represents, a number giving the currently rendered month and a number giving the currently rendered year. 416 * @type jQuery 417 * @name dpSetRenderCallback 418 * @cat plugins/datePicker 419 * @author Kelvin Luck (http://www.kelvinluck.com/) 420 * 421 * @example $('#date-picker').datePicker(); 422 * $('#date-picker').dpSetRenderCallback(function($td, thisDate, month, year) 423 * { 424 * // do stuff as each td is rendered dependant on the date in the td and the displayed month and year 425 * }); 426 * @desc Creates a date picker associated with the element with an id of date-picker and then creates a function which is called as each td is rendered when this date picker is displayed. 427 **/ 428 dpSetRenderCallback: function (a) { 429 return _w.call(this, 'setRenderCallback', a); 430 }, 431 /** 432 * Sets the position that the datePicker will pop up (relative to it's associated element) 433 * 434 * @param Number v The vertical alignment of the created date picker to it's associated element. Possible values are $.dpConst.POS_TOP and $.dpConst.POS_BOTTOM 435 * @param Number h The horizontal alignment of the created date picker to it's associated element. Possible values are $.dpConst.POS_LEFT and $.dpConst.POS_RIGHT 436 * @type jQuery 437 * @name dpSetPosition 438 * @cat plugins/datePicker 439 * @author Kelvin Luck (http://www.kelvinluck.com/) 440 * 441 * @example $('#date-picker').datePicker(); 442 * $('#date-picker').dpSetPosition($.dpConst.POS_BOTTOM, $.dpConst.POS_RIGHT);
443 * @desc Creates a date picker associated with the element with an id of date-picker and makes it so that when this date picker pops up it will be bottom and right aligned to the #date-picker element. 444 **/ 445 dpSetPosition: function (v, h) { 446 return _w.call(this, 'setPosition', v, h); 447 }, 448 /** 449 * Sets the offset that the popped up date picker will have from it's default position relative to it's associated element (as set by dpSetPosition) 450 * 451 * @param Number v The vertical offset of the created date picker. 452 * @param Number h The horizontal offset of the created date picker. 453 * @type jQuery 454 * @name dpSetOffset 455 * @cat plugins/datePicker 456 * @author Kelvin Luck (http://www.kelvinluck.com/) 457 * 458 * @example $('#date-picker').datePicker(); 459 * $('#date-picker').dpSetOffset(-20, 200); 460 * @desc Creates a date picker associated with the element with an id of date-picker and makes it so that when this date picker pops up it will be 20 pixels above and 200 pixels to the right of it's default position. 461 **/ 462 dpSetOffset: function (v, h) { 463 return _w.call(this, 'setOffset', v, h); 464 }, 465 /** 466 * Closes the open date picker associated with this element. 467 * 468 * @type jQuery 469 * @name dpClose 470 * @cat plugins/datePicker 471 * @author Kelvin Luck (http://www.kelvinluck.com/) 472 * 473 * @example $('.date-pick') 474 * .datePicker() 475 * .on( 476 * 'focus', 477 * function() 478 * { 479 * $(this).dpDisplay(); 480 * } 481 * ).on( 482 * 'blur', 483 * function() 484 * { 485 * $(this).dpClose(); 486 * } 487 * ); 488 **/ 489 dpClose: function () { 490 return _w.call(this, '_closeCalendar', false, this[0]); 491 }, 492 /** 493 * Rerenders the date picker's current month (for use with inline calendars and renderCallbacks). 494 * 495 * @type jQuery 496 * @name dpRerenderCalendar 497 * @cat plugins/datePicker 498 * @author Kelvin Luck (http://www.kelvinluck.com/) 499 * 500 **/ 501 dpRerenderCalendar: function () { 502 return _w.call(this, '_rerenderCalendar'); 503 }, 504 // private function called on unload to clean up any expandos etc and prevent memory links... 505 _dpDestroy: function () { 506 // TODO - implement this? 507 } 508 }); 509 510 // private internal function to cut down on the amount of code needed where we forward 511 // dp* methods on the jQuery object on to the relevant DatePicker controllers... 512 var _w = function (f, a1, a2, a3, a4) { 513 return this.each( 514 function () { 515 var c = _getController(this); 516 if (c) { 517 c[f](a1, a2, a3, a4); 518 } 519 } 520 ); 521 }; 522 523 function DatePicker(ele) { 524 this.ele = ele; 525 526 // initial values... 527 this.displayedMonth = null; 528 this.displayedYear = null; 529 this.startDate = null; 530 this.endDate = null; 531 this.showYearNavigation = null; 532 this.closeOnSelect = null; 533 this.displayClose = null; 534 this.rememberViewedMonth = null; 535 this.selectMultiple = null; 536 this.numSelectable = null; 537 this.numSelected = null; 538 this.verticalPosition = null; 539 this.horizontalPosition = null; 540 this.verticalOffset = null; 541 this.horizontalOffset = null; 542 this.button = null; 543 this.renderCallback = []; 544 this.selectedDates = {}; 545 this.inline = null; 546 this.context = '#dp-popup'; 547 this.uiForm = null; 548 this.settings = {}; 549 }; 550 $.extend( 551 DatePicker.prototype, 552 { 553 init: function (s) { 554 this.setStartDate(s.startDate); 555 this.setEndDate(s.endDate); 556 this.setDisplayedMonth(Number(s.month), Number(s.year)); 557 this.setRenderCallback(s.renderCallback); 558 this.showYearNavigation = s.showYearNavigation; 559 this.closeOnSelect = s.closeOnSelect; 560 this.displayClose = s.displayClose; 561 this.rememberViewedMonth = s.rememberViewedMonth; 562 this.selectMultiple = s.selectMultiple; 563 this.selectableDate = s.selectableDate || false; 564 this.numSelectable = s.selectMultiple ? s.numSelectable : 1; 565 this.numSelected = 0; 566 this.verticalPosition = s.verticalPosition; 567 this.horizontalPosition = s.horizontalPosition; 568 this.hoverClass = s.hoverClass; 569 this.setOffset(s.verticalOffset, s.horizontalOffset); 570 this.inline = s.inline; 571 this.settings = s;
572 if (this.inline) { 573 this.context = this.ele; 574 this.display(); 575 } 576 }, 577 setStartDate: function (d) { 578 if (d) { 579 if (d instanceof Date) { 580 this.startDate = d; 581 } else { 582 this.startDate = Date.fromString(d); 583 } 584 } 585 if (!this.startDate) { 586 this.startDate = (new Date()).zeroTime(); 587 } 588 this.setDisplayedMonth(this.displayedMonth, this.displayedYear); 589 }, 590 setEndDate: function (d) { 591 if (d) { 592 if (d instanceof Date) { 593 this.endDate = d; 594 } else { 595 this.endDate = Date.fromString(d); 596 } 597 } 598 if (!this.endDate) { 599 this.endDate = (new Date('12/31/2999')); // using the JS Date.parse function which expects mm/dd/yyyy 600 } 601 if (this.endDate.getTime() < this.startDate.getTime()) { 602 this.endDate = this.startDate; 603 } 604 this.setDisplayedMonth(this.displayedMonth, this.displayedYear); 605 }, 606 setPosition: function (v, h) { 607 this.verticalPosition = v; 608 this.horizontalPosition = h; 609 }, 610 setOffset: function (v, h) { 611 this.verticalOffset = parseInt(v) || 0; 612 this.horizontalOffset = parseInt(h) || 0; 613 }, 614 setDisabled: function (s) { 615 $e = $(this.ele); 616 $e[s ? 'addClass' : 'removeClass']('dp-disabled'); 617 if (this.button) { 618 $but = $(this.button); 619 $but[s ? 'addClass' : 'removeClass']('dp-disabled'); 620 $but.attr('title', s ? '' : $.dpText.TEXT_CHOOSE_DATE); 621 } 622 if ($e.is(':text')) { 623 $e.attr('disabled', s ? 'disabled' : ''); 624 } 625 }, 626 setDisplayedMonth: function (m, y, rerender) { 627 if (this.startDate == undefined || this.endDate == undefined) { 628 return; 629 } 630 var s = new Date(this.startDate.getTime()); 631 s.setDate(1); 632 var e = new Date(this.endDate.getTime()); 633 e.setDate(1); 634 635 var t; 636 if ((!m && !y) || (isNaN(m) && isNaN(y))) { 637 // no month or year passed - default to current month 638 t = new Date().zeroTime(); 639 t.setDate(1); 640 } else if (isNaN(m)) { 641 // just year passed in - presume we want the displayedMonth 642 t = new Date(y, this.displayedMonth, 1); 643 } else if (isNaN(y)) { 644 // just month passed in - presume we want the displayedYear 645 t = new Date(this.displayedYear, m, 1); 646 } else { 647 // year and month passed in - that's the date we want! 648 t = new Date(y, m, 1); 649 } 650 // check if the desired date is within the range of our defined startDate and endDate 651 var oldMonth = this.displayedMonth, 652 oldYear = this.displayedYear; 653 this.displayedMonth = t.getMonth(); 654 this.displayedYear = t.getFullYear(); 655 656 if (rerender && (this.displayedMonth != oldMonth || this.displayedYear != oldYear)) { 657 this._rerenderCalendar(); 658 $(this.ele).trigger('dpMonthChanged', [this.displayedMonth, this.displayedYear]); 659 } 660 }, 661 setSelected: function (d, v, moveToMonth, dispatchEvents) { 662 if (d < this.startDate || d.zeroTime() > this.endDate.zeroTime()) { 663 // Don't allow people to select dates outside range... 664 return; 665 } 666 var s = this.settings; 667 if (s.selectWeek) { 668 d = d.addDays(-(d.getDay() - Date.firstDayOfWeek + 7) % 7); 669 if (d < this.startDate) // The first day of this week is before the start date so is unselectable... 670 { 671 return; 672 } 673 } 674 if (v == this.isSelected(d)) // this date is already un/selected 675 { 676 return; 677 } 678 if (this.selectMultiple == false) { 679 this.clearSelected(); 680 } else if (v && this.numSelected == this.numSelectable) { 681 // can't select any more dates... 682 return; 683 } 684 if (moveToMonth && (this.displayedMonth != d.getMonth() || this.displayedYear != d.getFullYear())) { 685 this.setDisplayedMonth(d.getMonth(), d.getFullYear(), true); 686 } 687 this.selectedDates[d.asString()] = v; 688 this.numSelected += v ? 1 : -1;
689 var selectorString = 'td.' + (d.getMonth() == this.displayedMonth ? 'current-month' : 'other-month'); 690 var $td; 691 $(selectorString, this.context).each( 692 function () { 693 if ($(this).data('datePickerDate') == d.asString()) { 694 $td = $(this); 695 if (s.selectWeek) { 696 $td.parent()[v ? 'addClass' : 'removeClass']('selectedWeek'); 697 } 698 $td[v ? 'addClass' : 'removeClass']('selected'); 699 } 700 } 701 ); 702 $('td', this.context).not('.selected')[this.selectMultiple && this.numSelected == this.numSelectable ? 'addClass' : 'removeClass']('unselectable'); 703 704 if (dispatchEvents) { 705 var s = this.isSelected(d); 706 $e = $(this.ele); 707 var dClone = Date.fromString(d.asString()); 708 $e.trigger('dateSelected', [dClone, $td, s]); 709 $e.trigger('change'); 710 } 711 }, 712 isSelected: function (d) { 713 return this.selectedDates[d.asString()]; 714 }, 715 getSelected: function () { 716 var r = []; 717 for (var s in this.selectedDates) { 718 if (this.selectedDates[s] == true) { 719 r.push(Date.fromString(s)); 720 } 721 } 722 return r; 723 }, 724 clearSelected: function () { 725 this.selectedDates = {}; 726 this.numSelected = 0; 727 $('td.selected', this.context).removeClass('selected').parent().removeClass('selectedWeek'); 728 }, 729 display: function (eleAlignTo) { 730 if ($(this.ele).is('.dp-disabled')) return; 731 732 eleAlignTo = eleAlignTo || this.ele; 733 var c = this; 734 var $ele = $(eleAlignTo); 735 var eleOffset = $ele.offset(); 736 737 var $createIn; 738 var attrs; 739 var attrsCalendarHolder; 740 var cssRules; 741 742 if (c.inline) { 743 $createIn = $(this.ele); 744 attrs = { 745 'id': 'calendar-' + this.ele._dpId, 746 'class': 'dp-popup dp-popup-inline' 747 }; 748 749 $('.dp-popup', $createIn).remove(); 750 cssRules = { 751 }; 752 } else { 753 $createIn = $('body'); 754 attrs = { 755 'id': 'dp-popup', 756 'class': 'dp-popup' 757 }; 758 cssRules = { 759 'top': eleOffset.top + c.verticalOffset, 760 'left': eleOffset.left + c.horizontalOffset 761 }; 762 763 var _checkMouse = function (e) { 764 var el = e.target; 765 var cal = $('#dp-popup')[0]; 766 767 while (true) { 768 if (el == cal) { 769 return true; 770 } else if (el == document) { 771 c._closeCalendar(); 772 return false; 773 } else { 774 el = $(el).parent()[0]; 775 } 776 } 777 }; 778 this._checkMouse = _checkMouse; 779 780 c._closeCalendar(true); 781 $(document).on( 782 'keydown.datepicker', 783 function (event) { 784 if (event.keyCode == 27) { 785 c._closeCalendar(); 786 } 787 } 788 ); 789 } 790 791 if (!c.rememberViewedMonth) { 792 var selectedDate = this.getSelected()[0]; 793 if (selectedDate) { 794 selectedDate = new Date(selectedDate); 795 this.setDisplayedMonth(selectedDate.getMonth(), selectedDate.getFullYear(), false); 796 } 797 } 798 799 $createIn 800 .append( 801 $('<div></div>') 802 .attr(attrs) 803 .css(cssRules) 804 .append( 805// $('<a href="#" class="selecteee">aaa</a>'), 806 $('<h2></h2>'), 807 $('<div class="dp-nav-prev"></div>') 808 .append( 809 $('<a class="dp-nav-prev-year" href="#" title="' + $.dpText.TEXT_PREV_YEAR + '"><<</a>') 810 .on( 811 'click', 812 function () { 813 return c._displayNewMonth.call(c, this, 0, -1);
814 } 815 ), 816 $('<a class="dp-nav-prev-month" href="#" title="' + $.dpText.TEXT_PREV_MONTH + '"><</a>') 817 .on( 818 'click', 819 function () { 820 return c._displayNewMonth.call(c, this, -1, 0); 821 } 822 ) 823 ), 824 $('<div class="dp-nav-next"></div>') 825 .append( 826 $('<a class="dp-nav-next-year" href="#" title="' + $.dpText.TEXT_NEXT_YEAR + '">>></a>') 827 .on( 828 'click', 829 function () { 830 return c._displayNewMonth.call(c, this, 0, 1); 831 } 832 ), 833 $('<a class="dp-nav-next-month" href="#" title="' + $.dpText.TEXT_NEXT_MONTH + '">></a>') 834 .on( 835 'click', 836 function () { 837 return c._displayNewMonth.call(c, this, 1, 0); 838 } 839 ) 840 ), 841 $('<div class="dp-calendar"></div>') 842 ) 843 .bgIframe() 844 ); 845 846 var $pop = this.inline ? $('.dp-popup', this.context) : $('#dp-popup'); 847 848 if (this.showYearNavigation == false) { 849 $('.dp-nav-prev-year, .dp-nav-next-year', c.context).css('display', 'none'); 850 } 851 if (this.displayClose) { 852 $pop.append( 853 $('<a href="#" id="dp-close">' + $.dpText.TEXT_CLOSE + '</a>') 854 .on( 855 'click', 856 function () { 857 c._closeCalendar(); 858 return false; 859 } 860 ) 861 ); 862 } 863 c._renderCalendar(); 864 865 $(this.ele).trigger('dpDisplayed', $pop); 866 867 if (!c.inline) { 868 if (this.verticalPosition == $.dpConst.POS_BOTTOM) { 869 $pop.css('top', eleOffset.top + $ele.height() - $pop.height() + c.verticalOffset); 870 } 871 if (this.horizontalPosition == $.dpConst.POS_RIGHT) { 872 $pop.css('left', eleOffset.left + $ele.width() - $pop.width() + c.horizontalOffset); 873 } 874 // $('.selectee', this.context).focus(); 875 $(document).on('mousedown.datepicker', this._checkMouse); 876 } 877 878 }, 879 setRenderCallback: function (a) { 880 if (a == null) return; 881 if (a && typeof (a) == 'function') { 882 a = [a]; 883 } 884 this.renderCallback = this.renderCallback.concat(a); 885 }, 886 cellRender: function ($td, thisDate, month, year) { 887 var c = this.dpController; 888 var d = new Date(thisDate.getTime()); 889 890 // add our click handlers to deal with it when the days are clicked... 891 892 $td.on( 893 'click', 894 function () { 895 var $this = $(this); 896 if (!$this.is('.disabled')) { 897 c.setSelected(d, !$this.is('.selected') || !c.selectMultiple, false, true); 898 if (c.closeOnSelect) { 899 // Focus the next input in the form⦠900 if (c.settings.autoFocusNextInput) { 901 var ele = c.ele; 902 var found = false; 903 $(':input', ele.form).each( 904 function () { 905 if (found) { 906 $(this).focus(); 907 return false; 908 } 909 if (this == ele) { 910 found = true; 911 } 912 } 913 ); 914 } else { 915 try { 916 c.ele.focus(); 917 } catch (e) { } 918 } 919 c._closeCalendar(); 920 } 921 } 922 } 923 ); 924 if (c.isSelected(d)) { 925 $td.addClass('selected'); 926 if (c.settings.selectWeek) { 927 $td.parent().addClass('selectedWeek'); 928 } 929 } else if (c.selectMultiple && c.numSelected == c.numSelectable) { 930 $td.addClass('unselectable'); 931 } 932 933 }, 934 _applyRenderCallbacks: function () { 935 var c = this; 936 $('td', this.context).each( 937 function () { 938 for (var i = 0; i < c.renderCallback.length; i++) { 939 $td = $(this); 940 c.renderCallback[i].apply(this, [$td, Date.fromString($td.data('datePickerDate')), c.displayedMonth, c.displayedYear]); 941 } 942 } 943 ); 944 return; 945 }, 946 // ele is the clicked button - only proceed if it doesn't have the class disabled...
947 // m and y are -1, 0 or 1 depending which direction we want to go in... 948 _displayNewMonth: function (ele, m, y) { 949 if (!$(ele).is('.disabled')) { 950 951 this.setDisplayedMonth(this.displayedMonth + m, this.displayedYear + y, true); 952 953 /*if ( this.selectableDate && this.uiForm) { 954 this.uiForm.update({ 955 m: this.displayedMonth, 956 y: this.displayedYear 957 }); 958 };*/ 959 } 960 ele.blur(); 961 return false; 962 }, 963 _displayNewDate: function (m, y) { 964 this.setDisplayedMonth(m, y, true); 965 966 return false; 967 }, 968 _rerenderCalendar: function () { 969 this._clearCalendar(); 970 this._renderCalendar(); 971 if (this.selectableDate && this.uiForm) { 972 this._renderDateSelect(); 973 } 974 }, 975 _renderDateSelect: function (title, displayedMonth, displayedYear) { 976 var that = this, 977 thisYear = (new Date()).getFullYear(), 978 thisMonth = (new Date()).getMonth(), 979 startYear = thisYear - 3, 980 endYear = thisYear + 2, 981 startMonth = 0, 982 endMonth = 11; 983 984 function getMY(d) { 985 986 var d = Date.fromString(d, that.settings.dateFormat); 987 988 return [d.getFullYear(), d.getMonth()]; 989 } 990 991 if (this.settings.isLimited) { 992 993 var start = getMY(this.settings.isLimited[0]), 994 end = getMY(this.settings.isLimited[1]); 995 996 startYear = start[0]; 997 endYear = end[0]; 998 if (startYear == endYear) { 999 startMonth = start[1]; 1000 endMonth = end[1]; 1001 } 1002 } 1003 1004 if ((!title && this.uiForm) || title.hasClass('dp-active')) { 1005 this.uiForm.update({ 1006 m: this.displayedMonth, 1007 mm: Date.monthNames[this.displayedMonth], 1008 y: this.displayedYear 1009 }); 1010 return that; 1011 } 1012 1013 var uiForm = $('<div class="ui-form" style="display: flex; gap: 5px;"><div class="form-element w-60"><select class="select-month small"></select></div><div class="form-element"><select class="select-year small"></select></div></div>') 1014 selectYear = $('select.select-year', uiForm), 1015 selectMonth = $('select.select-month', uiForm); 1016 1017 for (var i = startMonth; i <= endMonth; i++) { 1018 var option = $('<option>').attr('value', i).html(Date.monthNames[i]); 1019 if (i == displayedMonth) { 1020 option.attr("selected", "selected").prop("selected", true); 1021 } 1022 option.appendTo(selectMonth); 1023 }; 1024 for (var i = endYear; i > startYear - 1; i--) { 1025 var option = $('<option>').attr('value', i).text(i); 1026 if (i == displayedYear) { 1027 option.attr("selected", "selected").prop("selected", true); 1028 } 1029 option.appendTo(selectYear); 1030 }; 1031 1032 $('<div>').addClass('form-element f-none'); 1033 1034 uiForm.appendTo(title); 1035 1036 title.addClass('dp-active'); 1037 1038 this.uiForm = $('.ui-form', title); 1039 this.uiForm.update = function (values) { 1040 $('select.select-year', this).selectbox('change', values.y.toString(), values.y); // val(values.y); 1041 $('select.select-month', this).selectbox('change', values.m.toString(), values.mm); // val(values.m); 1042 }; 1043 $('select', title).addClass('auto-list-width').selectbox().on("change", function (e) { 1044 1045 e.preventDefault(); 1046 1047 var m = $('option', $('select.select-month', that.uiForm)).filter(':selected').val(), 1048 y = $('option', $('select.select-year', that.uiForm)).filter(':selected').val(); 1049 1050 that._displayNewDate(m, y); 1051 }); 1052 }, 1053 _renderCalendar: function () { 1054 // set the title... 1055 if (!this.selectableDate) { 1056 $('h2', this.context).html((new Date(this.displayedYear, this.displayedMonth, 1)).asString($.dpText.HEADER_FORMAT)); 1057 } else { 1058 var title = $('h2', this.context); 1059 this._renderDateSelect.call(this, title, this.displayedMonth, this.displayedYear); 1060 } 1061 1062 // render the calendar... 1063 $('.dp-calendar', this.context).renderCalendar( 1064 $.extend( 1065 {}, 1066 this.settings, 1067 { 1068 month: this.displayedMonth, 1069 year: this.displayedYear, 1070 renderCallback: this.cellRender, 1071 dpController: this, 1072 hoverClass: this.hoverClass 1073 }) 1074 ); 1075 1076 // update the status of the control buttons and disable dates before startDate or after endDate... 1077 // TODO: When should the year buttons be disabled? When you can't go forward a whole year from where you are or is that annoying? 1078 if (this.displayedYear == this.startDate.getFullYear() && this.displayedMonth == this.startDate.getMonth()) { 1079 $('.dp-nav-prev-year', this.context).addClass('disabled'); 1080 $('.dp-nav-prev-month', this.context).addClass('disabled'); 1081 $('.dp-calendar td.other-month', this.context).each( 1082 function () { 1083 var $this = $(this); 1084 if (Number($this.text()) > 20) { 1085 $this.addClass('disabled'); 1086 } 1087 } 1088 ); 1089 var d = this.startDate.getDate(); 1090 $('.dp-calendar td.current-month', this.context).each( 1091 function () { 1092 var $this = $(this); 1093 if (Number($this.text()) < d) { 1094 $this.addClass('disabled'); 1095 } 1096 } 1097 ); 1098 } else { 1099 $('.dp-nav-prev-year', this.context).removeClass('disabled'); 1100 $('.dp-nav-prev-month', this.context).removeClass('disabled'); 1101 var d = this.startDate.getDate(); 1102 if (d > 20) { 1103 // check if the startDate is last month as we might need to add some disabled classes... 1104 var st = this.startDate.getTime(); 1105 var sd = new Date(st); 1106 sd.addMonths(1); 1107 if (this.displayedYear == sd.getFullYear() && this.displayedMonth == sd.getMonth()) { 1108 $('.dp-calendar td.other-month', this.context).each( 1109 function () { 1110 var $this = $(this); 1111 if (Date.fromString($this.data('datePickerDate')).getTime() < st) { 1112 $this.addClass('disabled'); 1113 } 1114 } 1115 ); 1116 } 1117 } 1118 } 1119 if (this.displayedYear == this.endDate.getFullYear() && this.displayedMonth == this.endDate.getMonth()) { 1120 $('.dp-nav-next-year', this.context).addClass('disabled'); 1121 $('.dp-nav-next-month', this.context).addClass('disabled'); 1122 $('.dp-calendar td.other-month', this.context).each( 1123 function () { 1124 var $this = $(this); 1125 if (Number($this.text()) < 14) { 1126 $this.addClass('disabled'); 1127 } 1128 } 1129 ); 1130 var d = this.endDate.getDate(); 1131 $('.dp-calendar td.current-month', this.context).each( 1132 function () { 1133 var $this = $(this); 1134 if (Number($this.text()) > d) { 1135 $this.addClass('disabled'); 1136 } 1137 } 1138 ); 1139 } else { 1140 $('.dp-nav-next-year', this.context).removeClass('disabled'); 1141 $('.dp-nav-next-month', this.context).removeClass('disabled'); 1142 var d = this.endDate.getDate(); 1143 if (d < 13) { 1144 // check if the endDate is next month as we might need to add some disabled classes... 1145 var ed = new Date(this.endDate.getTime()); 1146 ed.addMonths(-1); 1147 if (this.displayedYear == ed.getFullYear() && this.displayedMonth == ed.getMonth()) { 1148 $('.dp-calendar td.other-month', this.context).each( 1149 function () { 1150 var $this = $(this); 1151 var cellDay = Number($this.text()); 1152 if (cellDay < 13 && cellDay > d) { 1153 $this.addClass('disabled'); 1154 } 1155 } 1156 ); 1157 } 1158 } 1159 } 1160 this._applyRenderCallbacks(); 1161 }, 1162 _closeCalendar: function (programatic, ele) { 1163 if (!ele || ele == this.ele) { 1164 $(document).off('mousedown.datepicker'); 1165 $(document).off('keydown.datepicker');
1166 this._clearCalendar(); 1167 $('#dp-popup a').off(); 1168 $('#dp-popup').empty().remove(); 1169 if (!programatic) { 1170 $(this.ele).trigger('dpClosed', [this.getSelected()]); 1171 } 1172 } 1173 }, 1174 // empties the current dp-calendar div and makes sure that all events are unbound 1175 // and expandos removed to avoid memory leaks... 1176 _clearCalendar: function () { 1177 // TODO. 1178 $('.dp-calendar td', this.context).off(); 1179 $('.dp-calendar', this.context).empty(); 1180 } 1181 } 1182 ); 1183 1184 // static constants 1185 $.dpConst = { 1186 SHOW_HEADER_NONE: 0, 1187 SHOW_HEADER_SHORT: 1, 1188 SHOW_HEADER_LONG: 2, 1189 POS_TOP: 0, 1190 POS_BOTTOM: 1, 1191 POS_LEFT: 0, 1192 POS_RIGHT: 1, 1193 DP_INTERNAL_FOCUS: 'dpInternalFocusTrigger' 1194 }; 1195 // localisable text 1196 $.dpText = { 1197 TEXT_PREV_YEAR: Date.dpText.TEXT_PREV_YEAR || 'Previous year', 1198 TEXT_PREV_MONTH: Date.dpText.TEXT_PREV_MONTH || 'Previous month', 1199 TEXT_NEXT_YEAR: Date.dpText.TEXT_NEXT_YEAR || 'Next year', 1200 TEXT_NEXT_MONTH: Date.dpText.TEXT_NEXT_MONTH || 'Next month', 1201 TEXT_CLOSE: Date.dpText.TEXT_CLOSE || 'Close', 1202 TEXT_CHOOSE_DATE: Date.dpText.TEXT_CHOOSE_DATE || 'Choose date', 1203 HEADER_FORMAT: Date.dpText.HEADER_FORMAT || 'mmmm yyyy' 1204 }; 1205 // version 1206 $.dpVersion = '$Id$'; 1207 1208 $.fn.datePicker.defaults = { 1209 month: undefined, 1210 year: undefined, 1211 showHeader: $.dpConst.SHOW_HEADER_SHORT, 1212 startDate: undefined, 1213 endDate: undefined, 1214 inline: false, 1215 renderCallback: null, 1216 createButton: true, 1217 showYearNavigation: true, 1218 closeOnSelect: true, 1219 displayClose: false, 1220 selectMultiple: false, 1221 numSelectable: Number.MAX_VALUE, 1222 clickInput: false, 1223 rememberViewedMonth: true, 1224 selectWeek: false, 1225 selectableDate: false, 1226 verticalPosition: $.dpConst.POS_TOP, 1227 horizontalPosition: $.dpConst.POS_LEFT, 1228 verticalOffset: 0, 1229 horizontalOffset: 0, 1230 hoverClass: 'dp-hover', 1231 autoFocusNextInput: false 1232 }; 1233 1234 function _getController(ele) { 1235 if (ele._dpId) return $.event._dpCache[ele._dpId]; 1236 return false; 1237 }; 1238 1239 // make it so that no error is thrown if bgIframe plugin isn't included (allows you to use conditional 1240 // comments to only include bgIframe where it is needed in IE without breaking this plugin). 1241 if ($.fn.bgIframe == undefined) { 1242 $.fn.bgIframe = function () { return this; }; 1243 }; 1244 1245 1246 // clean-up 1247 $(window) 1248 .on('unload', function () { 1249 var els = $.event._dpCache || []; 1250 for (var i in els) { 1251 $(els[i].ele)._dpDestroy(); 1252 } 1253 }); 1254 1255})(jQuery); 1256/** 1257 * Copyright (c) 2008 Kelvin Luck (http://www.kelvinluck.com/) 1258 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 1259 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. 1260 * . 1261 * $Id$ 1262 **/ 1263 1264(function ($) { 1265 1266 $.fn.extend({ 1267 /** 1268 * Render a calendar table into any matched elements. 1269 * 1270 * @param Object s (optional) Customize your calendars. 1271 * @option Number month The month to render (NOTE that months are zero based). Default is today's month. 1272 * @option Number year The year to render. Default is today's year. 1273 * @option Function renderCallback A reference to a function that is called as each cell is rendered and which can add classes and event listeners to the created nodes. Default is no callback. 1274 * @option Number showHeader Whether or not to show the header row, possible values are: $.dpConst.SHOW_HEADER_NONE (no header), $.dpConst.SHOW_HEADER_SHORT (first letter of each day) and $.dpConst.SHOW_HEADER_LONG (full name of each day). Default is $.dpConst.SHOW_HEADER_SHORT. 1275 * @option String hoverClass The class to attach to each cell when you hover over it (to allow you to use hover effects in IE6 which doesn't support the :hover pseudo-class on elements other than links). Default is dp-hover. Pass fal
1275se if you don't want a hover class. 1276 * @type jQuery 1277 * @name renderCalendar 1278 * @cat plugins/datePicker 1279 * @author Kelvin Luck (http://www.kelvinluck.com/) 1280 * 1281 * @example $('#calendar-me').renderCalendar({month:0, year:2007}); 1282 * @desc Renders a calendar displaying January 2007 into the element with an id of calendar-me. 1283 * 1284 * @example 1285 * var testCallback = function($td, thisDate, month, year) 1286 * { 1287 * if ($td.is('.current-month') && thisDate.getDay() == 4) { 1288 * var d = thisDate.getDate(); 1289 * $td.on( 1290 * 'click', 1291 * function() 1292 * { 1293 * alert('You clicked on ' + d + '/' + (Number(month)+1) + '/' + year); 1294 * } 1295 * ).addClass('thursday'); 1296 * } else if (thisDate.getDay() == 5) { 1297 * $td.html('Friday the ' + $td.html() + 'th'); 1298 * } 1299 * } 1300 * $('#calendar-me').renderCalendar({month:0, year:2007, renderCallback:testCallback}); 1301 * 1302 * @desc Renders a calendar displaying January 2007 into the element with an id of calendar-me. Every Thursday in the current month has a class of "thursday" applied to it, is clickable and shows an alert when clicked. Every Friday on the calendar has the number inside replaced with text. 1303 **/ 1304 renderCalendar: function (s) { 1305 var dc = function (a) { 1306 return document.createElement(a); 1307 }; 1308 1309 s = $.extend({}, $.fn.datePicker.defaults, s); 1310 1311 if (s.showHeader != $.dpConst.SHOW_HEADER_NONE) { 1312 var headRow = $(dc('tr')); 1313 for (var i = Date.firstDayOfWeek; i < Date.firstDayOfWeek + 7; i++) { 1314 var weekday = i % 7; 1315 var day = Date.dayNames[weekday]; 1316 var dayShort = Date.abbrDayNames[weekday]; 1317 headRow.append( 1318 jQuery(dc('th')).attr({ 'scope': 'col', 'abbr': day, 'title': day, 'class': (weekday == 0 || weekday == 6 ? 'weekend' : 'weekday') }).html(s.showHeader == $.dpConst.SHOW_HEADER_SHORT ? dayShort : day) 1319 ); 1320 } 1321 }; 1322 1323 var calendarTable = $(dc('table')) 1324 .attr( 1325 { 1326 'cellspacing': 2 1327 } 1328 ) 1329 .addClass('jCalendar') 1330 .append( 1331 (s.showHeader != $.dpConst.SHOW_HEADER_NONE ? 1332 $(dc('thead')) 1333 .append(headRow) 1334 : 1335 dc('thead') 1336 ) 1337 ); 1338 var tbody = $(dc('tbody')); 1339 1340 var today = (new Date()).zeroTime(); 1341 today.setHours(12); 1342 1343 var month = s.month == undefined ? today.getMonth() : s.month; 1344 var year = s.year || today.getFullYear(); 1345 1346 var currentDate = (new Date(year, month, 1, 12, 0, 0)); 1347 1348 var firstDayOffset = Date.firstDayOfWeek - currentDate.getDay() + 1; 1349 if (firstDayOffset > 1) firstDayOffset -= 7; 1350 var weeksToDraw = Math.ceil(((-1 * firstDayOffset + 1) + currentDate.getDaysInMonth()) / 7); 1351 currentDate.addDays(firstDayOffset - 1); 1352 1353 var doHover = function (firstDayInBounds) { 1354 return function () { 1355 if (s.hoverClass) { 1356 var $this = $(this); 1357 if (!s.selectWeek) { 1358 $this.addClass(s.hoverClass); 1359 } else if (firstDayInBounds && !$this.is('.disabled')) { 1360 $this.parent().addClass('activeWeekHover'); 1361 } 1362 } 1363 } 1364 }; 1365 var unHover = function () { 1366 if (s.hoverClass) { 1367 var $this = $(this); 1368 $this.removeClass(s.hoverClass); 1369 $this.parent().removeClass('activeWeekHover'); 1370 } 1371 }; 1372 1373 var w = 0; 1374 while (w++ < weeksToDraw) { 1375 var r = jQuery(dc('tr')); 1376 var firstDayInBounds = s.dpController ? currentDate > s.dpController.startDate : false; 1377 for (var i = 0; i < 7; i++) { 1378 var thisMonth = currentDate.getMonth() == month; 1379 var d = $(dc('td')) 1380 .text(currentDate.getDate() + '') 1381 .addClass((thisMonth ? 'current-month ' : 'other-month ') + 1382 (currentDate.isWeekend() ? 'weekend ' : 'weekday ') + 1383 (thisMonth && currentDate.getTime() == today.getTime() ? 'today ' : '') 1384 ) 1385 .data('datePickerDate', currentDate.asString()) 1386 .hover(doHover(firstDayInBounds), unHover) 1387 ; 1388 r.append(d); 1389 if (s.renderCallback) { 1390 s.renderCallback(d, currentDate, month, year); 1391 } 1392 // addDays(1) fails in some locales due to daylight savings. See issue 39. 1393 //currentDate.addDays(1); 1394 // set the time to midday to avoid any weird timezone issues?? 1395 currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() + 1, 12, 0, 0); 1396 } 1397 tbody.append(r); 1398 } 1399 calendarTable.append(tbody); 1400 1401 return this.each( 1402 function () { 1403 $(this).empty().append(calendarTable); 1404 } 1405 ); 1406 }, 1407 /** 1408 * Create a datePicker associated with each of the matched elements. 1409 * 1410 * The matched element will receive a few custom events with the following signatures: 1411 * 1412 * dateSelected(event, date, $td, status) 1413 * Triggered when a date is selected. event is a reference to the event, date is the Date selected, $td is a jquery object wrapped around the TD that was clicked on and status is whether the date was selected (true) or deselected (false) 1414 * 1415 * dpClosed(event, selected) 1416 * Triggered when the date picker is closed. event is a reference to the event and selected is an Array containing Date objects. 1417 * 1418 * dpMonthChanged(event, displayedMonth, displayedYear) 1419 * Triggered when the month of the popped up calendar is changed. event is a reference to the event, displayedMonth is the number of the month now displayed (zero based) and displayedYear is the year of the month. 1420 * 1421 * dpDisplayed(event, $datePickerDiv) 1422 * Triggered when the date picker is created. $datePickerDiv is the div containing the date picker. Use this event to add custom content/ listeners to the popped up date picker. 1423 * 1424 * @param Object s (optional) Customize your date pickers. 1425 * @option Number month The month to render when the date picker is opened (NOTE that months are zero based). Default is today's month. 1426 * @option Number year The year to render when the date picker is opened. Default is today's year. 1427 * @option String|Date startDate The first date date can be selected. 1428 * @option String|Date endDate The last date that can be selected. 1429 * @option Boolean inline Whether to create the datePicker as inline (e.g. always on the page) or as a model popup. Default is false (== modal popup) 1430 * @option Boolean createButton Whether to create a .dp-choose-date anchor directly after the matched element which when clicked will trigger the showing of the date picker. Default is true. 1431 * @option Boolean showYearNavigation Whether to display buttons which allow the user to navigate through the months a year at a time. Default is true. 1432 * @option Boolean closeOnSelect Whether to close the date picker when a date is selected. Default is true. 1433 * @option Boolean displayClose Whether to create a "Close" button within the date picker popup. Default is false. 1434 * @option Boolean selectMultiple Whether a user should be able to select multiple dates with this date picker. Default is false. 1435 * @option Number numSelectable The maximum number of dates that can be selected where selectMultiple is true. Default is a very high number. 1436 * @option Boolean clickInput If the matched element is an input type="text" and this option is true then clicking on the input will cause the date picker to appear. 1437 * @option Boolean rememberViewedMonth Whether the datePicker should remember the last viewed month and open on it. If false then the date picker will always open with the month for the first selected date visible. 1438 * @option Boolean selectWeek Whether to select a complete week at a time... 1439 * @option Number verticalPosition The vertical alignment of the popped up date picker to the matched element. One of $.dpConst.POS_TOP and $.dpConst.POS_BOTTOM. Default is $.dpConst.POS_TOP. 1440 * @option Number horizontalPosition The horizontal alignment of the popped up date picker to the matched element. One of $.dpConst.POS_LEFT and $.dpConst.POS_RIGHT. 1441 * @option Number verticalOffset The number of pixels offset from the defined verticalPosition of this date picker that it should pop up in. Default in 0. 1442 * @option Number horizontalOffset The number of pixels offset from the defined horizontalPosition of this date picker that it should pop up in. Default in 0. 1443 * @option (Function|Array) renderCallback A reference to a function (or an array of separate functions) that is called as each cell is rendered and which can add classes and event listeners to the created nodes. Each callback function will receive four arguments;
1443 a jquery object wrapping the created TD, a Date object containing the date this TD represents, a number giving the currently rendered month and a number giving the currently rendered year. Default is no callback. 1444 * @option String hoverClass The class to attach to each cell when you hover over it (to allow you to use hover effects in IE6 which doesn't support the :hover pseudo-class on elements other than links). Default is dp-hover. Pass false if you don't want a hover class. 1445 * @option String autoFocusNextInput Whether focus should be passed onto the next input in the form (true) or remain on this input (false) when a date is selected and the calendar closes 1446 * @type jQuery 1447 * @name datePicker 1448 * @cat plugins/datePicker 1449 * @author Kelvin Luck (http://www.kelvinluck.com/) 1450 * 1451 * @example $('input.date-picker').datePicker(); 1452 * @desc Creates a date picker button next to all matched input elements. When the button is clicked on the value of the selected date will be placed in the corresponding input (formatted according to Date.format). 1453 * 1454 * @example demo/index.html 1455 * @desc See the projects homepage for many more complex examples... 1456 **/ 1457 datePicker: function (s) { 1458 if (!$.event._dpCache) $.event._dpCache = []; 1459 1460 // initialise the date picker controller with the relevant settings... 1461 s = $.extend({}, $.fn.datePicker.defaults, s); 1462 return this.each( 1463 function () { 1464 var $this = $(this); 1465 var alreadyExists = true; 1466 1467 if (!this._dpId) { 1468 this._dpId = $.guid++; 1469 $.event._dpCache[this._dpId] = new DatePicker(this); 1470 alreadyExists = false; 1471 } 1472 1473 if (s.inline) { 1474 s.createButton = false; 1475 s.displayClose = false; 1476 s.closeOnSelect = false; 1477 $this.empty(); 1478 } 1479 1480 var controller = $.event._dpCache[this._dpId]; 1481 1482 controller.init(s); 1483 1484 if (!alreadyExists && s.createButton) { 1485 // create it! 1486 controller.button = $('<a href="#" class="dp-choose-date" title="' + $.dpText.TEXT_CHOOSE_DATE + '">' + $.dpText.TEXT_CHOOSE_DATE + '</a>') 1487 .on( 1488 'click', 1489 function () { 1490 $this.dpDisplay(this); 1491 this.blur(); 1492 return false; 1493 } 1494 ); 1495 $this.after(controller.button); 1496 } 1497 1498 if (!alreadyExists && $this.is(':text')) { 1499 $this 1500 .on( 1501 'dateSelected', 1502 function (e, selectedDate, $td) { 1503 this.value = selectedDate.asString(); 1504 } 1505 ).on( 1506 'change', 1507 function () { 1508 if (this.value == '') { 1509 controller.clearSelected(); 1510 } else { 1511 var d = Date.fromString(this.value); 1512 if (d) { 1513 controller.setSelected(d, true, true); 1514 } 1515 } 1516 } 1517 ); 1518 1519 if (s.clickInput) { 1520 $this.on( 1521 'click', 1522 function () { 1523 // The change event doesn't happen until the input loses focus so we need to manually trigger it... 1524 $this.trigger('change'); 1525 $this.dpDisplay(); 1526 } 1527 ); 1528 } 1529 var d = Date.fromString(this.value); 1530 if (this.value != '' && d) { 1531 controller.setSelected(d, true, true); 1532 } 1533 } 1534 1535 $this.addClass('dp-applied'); 1536 1537 } 1538 ) 1539 }, 1540 /** 1541 * Disables or enables this date picker 1542 * 1543 * @param Boolean s Whether to disable (true) or enable (false) this datePicker 1544 * @type jQuery 1545 * @name dpSetDisabled 1546 * @cat plugins/datePicker 1547 * @author Kelvin Luck (http://www.kelvinluck.com/) 1548 * 1549 * @example $('.date-picker').datePicker(); 1550 * $('.date-picker').dpSetDisabled(true);
1551 * @desc Prevents this date picker from displaying and adds a class of dp-disabled to it (and it's associated button if it has one) for styling purposes. If the matched element is an input field then it will also set the disabled attribute to stop people directly editing the field. 1552 **/ 1553 dpSetDisabled: function (s) { 1554 return _w.call(this, 'setDisabled', s); 1555 }, 1556 /** 1557 * Updates the first selectable date for any date pickers on any matched elements. 1558 * 1559 * @param String|Date d A Date object or string representing the first selectable date (formatted according to Date.format). 1560 * @type jQuery 1561 * @name dpSetStartDate 1562 * @cat plugins/datePicker 1563 * @author Kelvin Luck (http://www.kelvinluck.com/) 1564 * 1565 * @example $('.date-picker').datePicker(); 1566 * $('.date-picker').dpSetStartDate('01/01/2000'); 1567 * @desc Creates a date picker associated with all elements with a class of "date-picker" then sets the first selectable date for each of these to the first day of the millenium. 1568 **/ 1569 dpSetStartDate: function (d) { 1570 return _w.call(this, 'setStartDate', d); 1571 }, 1572 /** 1573 * Updates the last selectable date for any date pickers on any matched elements. 1574 * 1575 * @param String|Date d A Date object or string representing the last selectable date (formatted according to Date.format). 1576 * @type jQuery 1577 * @name dpSetEndDate 1578 * @cat plugins/datePicker 1579 * @author Kelvin Luck (http://www.kelvinluck.com/) 1580 * 1581 * @example $('.date-picker').datePicker(); 1582 * $('.date-picker').dpSetEndDate('01/01/2010'); 1583 * @desc Creates a date picker associated with all elements with a class of "date-picker" then sets the last selectable date for each of these to the first Janurary 2010. 1584 **/ 1585 dpSetEndDate: function (d) { 1586 return _w.call(this, 'setEndDate', d); 1587 }, 1588 /** 1589 * Gets a list of Dates currently selected by this datePicker. This will be an empty array if no dates are currently selected or NULL if there is no datePicker associated with the matched element. 1590 * 1591 * @type Array 1592 * @name dpGetSelected 1593 * @cat plugins/datePicker 1594 * @author Kelvin Luck (http://www.kelvinluck.com/) 1595 * 1596 * @example $('.date-picker').datePicker(); 1597 * alert($('.date-picker').dpGetSelected()); 1598 * @desc Will alert an empty array (as nothing is selected yet) 1599 **/ 1600 dpGetSelected: function () { 1601 var c = _getController(this[0]); 1602 if (c) { 1603 return c.getSelected(); 1604 } 1605 return null; 1606 }, 1607 dpGetDisplayedMonthYear: function () { 1608 var c = _getController(this[0]); 1609 1610 return [c.displayedMonth, c.displayedYear]; 1611 }, 1612 /** 1613 * Selects or deselects a date on any matched element's date pickers. Deselcting is only useful on date pickers where selectMultiple==true. Selecting will only work if the passed date is within the startDate and endDate boundries for a given date picker. 1614 * 1615 * @param String|Date d A Date object or string representing the date you want to select (formatted according to Date.format). 1616 * @param Boolean v Whether you want to select (true) or deselect (false) this date. Optional - default = true. 1617 * @param Boolean m Whether you want the date picker to open up on the month of this date when it is next opened. Optional - default = true. 1618 * @param Boolean e Whether you want the date picker to dispatch events related to this change of selection. Optional - default = true. 1619 * @type jQuery 1620 * @name dpSetSelected 1621 * @cat plugins/datePicker 1622 * @author Kelvin Luck (http://www.kelvinluck.com/) 1623 * 1624 * @example $('.date-picker').datePicker(); 1625 * $('.date-picker').dpSetSelected('01/01/2010');
1626 * @desc Creates a date picker associated with all elements with a class of "date-picker" then sets the selected date on these date pickers to the first Janurary 2010. When the date picker is next opened it will display Janurary 2010. 1627 **/ 1628 dpSetSelected: function (d, v, m, e) { 1629 if (v == undefined) v = true; 1630 if (m == undefined) m = true; 1631 if (e == undefined) e = true; 1632 return _w.call(this, 'setSelected', Date.fromString(d), v, m, e); 1633 }, 1634 /** 1635 * Sets the month that will be displayed when the date picker is next opened. If the passed month is before startDate then the month containing startDate will be displayed instead. If the passed month is after endDate then the month containing the endDate will be displayed instead. 1636 * 1637 * @param Number m The month you want the date picker to display. Optional - defaults to the currently displayed month. 1638 * @param Number y The year you want the date picker to display. Optional - defaults to the currently displayed year. 1639 * @type jQuery 1640 * @name dpSetDisplayedMonth 1641 * @cat plugins/datePicker 1642 * @author Kelvin Luck (http://www.kelvinluck.com/) 1643 * 1644 * @example $('.date-picker').datePicker(); 1645 * $('.date-picker').dpSetDisplayedMonth(10, 2008); 1646 * @desc Creates a date picker associated with all elements with a class of "date-picker" then sets the selected date on these date pickers to the first Janurary 2010. When the date picker is next opened it will display Janurary 2010. 1647 **/ 1648 dpSetDisplayedMonth: function (m, y) { 1649 return _w.call(this, 'setDisplayedMonth', Number(m), Number(y), true); 1650 }, 1651 /** 1652 * Displays the date picker associated with the matched elements. Since only one date picker can be displayed at once then the date picker associated with the last matched element will be the one that is displayed. 1653 * 1654 * @param HTMLElement e An element that you want the date picker to pop up relative in position to. Optional - default behaviour is to pop up next to the element associated with this date picker. 1655 * @type jQuery 1656 * @name dpDisplay 1657 * @cat plugins/datePicker 1658 * @author Kelvin Luck (http://www.kelvinluck.com/) 1659 * 1660 * @example $('#date-picker').datePicker(); 1661 * $('#date-picker').dpDisplay(); 1662 * @desc Creates a date picker associated with the element with an id of date-picker and then causes it to pop up. 1663 **/ 1664 dpDisplay: function (e) { 1665 return _w.call(this, 'display', e); 1666 }, 1667 /** 1668 * Sets a function or array of functions that is called when each TD of the date picker popup is rendered to the page 1669 * 1670 * @param (Function|Array) a A function or an array of functions that are called when each td is rendered. Each function will receive four arguments; a jquery object wrapping the created TD, a Date object containing the date this TD represents, a number giving the currently rendered month and a number giving the currently rendered year. 1671 * @type jQuery 1672 * @name dpSetRenderCallback 1673 * @cat plugins/datePicker 1674 * @author Kelvin Luck (http://www.kelvinluck.com/) 1675 * 1676 * @example $('#date-picker').datePicker(); 1677 * $('#date-picker').dpSetRenderCallback(function($td, thisDate, month, year) 1678 * { 1679 * // do stuff as each td is rendered dependant on the date in the td and the displayed month and year 1680 * }); 1681 * @desc Creates a date picker associated with the element with an id of date-picker and then creates a function which is called as each td is rendered when this date picker is displayed. 1682 **/ 1683 dpSetRenderCallback: function (a) { 1684 return _w.call(this, 'setRenderCallback', a); 1685 }, 1686 /** 1687 * Sets the position that the datePicker will pop up (relative to it's associated element) 1688 * 1689 * @param Number v The vertical alignment of the created date picker to it's associated element. Possible values are $.dpConst.POS_TOP and $.dpConst.POS_BOTTOM 1690 * @param Number h The horizontal alignment of the created date picker to it's associated element. Possible values are $.dpConst.POS_LEFT and $.dpConst.POS_RIGHT 1691 * @type jQuery 1692 * @name dpSetPosition 1693 * @cat plugins/datePicker 1694 * @author Kelvin Luck (http://www.kelvinluck.com/) 1695 * 1696 * @example $('#date-picker').datePicker(); 1697 * $('#date-picker').dpSetPosition($.dpConst.POS_BOTTOM, $.dpConst.POS_RIGHT);
1698 * @desc Creates a date picker associated with the element with an id of date-picker and makes it so that when this date picker pops up it will be bottom and right aligned to the #date-picker element. 1699 **/ 1700 dpSetPosition: function (v, h) { 1701 return _w.call(this, 'setPosition', v, h); 1702 }, 1703 /** 1704 * Sets the offset that the popped up date picker will have from it's default position relative to it's associated element (as set by dpSetPosition) 1705 * 1706 * @param Number v The vertical offset of the created date picker. 1707 * @param Number h The horizontal offset of the created date picker. 1708 * @type jQuery 1709 * @name dpSetOffset 1710 * @cat plugins/datePicker 1711 * @author Kelvin Luck (http://www.kelvinluck.com/) 1712 * 1713 * @example $('#date-picker').datePicker(); 1714 * $('#date-picker').dpSetOffset(-20, 200); 1715 * @desc Creates a date picker associated with the element with an id of date-picker and makes it so that when this date picker pops up it will be 20 pixels above and 200 pixels to the right of it's default position. 1716 **/ 1717 dpSetOffset: function (v, h) { 1718 return _w.call(this, 'setOffset', v, h); 1719 }, 1720 /** 1721 * Closes the open date picker associated with this element. 1722 * 1723 * @type jQuery 1724 * @name dpClose 1725 * @cat plugins/datePicker 1726 * @author Kelvin Luck (http://www.kelvinluck.com/) 1727 * 1728 * @example $('.date-pick') 1729 * .datePicker() 1730 * .on( 1731 * 'focus', 1732 * function() 1733 * { 1734 * $(this).dpDisplay(); 1735 * } 1736 * ).on( 1737 * 'blur', 1738 * function() 1739 * { 1740 * $(this).dpClose(); 1741 * } 1742 * ); 1743 **/ 1744 dpClose: function () { 1745 return _w.call(this, '_closeCalendar', false, this[0]); 1746 }, 1747 /** 1748 * Rerenders the date picker's current month (for use with inline calendars and renderCallbacks). 1749 * 1750 * @type jQuery 1751 * @name dpRerenderCalendar 1752 * @cat plugins/datePicker 1753 * @author Kelvin Luck (http://www.kelvinluck.com/) 1754 * 1755 **/ 1756 dpRerenderCalendar: function () { 1757 return _w.call(this, '_rerenderCalendar'); 1758 }, 1759 // private function called on unload to clean up any expandos etc and prevent memory links... 1760 _dpDestroy: function () { 1761 // TODO - implement this? 1762 } 1763 }); 1764 1765 // private internal function to cut down on the amount of code needed where we forward 1766 // dp* methods on the jQuery object on to the relevant DatePicker controllers... 1767 var _w = function (f, a1, a2, a3, a4) { 1768 return this.each( 1769 function () { 1770 var c = _getController(this); 1771 if (c) { 1772 c[f](a1, a2, a3, a4); 1773 } 1774 } 1775 ); 1776 }; 1777 1778 function DatePicker(ele) { 1779 this.ele = ele; 1780 1781 // initial values... 1782 this.displayedMonth = null; 1783 this.displayedYear = null; 1784 this.startDate = null; 1785 this.endDate = null; 1786 this.showYearNavigation = null; 1787 this.closeOnSelect = null; 1788 this.displayClose = null; 1789 this.rememberViewedMonth = null; 1790 this.selectMultiple = null; 1791 this.numSelectable = null; 1792 this.numSelected = null; 1793 this.verticalPosition = null; 1794 this.horizontalPosition = null; 1795 this.verticalOffset = null; 1796 this.horizontalOffset = null; 1797 this.button = null; 1798 this.renderCallback = []; 1799 this.selectedDates = {}; 1800 this.inline = null; 1801 this.context = '#dp-popup'; 1802 this.uiForm = null; 1803 this.settings = {}; 1804 }; 1805 $.extend( 1806 DatePicker.prototype, 1807 { 1808 init: function (s) { 1809 this.setStartDate(s.startDate); 1810 this.setEndDate(s.endDate); 1811 this.setDisplayedMonth(Number(s.month), Number(s.year)); 1812 this.setRenderCallback(s.renderCallback); 1813 this.showYearNavigation = s.showYearNavigation; 1814 this.closeOnSelect = s.closeOnSelect; 1815 this.displayClose = s.displayClose; 1816 this.rememberViewedMonth = s.rememberViewedMonth; 1817 this.selectMultiple = s.selectMultiple; 1818 this.selectableDate = s.selectableDate || false; 1819 this.numSelectable = s.selectMultiple ? s.numSelectable : 1; 1820 this.numSelected = 0; 1821 this.verticalPosition = s.verticalPosition; 1822 this.horizontalPosition = s.horizontalPosition; 1823 this.hoverClass = s.hoverClass; 1824 this.setOffset(s.verticalOffset, s.horizontalOffset); 1825 this.inline = s.inline; 1826 this.settings = s;
1827 if (this.inline) { 1828 this.context = this.ele; 1829 this.display(); 1830 } 1831 }, 1832 setStartDate: function (d) { 1833 if (d) { 1834 if (d instanceof Date) { 1835 this.startDate = d; 1836 } else { 1837 this.startDate = Date.fromString(d); 1838 } 1839 } 1840 if (!this.startDate) { 1841 this.startDate = (new Date()).zeroTime(); 1842 } 1843 this.setDisplayedMonth(this.displayedMonth, this.displayedYear); 1844 }, 1845 setEndDate: function (d) { 1846 if (d) { 1847 if (d instanceof Date) { 1848 this.endDate = d; 1849 } else { 1850 this.endDate = Date.fromString(d); 1851 } 1852 } 1853 if (!this.endDate) { 1854 this.endDate = (new Date('12/31/2999')); // using the JS Date.parse function which expects mm/dd/yyyy 1855 } 1856 if (this.endDate.getTime() < this.startDate.getTime()) { 1857 this.endDate = this.startDate; 1858 } 1859 this.setDisplayedMonth(this.displayedMonth, this.displayedYear); 1860 }, 1861 setPosition: function (v, h) { 1862 this.verticalPosition = v; 1863 this.horizontalPosition = h; 1864 }, 1865 setOffset: function (v, h) { 1866 this.verticalOffset = parseInt(v) || 0; 1867 this.horizontalOffset = parseInt(h) || 0; 1868 }, 1869 setDisabled: function (s) { 1870 $e = $(this.ele); 1871 $e[s ? 'addClass' : 'removeClass']('dp-disabled'); 1872 if (this.button) { 1873 $but = $(this.button); 1874 $but[s ? 'addClass' : 'removeClass']('dp-disabled'); 1875 $but.attr('title', s ? '' : $.dpText.TEXT_CHOOSE_DATE); 1876 } 1877 if ($e.is(':text')) { 1878 $e.attr('disabled', s ? 'disabled' : ''); 1879 } 1880 }, 1881 setDisplayedMonth: function (m, y, rerender) { 1882 if (this.startDate == undefined || this.endDate == undefined) { 1883 return; 1884 } 1885 var s = new Date(this.startDate.getTime()); 1886 s.setDate(1); 1887 var e = new Date(this.endDate.getTime()); 1888 e.setDate(1); 1889 1890 var t; 1891 if ((!m && !y) || (isNaN(m) && isNaN(y))) { 1892 // no month or year passed - default to current month 1893 t = new Date().zeroTime(); 1894 t.setDate(1); 1895 } else if (isNaN(m)) { 1896 // just year passed in - presume we want the displayedMonth 1897 t = new Date(y, this.displayedMonth, 1); 1898 } else if (isNaN(y)) { 1899 // just month passed in - presume we want the displayedYear 1900 t = new Date(this.displayedYear, m, 1); 1901 } else { 1902 // year and month passed in - that's the date we want! 1903 t = new Date(y, m, 1); 1904 } 1905 // check if the desired date is within the range of our defined startDate and endDate 1906 var oldMonth = this.displayedMonth, 1907 oldYear = this.displayedYear; 1908 this.displayedMonth = t.getMonth(); 1909 this.displayedYear = t.getFullYear(); 1910 1911 if (rerender && (this.displayedMonth != oldMonth || this.displayedYear != oldYear)) { 1912 this._rerenderCalendar(); 1913 $(this.ele).trigger('dpMonthChanged', [this.displayedMonth, this.displayedYear]); 1914 } 1915 }, 1916 setSelected: function (d, v, moveToMonth, dispatchEvents) { 1917 if (d < this.startDate || d.zeroTime() > this.endDate.zeroTime()) { 1918 // Don't allow people to select dates outside range... 1919 return; 1920 } 1921 var s = this.settings; 1922 if (s.selectWeek) { 1923 d = d.addDays(-(d.getDay() - Date.firstDayOfWeek + 7) % 7); 1924 if (d < this.startDate) // The first day of this week is before the start date so is unselectable... 1925 { 1926 return; 1927 } 1928 } 1929 if (v == this.isSelected(d)) // this date is already un/selected 1930 { 1931 return; 1932 } 1933 if (this.selectMultiple == false) { 1934 this.clearSelected(); 1935 } else if (v && this.numSelected == this.numSelectable) { 1936 // can't select any more dates... 1937 return; 1938 } 1939 if (moveToMonth && (this.displayedMonth != d.getMonth() || this.displayedYear != d.getFullYear())) { 1940 this.setDisplayedMonth(d.getMonth(), d.getFullYear(), true); 1941 } 1942 this.selectedDates[d.asString()] = v; 1943 this.numSelected += v ? 1 : -1;
1944 var selectorString = 'td.' + (d.getMonth() == this.displayedMonth ? 'current-month' : 'other-month'); 1945 var $td; 1946 $(selectorString, this.context).each( 1947 function () { 1948 if ($(this).data('datePickerDate') == d.asString()) { 1949 $td = $(this); 1950 if (s.selectWeek) { 1951 $td.parent()[v ? 'addClass' : 'removeClass']('selectedWeek'); 1952 } 1953 $td[v ? 'addClass' : 'removeClass']('selected'); 1954 } 1955 } 1956 ); 1957 $('td', this.context).not('.selected')[this.selectMultiple && this.numSelected == this.numSelectable ? 'addClass' : 'removeClass']('unselectable'); 1958 1959 if (dispatchEvents) { 1960 var s = this.isSelected(d); 1961 $e = $(this.ele); 1962 var dClone = Date.fromString(d.asString()); 1963 $e.trigger('dateSelected', [dClone, $td, s]); 1964 $e.trigger('change'); 1965 } 1966 }, 1967 isSelected: function (d) { 1968 return this.selectedDates[d.asString()]; 1969 }, 1970 getSelected: function () { 1971 var r = []; 1972 for (var s in this.selectedDates) { 1973 if (this.selectedDates[s] == true) { 1974 r.push(Date.fromString(s)); 1975 } 1976 } 1977 return r; 1978 }, 1979 clearSelected: function () { 1980 this.selectedDates = {}; 1981 this.numSelected = 0; 1982 $('td.selected', this.context).removeClass('selected').parent().removeClass('selectedWeek'); 1983 }, 1984 display: function (eleAlignTo) { 1985 if ($(this.ele).is('.dp-disabled')) return; 1986 1987 eleAlignTo = eleAlignTo || this.ele; 1988 var c = this; 1989 var $ele = $(eleAlignTo); 1990 var eleOffset = $ele.offset(); 1991 1992 var $createIn; 1993 var attrs; 1994 var attrsCalendarHolder; 1995 var cssRules; 1996 1997 if (c.inline) { 1998 $createIn = $(this.ele); 1999 attrs = { 2000 'id': 'calendar-' + this.ele._dpId, 2001 'class': 'dp-popup dp-popup-inline' 2002 }; 2003 2004 $('.dp-popup', $createIn).remove(); 2005 cssRules = { 2006 }; 2007 } else { 2008 $createIn = $('body'); 2009 attrs = { 2010 'id': 'dp-popup', 2011 'class': 'dp-popup' 2012 }; 2013 cssRules = { 2014 'top': eleOffset.top + c.verticalOffset, 2015 'left': eleOffset.left + c.horizontalOffset 2016 }; 2017 2018 var _checkMouse = function (e) { 2019 var el = e.target; 2020 var cal = $('#dp-popup')[0]; 2021 2022 while (true) { 2023 if (el == cal) { 2024 return true; 2025 } else if (el == document) { 2026 c._closeCalendar(); 2027 return false; 2028 } else { 2029 el = $(el).parent()[0]; 2030 } 2031 } 2032 }; 2033 this._checkMouse = _checkMouse; 2034 2035 c._closeCalendar(true); 2036 $(document).on( 2037 'keydown.datepicker', 2038 function (event) { 2039 if (event.keyCode == 27) { 2040 c._closeCalendar(); 2041 } 2042 } 2043 ); 2044 } 2045 2046 if (!c.rememberViewedMonth) { 2047 var selectedDate = this.getSelected()[0]; 2048 if (selectedDate) { 2049 selectedDate = new Date(selectedDate); 2050 this.setDisplayedMonth(selectedDate.getMonth(), selectedDate.getFullYear(), false); 2051 } 2052 } 2053 2054 $createIn 2055 .append( 2056 $('<div></div>') 2057 .attr(attrs) 2058 .css(cssRules) 2059 .append( 2060 $('<div class="dp-top"></div>').append( 2061 $('<h2></h2>'), 2062 $('<div class="dp-nav-prev"></div>') 2063 .append( 2064 $('<a class="dp-nav-prev-year" href="#" title="' + $.dpText.TEXT_PREV_YEAR + '"><<</a>') 2065 .on( 2066 'click', 2067 function () { 2068 return c._displayNewMonth.call(c, this, 0, -1);
2069 } 2070 ), 2071 $('<a class="dp-nav-prev-month" href="#" title="' + $.dpText.TEXT_PREV_MONTH + '"><</a>') 2072 .on( 2073 'click', 2074 function () { 2075 return c._displayNewMonth.call(c, this, -1, 0); 2076 } 2077 ) 2078 ), 2079 $('<div class="dp-nav-next"></div>') 2080 .append( 2081 $('<a class="dp-nav-next-year" href="#" title="' + $.dpText.TEXT_NEXT_YEAR + '">>></a>') 2082 .on( 2083 'click', 2084 function () { 2085 return c._displayNewMonth.call(c, this, 0, 1); 2086 } 2087 ), 2088 $('<a class="dp-nav-next-month" href="#" title="' + $.dpText.TEXT_NEXT_MONTH + '">></a>') 2089 .on( 2090 'click', 2091 function () { 2092 return c._displayNewMonth.call(c, this, 1, 0); 2093 } 2094 ) 2095 ) 2096 ), 2097 $('<div class="dp-calendar"></div>') 2098 ) 2099 .bgIframe() 2100 ); 2101 2102 var $pop = this.inline ? $('.dp-popup', this.context) : $('#dp-popup'); 2103 2104 if (this.showYearNavigation == false) { 2105 $('.dp-nav-prev-year, .dp-nav-next-year', c.context).css('display', 'none'); 2106 } 2107 if (this.displayClose) { 2108 $pop.append( 2109 $('<a href="#" id="dp-close">' + $.dpText.TEXT_CLOSE + '</a>') 2110 .on( 2111 'click', 2112 function () { 2113 c._closeCalendar(); 2114 return false; 2115 } 2116 ) 2117 ); 2118 } 2119 c._renderCalendar(); 2120 2121 $(this.ele).trigger('dpDisplayed', $pop); 2122 2123 if (!c.inline) { 2124 if (this.verticalPosition == $.dpConst.POS_BOTTOM) { 2125 $pop.css('top', eleOffset.top + $ele.height() - $pop.height() + c.verticalOffset); 2126 } 2127 if (this.horizontalPosition == $.dpConst.POS_RIGHT) { 2128 $pop.css('left', eleOffset.left + $ele.width() - $pop.width() + c.horizontalOffset); 2129 } 2130 // $('.selectee', this.context).focus(); 2131 $(document).on('mousedown.datepicker', this._checkMouse); 2132 } 2133 2134 }, 2135 setRenderCallback: function (a) { 2136 if (a == null) return; 2137 if (a && typeof (a) == 'function') { 2138 a = [a]; 2139 } 2140 this.renderCallback = this.renderCallback.concat(a); 2141 }, 2142 cellRender: function ($td, thisDate, month, year) { 2143 var c = this.dpController; 2144 var d = new Date(thisDate.getTime()); 2145 2146 // add our click handlers to deal with it when the days are clicked... 2147 2148 $td.on( 2149 'click', 2150 function () { 2151 var $this = $(this); 2152 if (!$this.is('.disabled')) { 2153 c.setSelected(d, !$this.is('.selected') || !c.selectMultiple, false, true); 2154 if (c.closeOnSelect) { 2155 // Focus the next input in the form⦠2156 if (c.settings.autoFocusNextInput) { 2157 var ele = c.ele; 2158 var found = false; 2159 $(':input', ele.form).each( 2160 function () { 2161 if (found) { 2162 $(this).focus(); 2163 return false; 2164 } 2165 if (this == ele) { 2166 found = true; 2167 } 2168 } 2169 ); 2170 } else { 2171 try { 2172 c.ele.focus(); 2173 } catch (e) { } 2174 } 2175 c._closeCalendar(); 2176 } 2177 } 2178 } 2179 ); 2180 if (c.isSelected(d)) { 2181 $td.addClass('selected'); 2182 if (c.settings.selectWeek) { 2183 $td.parent().addClass('selectedWeek'); 2184 } 2185 } else if (c.selectMultiple && c.numSelected == c.numSelectable) { 2186 $td.addClass('unselectable'); 2187 } 2188 2189 }, 2190 _applyRenderCallbacks: function () { 2191 var c = this; 2192 $('td', this.context).each( 2193 function () { 2194 for (var i = 0; i < c.renderCallback.length; i++) { 2195 $td = $(this); 2196 c.renderCallback[i].apply(this, [$td, Date.fromString($td.data('datePickerDate')), c.displayedMonth, c.displayedYear]); 2197 } 2198 } 2199 ); 2200 return; 2201 }, 2202 // ele is the clicked button - only proceed if it doesn't have the class disabled...
2203 // m and y are -1, 0 or 1 depending which direction we want to go in... 2204 _displayNewMonth: function (ele, m, y) { 2205 if (!$(ele).is('.disabled')) { 2206 2207 this.setDisplayedMonth(this.displayedMonth + m, this.displayedYear + y, true); 2208 2209 /*if ( this.selectableDate && this.uiForm) { 2210 this.uiForm.update({ 2211 m: this.displayedMonth, 2212 y: this.displayedYear 2213 }); 2214 };*/ 2215 } 2216 ele.blur(); 2217 return false; 2218 }, 2219 _displayNewDate: function (m, y) { 2220 this.setDisplayedMonth(m, y, true); 2221 2222 return false; 2223 }, 2224 _rerenderCalendar: function () { 2225 this._clearCalendar(); 2226 this._renderCalendar(); 2227 if (this.selectableDate && this.uiForm) { 2228 this._renderDateSelect(); 2229 } 2230 }, 2231 _renderDateSelect: function (title, displayedMonth, displayedYear) { 2232 var that = this, 2233 thisYear = (new Date()).getFullYear(), 2234 thisMonth = (new Date()).getMonth(), 2235 startYear = thisYear - 3, 2236 endYear = thisYear + 2, 2237 startMonth = 0, 2238 endMonth = 11; 2239 2240 function getMY(d) { 2241 2242 var d = Date.fromString(d, that.settings.dateFormat); 2243 2244 return [d.getFullYear(), d.getMonth()]; 2245 } 2246 2247 if (this.settings.isLimited) { 2248 2249 var start = getMY(this.settings.isLimited[0]), 2250 end = getMY(this.settings.isLimited[1]); 2251 2252 startYear = start[0]; 2253 endYear = end[0]; 2254 if (startYear == endYear) { 2255 startMonth = start[1]; 2256 endMonth = end[1]; 2257 } 2258 } 2259 2260 if ((!title && this.uiForm) || title.hasClass('dp-active')) { 2261 this.uiForm.update({ 2262 m: this.displayedMonth, 2263 mm: Date.monthNames[this.displayedMonth], 2264 y: this.displayedYear 2265 }); 2266 return that; 2267 } 2268 2269 var uiForm = $('<div class="ui-form" style="display: flex; gap: 5px;"><div class="form-element w-60"><select class="select-month small"></select></div><div class="form-element"><select class="select-year small"></select></div></div>') 2270 selectYear = $('select.select-year', uiForm), 2271 selectMonth = $('select.select-month', uiForm); 2272 2273 for (var i = startMonth; i <= endMonth; i++) { 2274 var option = $('<option>').attr('value', i).html(Date.monthNames[i]); 2275 if (i == displayedMonth) { 2276 option.attr("selected", "selected").prop("selected", true); 2277 } 2278 option.appendTo(selectMonth); 2279 }; 2280 for (var i = endYear; i > startYear - 1; i--) { 2281 var option = $('<option>').attr('value', i).text(i); 2282 if (i == displayedYear) { 2283 option.attr("selected", "selected").prop("selected", true); 2284 } 2285 option.appendTo(selectYear); 2286 }; 2287 2288 $('<div>').addClass('form-element f-none'); 2289 2290 uiForm.appendTo(title); 2291 2292 title.addClass('dp-active'); 2293 2294 this.uiForm = $('.ui-form', title); 2295 this.uiForm.update = function (values) { 2296 $('select.select-year', this).selectbox('change', values.y.toString(), values.y); // val(values.y); 2297 $('select.select-month', this).selectbox('change', values.m.toString(), values.mm); // val(values.m); 2298 }; 2299 $('select', title).addClass('auto-list-width').selectbox().on("change", function (e) { 2300 2301 e.preventDefault(); 2302 2303 var m = $('option', $('select.select-month', that.uiForm)).filter(':selected').val(), 2304 y = $('option', $('select.select-year', that.uiForm)).filter(':selected').val(); 2305 2306 that._displayNewDate(m, y); 2307 }); 2308 }, 2309 _renderCalendar: function () { 2310 // set the title... 2311 if (!this.selectableDate) { 2312 $('h2', this.context).html((new Date(this.displayedYear, this.displayedMonth, 1)).asString($.dpText.HEADER_FORMAT)); 2313 } else { 2314 var title = $('h2', this.context); 2315 this._renderDateSelect.call(this, title, this.displayedMonth, this.displayedYear); 2316 } 2317 2318 // render the calendar... 2319 $('.dp-calendar', this.context).renderCalendar( 2320 $.extend( 2321 {}, 2322 this.settings, 2323 { 2324 month: this.displayedMonth, 2325 year: this.displayedYear, 2326 renderCallback: this.cellRender, 2327 dpController: this, 2328 hoverClass: this.hoverClass 2329 }) 2330 ); 2331 2332 // update the status of the control buttons and disable dates before startDate or after endDate... 2333 // TODO: When should the year buttons be disabled? When you can't go forward a whole year from where you are or is that annoying? 2334 if (this.displayedYear == this.startDate.getFullYear() && this.displayedMonth == this.startDate.getMonth()) { 2335 $('.dp-nav-prev-year', this.context).addClass('disabled'); 2336 $('.dp-nav-prev-month', this.context).addClass('disabled'); 2337 $('.dp-calendar td.other-month', this.context).each( 2338 function () { 2339 var $this = $(this); 2340 if (Number($this.text()) > 20) { 2341 $this.addClass('disabled'); 2342 } 2343 } 2344 ); 2345 var d = this.startDate.getDate(); 2346 $('.dp-calendar td.current-month', this.context).each( 2347 function () { 2348 var $this = $(this); 2349 if (Number($this.text()) < d) { 2350 $this.addClass('disabled'); 2351 } 2352 } 2353 ); 2354 } else { 2355 $('.dp-nav-prev-year', this.context).removeClass('disabled'); 2356 $('.dp-nav-prev-month', this.context).removeClass('disabled'); 2357 var d = this.startDate.getDate(); 2358 if (d > 20) { 2359 // check if the startDate is last month as we might need to add some disabled classes... 2360 var st = this.startDate.getTime(); 2361 var sd = new Date(st); 2362 sd.addMonths(1); 2363 if (this.displayedYear == sd.getFullYear() && this.displayedMonth == sd.getMonth()) { 2364 $('.dp-calendar td.other-month', this.context).each( 2365 function () { 2366 var $this = $(this); 2367 if (Date.fromString($this.data('datePickerDate')).getTime() < st) { 2368 $this.addClass('disabled'); 2369 } 2370 } 2371 ); 2372 } 2373 } 2374 } 2375 if (this.displayedYear == this.endDate.getFullYear() && this.displayedMonth == this.endDate.getMonth()) { 2376 $('.dp-nav-next-year', this.context).addClass('disabled'); 2377 $('.dp-nav-next-month', this.context).addClass('disabled'); 2378 $('.dp-calendar td.other-month', this.context).each( 2379 function () { 2380 var $this = $(this); 2381 if (Number($this.text()) < 14) { 2382 $this.addClass('disabled'); 2383 } 2384 } 2385 ); 2386 var d = this.endDate.getDate(); 2387 $('.dp-calendar td.current-month', this.context).each( 2388 function () { 2389 var $this = $(this); 2390 if (Number($this.text()) > d) { 2391 $this.addClass('disabled'); 2392 } 2393 } 2394 ); 2395 } else { 2396 $('.dp-nav-next-year', this.context).removeClass('disabled'); 2397 $('.dp-nav-next-month', this.context).removeClass('disabled'); 2398 var d = this.endDate.getDate(); 2399 if (d < 13) { 2400 // check if the endDate is next month as we might need to add some disabled classes... 2401 var ed = new Date(this.endDate.getTime()); 2402 ed.addMonths(-1); 2403 if (this.displayedYear == ed.getFullYear() && this.displayedMonth == ed.getMonth()) { 2404 $('.dp-calendar td.other-month', this.context).each( 2405 function () { 2406 var $this = $(this); 2407 var cellDay = Number($this.text()); 2408 if (cellDay < 13 && cellDay > d) { 2409 $this.addClass('disabled'); 2410 } 2411 } 2412 ); 2413 } 2414 } 2415 } 2416 this._applyRenderCallbacks(); 2417 }, 2418 _closeCalendar: function (programatic, ele) { 2419 if (!ele || ele == this.ele) { 2420 $(document).off('mousedown.datepicker'); 2421 $(document).off('keydown.datepicker');
2422 this._clearCalendar(); 2423 $('#dp-popup a').off(); 2424 $('#dp-popup').empty().remove(); 2425 if (!programatic) { 2426 $(this.ele).trigger('dpClosed', [this.getSelected()]); 2427 } 2428 } 2429 }, 2430 // empties the current dp-calendar div and makes sure that all events are unbound 2431 // and expandos removed to avoid memory leaks... 2432 _clearCalendar: function () { 2433 // TODO. 2434 $('.dp-calendar td', this.context).off(); 2435 $('.dp-calendar', this.context).empty(); 2436 } 2437 } 2438 ); 2439 2440 // static constants 2441 $.dpConst = { 2442 SHOW_HEADER_NONE: 0, 2443 SHOW_HEADER_SHORT: 1, 2444 SHOW_HEADER_LONG: 2, 2445 POS_TOP: 0, 2446 POS_BOTTOM: 1, 2447 POS_LEFT: 0, 2448 POS_RIGHT: 1, 2449 DP_INTERNAL_FOCUS: 'dpInternalFocusTrigger' 2450 }; 2451 // localisable text 2452 $.dpText = { 2453 TEXT_PREV_YEAR: Date.dpText.TEXT_PREV_YEAR || 'Previous year', 2454 TEXT_PREV_MONTH: Date.dpText.TEXT_PREV_MONTH || 'Previous month', 2455 TEXT_NEXT_YEAR: Date.dpText.TEXT_NEXT_YEAR || 'Next year', 2456 TEXT_NEXT_MONTH: Date.dpText.TEXT_NEXT_MONTH || 'Next month', 2457 TEXT_CLOSE: Date.dpText.TEXT_CLOSE || 'Close', 2458 TEXT_CHOOSE_DATE: Date.dpText.TEXT_CHOOSE_DATE || 'Choose date', 2459 HEADER_FORMAT: Date.dpText.HEADER_FORMAT || 'mmmm yyyy' 2460 }; 2461 // version 2462 $.dpVersion = '$Id$'; 2463 2464 $.fn.datePicker.defaults = { 2465 month: undefined, 2466 year: undefined, 2467 showHeader: $.dpConst.SHOW_HEADER_SHORT, 2468 startDate: undefined, 2469 endDate: undefined, 2470 inline: false, 2471 renderCallback: null, 2472 createButton: true, 2473 showYearNavigation: true, 2474 closeOnSelect: true, 2475 displayClose: false, 2476 selectMultiple: false, 2477 numSelectable: Number.MAX_VALUE, 2478 clickInput: false, 2479 rememberViewedMonth: true, 2480 selectWeek: false, 2481 selectableDate: false, 2482 verticalPosition: $.dpConst.POS_TOP, 2483 horizontalPosition: $.dpConst.POS_LEFT, 2484 verticalOffset: 0, 2485 horizontalOffset: 0, 2486 hoverClass: 'dp-hover', 2487 autoFocusNextInput: false 2488 }; 2489 2490 function _getController(ele) { 2491 if (ele._dpId) return $.event._dpCache[ele._dpId]; 2492 return false; 2493 }; 2494 2495 // make it so that no error is thrown if bgIframe plugin isn't included (allows you to use conditional 2496 // comments to only include bgIframe where it is needed in IE without breaking this plugin). 2497 if ($.fn.bgIframe == undefined) { 2498 $.fn.bgIframe = function () { return this; }; 2499 }; 2500 2501 2502 // clean-up 2503 $(window) 2504 .on('unload', function () { 2505 var els = $.event._dpCache || []; 2506 for (var i in els) { 2507 $(els[i].ele)._dpDestroy(); 2508 } 2509 }); 2510 2511})(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.