1 2 3(function($, undefined){ 4 5 function UTCDate(){ 6 return new Date(Date.UTC.apply(Date, arguments)); 7 } 8 function UTCToday(){ 9 var today = new Date(); 10 return UTCDate(today.getFullYear(), today.getMonth(), today.getDate()); 11 } 12 function isUTCEquals(date1, date2) { 13 return ( 14 date1.getUTCFullYear() === date2.getUTCFullYear() && 15 date1.getUTCMonth() === date2.getUTCMonth() && 16 date1.getUTCDate() === date2.getUTCDate() 17 ); 18 } 19 function alias(method){ 20 return function(){ 21 return this[method].apply(this, arguments); 22 }; 23 } 24 25 var DateArray = (function(){ 26 var extras = { 27 get: function(i){ 28 return this.slice(i)[0]; 29 }, 30 contains: function(d){ 31 // Array.indexOf is not cross-browser; 32 // $.inArray doesn't work with Dates 33 var val = d && d.valueOf(); 34 for (var i=0, l=this.length; i < l; i++) 35 if (this[i].valueOf() === val) 36 return i; 37 return -1; 38 }, 39 remove: function(i){ 40 this.splice(i,1); 41 }, 42 replace: function(new_array){ 43 if (!new_array) 44 return; 45 if (!$.isArray(new_array)) 46 new_array = [new_array]; 47 this.clear(); 48 this.push.apply(this, new_array); 49 }, 50 clear: function(){ 51 this.length = 0; 52 }, 53 copy: function(){ 54 var a = new DateArray(); 55 a.replace(this); 56 return a; 57 } 58 }; 59 60 return function(){ 61 var a = []; 62 a.push.apply(a, arguments); 63 $.extend(a, extras); 64 return a; 65 }; 66 })(); 67 68 69 // Picker object 70 71 var Datepicker = function(element, options){ 72 this._process_options(options); 73 74 this.dates = new DateArray(); 75 this.viewDate = this.o.defaultViewDate; 76 this.focusDate = null; 77 78 this.element = $(element); 79 this.isInline = false; 80 this.isInput = this.element.is('input'); 81 this.component = this.element.hasClass('date') ? this.element.find('.add-on, .input-group-addon, .btn') : false; 82 this.hasInput = this.component && this.element.find('input').length; 83 if (this.component && this.component.length === 0) 84 this.component = false; 85 86 this.picker = $(DPGlobal.template); 87 this._buildEvents(); 88 this._attachEvents(); 89 90 if (this.isInline){ 91 this.picker.addClass('datepicker-inline').appendTo(this.element); 92 } 93 else { 94 this.picker.addClass('datepicker-dropdown dropdown-menu'); 95 } 96 97 if (this.o.rtl){ 98 this.picker.addClass('datepicker-rtl'); 99 } 100 101 this.viewMode = this.o.startView; 102 103 if (this.o.calendarWeeks) 104 this.picker.find('tfoot .today, tfoot .clear') 105 .attr('colspan', function(i, val){ 106 return parseInt(val) + 1; 107 }); 108 109 this._allow_update = false; 110 111 this.setStartDate(this._o.startDate); 112 this.setEndDate(this._o.endDate); 113 this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled); 114 this.setDatesDisabled(this.o.datesDisabled); 115 116 this.fillDow(); 117 this.fillMonths(); 118 119 this._allow_update = true; 120 121 this.update(); 122 this.showMode(); 123 124 if (this.isInline){ 125 this.show(); 126 } 127 }; 128 129 Datepicker.prototype = { 130 constructor: Datepicker, 131 132 _process_options: function(opts){ 133 // Store raw options for reference 134 this._o = $.extend({}, this._o, opts); 135 // Processed options 136 var o = this.o = $.extend({}, this._o); 137 138 // Check if "de-DE" style date is available, if not language should 139 // fallback to 2 letter code eg "de" 140 var lang = o.language; 141 if (!dates[lang]){ 142 lang = lang.split('-')[0]; 143 if (!dates[lang]) 144 lang = defaults.language; 145 } 146 o.language = lang; 147 148 switch (o.startView){ 149 case 2: 150 case 'decade': 151 o.startView = 2; 152 break; 153 case 1: 154 case 'year': 155 o.startView = 1; 156 break; 157 default: 158 o.startView = 0; 159 } 160 161 switch (o.minViewMode){ 162 case 1: 163 case 'months': 164 o.minViewMode = 1; 165 break; 166 case 2: 167 case 'years': 168 o.minViewMode = 2; 169 break; 170 default: 171 o.minViewMode = 0; 172 } 173 174 o.startView = Math.max(o.startView, o.minViewMode); 175 176 // true, false, or Number > 0 177 if (o.multidate !== true){ 178 o.multidate = Number(o.multidate) || false; 179 if (o.multidate !== false) 180 o.multidate = Math.max(0, o.multidate); 181 } 182 o.multidateSeparator = String(o.multidateSeparator); 183 184 o.weekStart %= 7; 185 o.weekEnd = ((o.weekStart + 6) % 7); 186 187 var format = DPGlobal.parseFormat(o.format); 188 if (o.startDate !== -Infinity){ 189 if (!!o.startDate){ 190 if (o.startDate instanceof Date) 191 o.startDate = this._local_to_utc(this._zero_time(o.startDate)); 192 else 193 o.startDate = DPGlobal.parseDate(o.startDate, format, o.language); 194 } 195 else { 196 o.startDate = -Infinity; 197 } 198 } 199 if (o.endDate !== Infinity){ 200 if (!!o.endDate){ 201 if (o.endDate instanceof Date) 202 o.endDate = this._local_to_utc(this._zero_time(o.endDate)); 203 else 204 o.endDate = DPGlobal.parseDate(o.endDate, format, o.language); 205 } 206 else { 207 o.endDate = Infinity; 208 } 209 } 210 211 o.daysOfWeekDisabled = o.daysOfWeekDisabled||[]; 212 if (!$.isArray(o.daysOfWeekDisabled)) 213 o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/); 214 o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function(d){ 215 return parseInt(d, 10); 216 }); 217 218 o.datesDisabled = o.datesDisabled||[]; 219 if (!$.isArray(o.datesDisabled)) { 220 var datesDisabled = []; 221 datesDisabled.push(DPGlobal.parseDate(o.datesDisabled, format, o.language)); 222 o.datesDisabled = datesDisabled; 223 } 224 o.datesDisabled = $.map(o.datesDisabled,function(d){ 225 return DPGlobal.parseDate(d, format, o.language); 226 }); 227 228 var plc = String(o.orientation).toLowerCase().split(/\s+/g), 229 _plc = o.orientation.toLowerCase(); 230 plc = $.grep(plc, function(word){ 231 return /^auto|left|right|top|bottom$/.test(word); 232 }); 233 o.orientation = {x: 'auto', y: 'auto'}; 234 if (!_plc || _plc === 'auto') 235 ; // no action 236 else if (plc.length === 1){ 237 switch (plc[0]){ 238 case 'top': 239 case 'bottom': 240 o.orientation.y = plc[0]; 241 break; 242 case 'left': 243 case 'right': 244 o.orientation.x = plc[0]; 245 break; 246 } 247 } 248 else { 249 _plc = $.grep(plc, function(word){ 250 return /^left|right$/.test(word); 251 }); 252 o.orientation.x = _plc[0] || 'auto'; 253 254 _plc = $.grep(plc, function(word){ 255 return /^top|bottom$/.test(word); 256 }); 257 o.orientation.y = _plc[0] || 'auto'; 258 } 259 if (o.defaultViewDate) { 260 var year = o.defaultViewDate.year || new Date().getFullYear();
261 var month = o.defaultViewDate.month || 0; 262 var day = o.defaultViewDate.day || 1; 263 o.defaultViewDate = UTCDate(year, month, day); 264 } else { 265 o.defaultViewDate = UTCToday(); 266 } 267 o.showOnFocus = o.showOnFocus !== undefined ? o.showOnFocus : true; 268 }, 269 _events: [], 270 _secondaryEvents: [], 271 _applyEvents: function(evs){ 272 for (var i=0, el, ch, ev; i < evs.length; i++){ 273 el = evs[i][0]; 274 if (evs[i].length === 2){ 275 ch = undefined; 276 ev = evs[i][1]; 277 } 278 else if (evs[i].length === 3){ 279 ch = evs[i][1]; 280 ev = evs[i][2]; 281 } 282 el.on(ev, ch); 283 } 284 }, 285 _unapplyEvents: function(evs){ 286 for (var i=0, el, ev, ch; i < evs.length; i++){ 287 el = evs[i][0]; 288 if (evs[i].length === 2){ 289 ch = undefined; 290 ev = evs[i][1]; 291 } 292 else if (evs[i].length === 3){ 293 ch = evs[i][1]; 294 ev = evs[i][2]; 295 } 296 el.off(ev, ch); 297 } 298 }, 299 _buildEvents: function(){ 300 var events = { 301 keyup: $.proxy(function(e){ 302 if ($.inArray(e.keyCode, [27, 37, 39, 38, 40, 32, 13, 9]) === -1) 303 this.update(); 304 }, this), 305 keydown: $.proxy(this.keydown, this), 306 paste: $.proxy(this.paste, this) 307 }; 308 309 if (this.o.showOnFocus === true) { 310 events.focus = $.proxy(this.show, this); 311 } 312 313 if (this.isInput) { // single input 314 this._events = [ 315 [this.element, events] 316 ]; 317 } 318 else if (this.component && this.hasInput) { // component: input + button 319 this._events = [ 320 // For components that are not readonly, allow keyboard nav 321 [this.element.find('input'), events], 322 [this.component, { 323 click: $.proxy(this.show, this) 324 }] 325 ]; 326 } 327 else if (this.element.is('div')){ // inline datepicker 328 this.isInline = true; 329 } 330 else { 331 this._events = [ 332 [this.element, { 333 click: $.proxy(this.show, this) 334 }] 335 ]; 336 } 337 this._events.push( 338 // Component: listen for blur on element descendants 339 [this.element, '*', { 340 blur: $.proxy(function(e){ 341 this._focused_from = e.target; 342 }, this) 343 }], 344 // Input: listen for blur on element 345 [this.element, { 346 blur: $.proxy(function(e){ 347 this._focused_from = e.target; 348 }, this) 349 }] 350 ); 351 352 this._secondaryEvents = [ 353 [this.picker, { 354 click: $.proxy(this.click, this) 355 }], 356 [$(window), { 357 resize: $.proxy(this.place, this) 358 }], 359 [$(document), { 360 mousedown: $.proxy(function(e){ 361 // Clicked outside the datepicker, hide it 362 if (!( 363 this.element.is(e.target) || 364 this.element.find(e.target).length || 365 this.picker.is(e.target) || 366 this.picker.find(e.target).length 367 )){ 368 this.hide(); 369 } 370 }, this) 371 }] 372 ]; 373 }, 374 _attachEvents: function(){ 375 this._detachEvents(); 376 this._applyEvents(this._events); 377 }, 378 _detachEvents: function(){ 379 this._unapplyEvents(this._events); 380 }, 381 _attachSecondaryEvents: function(){ 382 this._detachSecondaryEvents(); 383 this._applyEvents(this._secondaryEvents); 384 }, 385 _detachSecondaryEvents: function(){ 386 this._unapplyEvents(this._secondaryEvents); 387 }, 388 _trigger: function(event, altdate){ 389 var date = altdate || this.dates.get(-1), 390 local_date = this._utc_to_local(date); 391 392 this.element.trigger({ 393 type: event, 394 date: local_date, 395 dates: $.map(this.dates, this._utc_to_local), 396 format: $.proxy(function(ix, format){ 397 if (arguments.length === 0){ 398 ix = this.dates.length - 1; 399 format = this.o.format; 400 } 401 else if (typeof ix === 'string'){ 402 format = ix; 403 ix = this.dates.length - 1; 404 } 405 format = format || this.o.format; 406 var date = this.dates.get(ix); 407 return DPGlobal.formatDate(date, format, this.o.language); 408 }, this) 409 }); 410 }, 411 412 show: function(){ 413 if (this.element.attr('readonly') && this.o.enableOnReadonly === false) 414 return; 415 if (!this.isInline) 416 this.picker.appendTo(this.o.container); 417 this.place(); 418 this.picker.show(); 419 this._attachSecondaryEvents(); 420 this._trigger('show'); 421 if ((window.navigator.msMaxTouchPoints || 'ontouchstart' in document) && this.o.disableTouchKeyboard) { 422 $(this.element).blur(); 423 } 424 return this; 425 }, 426 427 hide: function(){ 428 if (this.isInline) 429 return this; 430 if (!this.picker.is(':visible')) 431 return this; 432 this.focusDate = null; 433 this.picker.hide().detach(); 434 this._detachSecondaryEvents(); 435 this.viewMode = this.o.startView; 436 this.showMode(); 437 438 if ( 439 this.o.forceParse && 440 ( 441 this.isInput && this.element.val() || 442 this.hasInput && this.element.find('input').val() 443 ) 444 ) 445 this.setValue(); 446 this._trigger('hide'); 447 return this; 448 }, 449 450 remove: function(){ 451 this.hide(); 452 this._detachEvents(); 453 this._detachSecondaryEvents(); 454 this.picker.remove(); 455 delete this.element.data().datepicker; 456 if (!this.isInput){ 457 delete this.element.data().date; 458 } 459 return this; 460 }, 461 462 paste: function(evt){ 463 var dateString; 464 if (evt.originalEvent.clipboardData && evt.originalEvent.clipboardData.types 465 && $.inArray('text/plain', evt.originalEvent.clipboardData.types) !== -1) { 466 dateString = evt.originalEvent.clipboardData.getData('text/plain'); 467 } 468 else if (window.clipboardData) { 469 dateString = window.clipboardData.getData('Text'); 470 } 471 else { 472 return; 473 } 474 this.setDate(dateString); 475 this.update(); 476 evt.preventDefault(); 477 }, 478 479 _utc_to_local: function(utc){ 480 return utc && new Date(utc.getTime() + (utc.getTimezoneOffset()*60000)); 481 }, 482 _local_to_utc: function(local){ 483 return local && new Date(local.getTime() - (local.getTimezoneOffset()*60000)); 484 }, 485 _zero_time: function(local){ 486 return local && new Date(local.getFullYear(), local.getMonth(), local.getDate()); 487 }, 488 _zero_utc_time: function(utc){ 489 return utc && new Date(Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate())); 490 }, 491 492 getDates: function(){ 493 return $.map(this.dates, this._utc_to_local); 494 }, 495 496 getUTCDates: function(){ 497 return $.map(this.dates, function(d){ 498 return new Date(d); 499 }); 500 }, 501 502 getDate: function(){ 503 return this._utc_to_local(this.getUTCDate()); 504 }, 505 506 getUTCDate: function(){ 507 var selected_date = this.dates.get(-1); 508 if (typeof selected_date !== 'undefined') { 509 return new Date(selected_date); 510 } else { 511 return null; 512 } 513 }, 514 515 clearDates: function(){ 516 var element; 517 if (this.isInput) { 518 element = this.element; 519 } else if (this.component) { 520 element = this.element.find('input'); 521 } 522 523 if (element) { 524 element.val('').change(); 525 } 526 527 this.update(); 528 this._trigger('changeDate'); 529 530 if (this.o.autoclose) { 531 this.hide(); 532 } 533 }, 534 setDates: function(){ 535 var args = $.isArray(arguments[0]) ? arguments[0] : arguments; 536 this.update.apply(this, args); 537 this._trigger('changeDate'); 538 this.setValue(); 539 return this; 540 }, 541 542 setUTCDates: function(){ 543 var args = $.isArray(arguments[0]) ? arguments[0] : arguments; 544 this.update.apply(this, $.map(args, this._utc_to_local)); 545 this._trigger('changeDate'); 546 this.setValue(); 547 return this; 548 }, 549 550 setDate: alias('setDates'), 551 setUTCDate: alias('setUTCDates'), 552 553 setValue: function(){ 554 var formatted = this.getFormattedDate(); 555 if (!this.isInput){ 556 if (this.component){ 557 this.element.find('input').val(formatted).change(); 558 } 559 } 560 else { 561 this.element.val(formatted).change(); 562 } 563 return this; 564 }, 565 566 getFormattedDate: function(format){ 567 if (format === undefined) 568 format = this.o.format; 569 570 var lang = this.o.language; 571 return $.map(this.dates, function(d){ 572 return DPGlobal.formatDate(d, format, lang); 573 }).join(this.o.multidateSeparator); 574 }, 575 576 setStartDate: function(startDate){ 577 this._process_options({startDate: startDate}); 578 this.update(); 579 this.updateNavArrows(); 580 return this; 581 }, 582
583 setEndDate: function(endDate){ 584 this._process_options({endDate: endDate}); 585 this.update(); 586 this.updateNavArrows(); 587 return this; 588 }, 589 590 setDaysOfWeekDisabled: function(daysOfWeekDisabled){ 591 this._process_options({daysOfWeekDisabled: daysOfWeekDisabled}); 592 this.update(); 593 this.updateNavArrows(); 594 return this; 595 }, 596 597 setDatesDisabled: function(datesDisabled){ 598 this._process_options({datesDisabled: datesDisabled}); 599 this.update(); 600 this.updateNavArrows(); 601 }, 602 603 place: function(){ 604 if (this.isInline) 605 return this; 606 var calendarWidth = this.picker.outerWidth(), 607 calendarHeight = this.picker.outerHeight(), 608 visualPadding = 10, 609 windowWidth = $(this.o.container).width(), 610 windowHeight = $(this.o.container).height(), 611 scrollTop = $(this.o.container).scrollTop(), 612 appendOffset = $(this.o.container).offset(); 613 614 var parentsZindex = []; 615 this.element.parents().each(function(){ 616 var itemZIndex = $(this).css('z-index'); 617 if (itemZIndex !== 'auto' && itemZIndex !== 0) parentsZindex.push(parseInt(itemZIndex)); 618 }); 619 var zIndex = Math.max.apply(Math, parentsZindex) + 10; 620 var offset = this.component ? this.component.parent().offset() : this.element.offset(); 621 var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false); 622 var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false); 623 var left = offset.left - appendOffset.left, 624 top = offset.top - appendOffset.top; 625 626 this.picker.removeClass( 627 'datepicker-orient-top datepicker-orient-bottom '+ 628 'datepicker-orient-right datepicker-orient-left' 629 ); 630 631 if (this.o.orientation.x !== 'auto'){ 632 this.picker.addClass('datepicker-orient-' + this.o.orientation.x); 633 if (this.o.orientation.x === 'right') 634 left -= calendarWidth - width; 635 } 636 // auto x orientation is best-placement: if it crosses a window 637 // edge, fudge it sideways 638 else { 639 if (offset.left < 0) { 640 // component is outside the window on the left side. Move it into visible range 641 this.picker.addClass('datepicker-orient-left'); 642 left -= offset.left - visualPadding; 643 } else if (left + calendarWidth > windowWidth) { 644 // the calendar passes the widow right edge. Align it to component right side 645 this.picker.addClass('datepicker-orient-right'); 646 left = offset.left + width - calendarWidth; 647 } else { 648 // Default to left 649 this.picker.addClass('datepicker-orient-left'); 650 } 651 } 652 653 // auto y orientation is best-situation: top or bottom, no fudging, 654 // decision based on which shows more of the calendar 655 var yorient = this.o.orientation.y, 656 top_overflow, bottom_overflow; 657 if (yorient === 'auto'){ 658 top_overflow = -scrollTop + top - calendarHeight; 659 bottom_overflow = scrollTop + windowHeight - (top + height + calendarHeight); 660 if (Math.max(top_overflow, bottom_overflow) === bottom_overflow) 661 yorient = 'top'; 662 else 663 yorient = 'bottom'; 664 } 665 this.picker.addClass('datepicker-orient-' + yorient); 666 if (yorient === 'top') 667 top += height; 668 else 669 top -= calendarHeight + parseInt(this.picker.css('padding-top')); 670 671 if (this.o.rtl) { 672 var right = windowWidth - (left + width); 673 this.picker.css({ 674 top: top, 675 right: right, 676 zIndex: zIndex 677 }); 678 } else { 679 this.picker.css({ 680 top: top, 681 left: left, 682 zIndex: zIndex 683 }); 684 } 685 return this; 686 }, 687 688 _allow_update: true, 689 update: function(){ 690 if (!this._allow_update) 691 return this; 692 693 var oldDates = this.dates.copy(), 694 dates = [], 695 fromArgs = false; 696 if (arguments.length){ 697 $.each(arguments, $.proxy(function(i, date){ 698 if (date instanceof Date) 699 date = this._local_to_utc(date); 700 dates.push(date); 701 }, this)); 702 fromArgs = true; 703 } 704 else { 705 dates = this.isInput 706 ? this.element.val() 707 : this.element.data('date') || this.element.find('input').val(); 708 if (dates && this.o.multidate) 709 dates = dates.split(this.o.multidateSeparator); 710 else 711 dates = [dates]; 712 delete this.element.data().date; 713 } 714 715 dates = $.map(dates, $.proxy(function(date){ 716 return DPGlobal.parseDate(date, this.o.format, this.o.language); 717 }, this)); 718 dates = $.grep(dates, $.proxy(function(date){ 719 return ( 720 date < this.o.startDate || 721 date > this.o.endDate || 722 !date 723 ); 724 }, this), true); 725 this.dates.replace(dates); 726 727 if (this.dates.length) 728 this.viewDate = new Date(this.dates.get(-1)); 729 else if (this.viewDate < this.o.startDate) 730 this.viewDate = new Date(this.o.startDate); 731 else if (this.viewDate > this.o.endDate) 732 this.viewDate = new Date(this.o.endDate); 733 734 if (fromArgs){ 735 // setting date by clicking 736 this.setValue(); 737 } 738 else if (dates.length){ 739 // setting date by typing 740 if (String(oldDates) !== String(this.dates)) 741 this._trigger('changeDate'); 742 } 743 if (!this.dates.length && oldDates.length) 744 this._trigger('clearDate'); 745 746 this.fill(); 747 return this; 748 }, 749 750 fillDow: function(){ 751 var dowCnt = this.o.weekStart, 752 html = '<tr>'; 753 if (this.o.calendarWeeks){ 754 this.picker.find('.datepicker-days thead tr:first-child .datepicker-switch') 755 .attr('colspan', function(i, val){ 756 return parseInt(val) + 1; 757 }); 758 var cell = '<th class="cw"> </th>'; 759 html += cell; 760 } 761 while (dowCnt < this.o.weekStart + 7){ 762 html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>'; 763 } 764 html += '</tr>'; 765 this.picker.find('.datepicker-days thead').append(html); 766 }, 767 768 fillMonths: function(){ 769 var html = '', 770 i = 0; 771 while (i < 12){ 772 html += '<span class="month">'+dates[this.o.language].monthsShort[i++]+'</span>'; 773 } 774 this.picker.find('.datepicker-months td').html(html); 775 }, 776 777 setRange: function(range){ 778 if (!range || !range.length) 779 delete this.range; 780 else 781 this.range = $.map(range, function(d){ 782 return d.valueOf(); 783 }); 784 this.fill(); 785 }, 786 787 getClassNames: function(date){ 788 var cls = [], 789 year = this.viewDate.getUTCFullYear(),
790 month = this.viewDate.getUTCMonth(), 791 today = new Date(); 792 if (date.getUTCFullYear() < year || (date.getUTCFullYear() === year && date.getUTCMonth() < month)){ 793 cls.push('old'); 794 } 795 else if (date.getUTCFullYear() > year || (date.getUTCFullYear() === year && date.getUTCMonth() > month)){ 796 cls.push('new'); 797 } 798 if (this.focusDate && date.valueOf() === this.focusDate.valueOf()) 799 cls.push('focused'); 800 // Compare internal UTC date with local today, not UTC today 801 if (this.o.todayHighlight && 802 date.getUTCFullYear() === today.getFullYear() && 803 date.getUTCMonth() === today.getMonth() && 804 date.getUTCDate() === today.getDate()){ 805 cls.push('today'); 806 } 807 if (this.dates.contains(date) !== -1) 808 cls.push('active'); 809 if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate || 810 $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1){ 811 cls.push('disabled'); 812 } 813 if (this.o.datesDisabled.length > 0 && 814 $.grep(this.o.datesDisabled, function(d){ 815 return isUTCEquals(date, d); }).length > 0) { 816 cls.push('disabled', 'disabled-date'); 817 } 818 819 if (this.range){ 820 if (date > this.range[0] && date < this.range[this.range.length-1]){ 821 cls.push('range'); 822 } 823 if ($.inArray(date.valueOf(), this.range) !== -1){ 824 cls.push('selected'); 825 } 826 } 827 return cls; 828 }, 829 830 fill: function(){ 831 var d = new Date(this.viewDate), 832 year = d.getUTCFullYear(), 833 month = d.getUTCMonth(), 834 startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity, 835 startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity, 836 endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity, 837 endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity, 838 todaytxt = dates[this.o.language].today || dates['en'].today || '', 839 cleartxt = dates[this.o.language].clear || dates['en'].clear || '', 840 tooltip; 841 if (isNaN(year) || isNaN(month)) 842 return; 843 this.picker.find('.datepicker-days thead .datepicker-switch') 844 .text(dates[this.o.language].months[month]+' '+year); 845 this.picker.find('tfoot .today') 846 .text(todaytxt) 847 .toggle(this.o.todayBtn !== false); 848 this.picker.find('tfoot .clear') 849 .text(cleartxt) 850 .toggle(this.o.clearBtn !== false); 851 this.updateNavArrows(); 852 this.fillMonths(); 853 var prevMonth = UTCDate(year, month-1, 28), 854 day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth()); 855 prevMonth.setUTCDate(day); 856 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7); 857 var nextMonth = new Date(prevMonth); 858 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42); 859 nextMonth = nextMonth.valueOf(); 860 var html = []; 861 var clsName; 862 while (prevMonth.valueOf() < nextMonth){ 863 if (prevMonth.getUTCDay() === this.o.weekStart){ 864 html.push('<tr>'); 865 if (this.o.calendarWeeks){ 866 // ISO 8601: First week contains first thursday. 867 // ISO also states week starts on Monday, but we can be more abstract here. 868 var 869 // Start of current week: based on weekstart/current date 870 ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5), 871 // Thursday of this week 872 th = new Date(Number(ws) + (7 + 4 - ws.getUTCDay()) % 7 * 864e5), 873 // First Thursday of year, year from thursday 874 yth = new Date(Number(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5), 875 // Calendar week: ms between thursdays, div ms per day, div 7 days 876 calWeek = (th - yth) / 864e5 / 7 + 1; 877 html.push('<td class="cw">'+ calWeek +'</td>'); 878 879 } 880 } 881 clsName = this.getClassNames(prevMonth); 882 clsName.push('day'); 883 884 if (this.o.beforeShowDay !== $.noop){ 885 var before = this.o.beforeShowDay(this._utc_to_local(prevMonth)); 886 if (before === undefined) 887 before = {}; 888 else if (typeof(before) === 'boolean') 889 before = {enabled: before}; 890 else if (typeof(before) === 'string') 891 before = {classes: before}; 892 if (before.enabled === false) 893 clsName.push('disabled'); 894 if (before.classes) 895 clsName = clsName.concat(before.classes.split(/\s+/)); 896 if (before.tooltip) 897 tooltip = before.tooltip; 898 } 899 900 clsName = $.unique(clsName); 901 html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + '>'+prevMonth.getUTCDate() + '</td>'); 902 tooltip = null; 903 if (prevMonth.getUTCDay() === this.o.weekEnd){ 904 html.push('</tr>'); 905 } 906 prevMonth.setUTCDate(prevMonth.getUTCDate()+1); 907 } 908 this.picker.find('.datepicker-days tbody').empty().append(html.join('')); 909 910 var months = this.picker.find('.datepicker-months') 911 .find('th:eq(1)') 912 .text(year) 913 .end() 914 .find('span').removeClass('active'); 915 916 $.each(this.dates, function(i, d){ 917 if (d.getUTCFullYear() === year) 918 months.eq(d.getUTCMonth()).addClass('active'); 919 }); 920 921 if (year < startYear || year > endYear){ 922 months.addClass('disabled'); 923 } 924 if (year === startYear){ 925 months.slice(0, startMonth).addClass('disabled'); 926 } 927 if (year === endYear){ 928 months.slice(endMonth+1).addClass('disabled'); 929 } 930 931 if (this.o.beforeShowMonth !== $.noop){ 932 var that = this; 933 $.each(months, function(i, month){ 934 if (!$(month).hasClass('disabled')) { 935 var moDate = new Date(year, i, 1); 936 var before = that.o.beforeShowMonth(moDate); 937 if (before === false) 938 $(month).addClass('disabled'); 939 } 940 }); 941 } 942 943 html = ''; 944 year = parseInt(year/10, 10) * 10; 945 var yearCont = this.picker.find('.datepicker-years') 946 .find('th:eq(1)') 947 .text(year + '-' + (year + 9)) 948 .end() 949 .find('td'); 950 year -= 1; 951 var years = $.map(this.dates, function(d){ 952 return d.getUTCFullYear(); 953 }), 954 classes;
955 for (var i = -1; i < 11; i++){ 956 classes = ['year']; 957 if (i === -1) 958 classes.push('old'); 959 else if (i === 10) 960 classes.push('new'); 961 if ($.inArray(year, years) !== -1) 962 classes.push('active'); 963 if (year < startYear || year > endYear) 964 classes.push('disabled'); 965 html += '<span class="' + classes.join(' ') + '">' + year + '</span>'; 966 year += 1; 967 } 968 yearCont.html(html); 969 }, 970 971 updateNavArrows: function(){ 972 if (!this._allow_update) 973 return; 974 975 var d = new Date(this.viewDate), 976 year = d.getUTCFullYear(), 977 month = d.getUTCMonth(); 978 switch (this.viewMode){ 979 case 0: 980 if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()){ 981 this.picker.find('.prev').css({visibility: 'hidden'}); 982 } 983 else { 984 this.picker.find('.prev').css({visibility: 'visible'}); 985 } 986 if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()){ 987 this.picker.find('.next').css({visibility: 'hidden'}); 988 } 989 else { 990 this.picker.find('.next').css({visibility: 'visible'}); 991 } 992 break; 993 case 1: 994 case 2: 995 if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()){ 996 this.picker.find('.prev').css({visibility: 'hidden'}); 997 } 998 else { 999 this.picker.find('.prev').css({visibility: 'visible'}); 1000 } 1001 if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()){ 1002 this.picker.find('.next').css({visibility: 'hidden'}); 1003 } 1004 else { 1005 this.picker.find('.next').css({visibility: 'visible'}); 1006 } 1007 break; 1008 } 1009 }, 1010 1011 click: function(e){ 1012 e.preventDefault(); 1013 var target = $(e.target).closest('span, td, th'), 1014 year, month, day; 1015 if (target.length === 1){ 1016 switch (target[0].nodeName.toLowerCase()){ 1017 case 'th': 1018 switch (target[0].className){ 1019 case 'datepicker-switch': 1020 this.showMode(1); 1021 break; 1022 case 'prev': 1023 case 'next': 1024 var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1); 1025 switch (this.viewMode){ 1026 case 0: 1027 this.viewDate = this.moveMonth(this.viewDate, dir); 1028 this._trigger('changeMonth', this.viewDate); 1029 break; 1030 case 1: 1031 case 2: 1032 this.viewDate = this.moveYear(this.viewDate, dir); 1033 if (this.viewMode === 1) 1034 this._trigger('changeYear', this.viewDate); 1035 break; 1036 } 1037 this.fill(); 1038 break; 1039 case 'today': 1040 var date = new Date(); 1041 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); 1042 1043 this.showMode(-2); 1044 var which = this.o.todayBtn === 'linked' ? null : 'view'; 1045 this._setDate(date, which); 1046 break; 1047 case 'clear': 1048 this.clearDates(); 1049 break; 1050 } 1051 break; 1052 case 'span': 1053 if (!target.hasClass('disabled')){ 1054 this.viewDate.setUTCDate(1); 1055 if (target.hasClass('month')){ 1056 day = 1; 1057 month = target.parent().find('span').index(target); 1058 year = this.viewDate.getUTCFullYear(); 1059 this.viewDate.setUTCMonth(month); 1060 this._trigger('changeMonth', this.viewDate); 1061 if (this.o.minViewMode === 1){ 1062 this._setDate(UTCDate(year, month, day)); 1063 } 1064 } 1065 else { 1066 day = 1; 1067 month = 0; 1068 year = parseInt(target.text(), 10)||0; 1069 this.viewDate.setUTCFullYear(year); 1070 this._trigger('changeYear', this.viewDate); 1071 if (this.o.minViewMode === 2){ 1072 this._setDate(UTCDate(year, month, day)); 1073 } 1074 } 1075 this.showMode(-1); 1076 this.fill(); 1077 } 1078 break; 1079 case 'td': 1080 if (target.hasClass('day') && !target.hasClass('disabled')){ 1081 day = parseInt(target.text(), 10)||1; 1082 year = this.viewDate.getUTCFullYear(); 1083 month = this.viewDate.getUTCMonth(); 1084 if (target.hasClass('old')){ 1085 if (month === 0){ 1086 month = 11; 1087 year -= 1; 1088 } 1089 else { 1090 month -= 1; 1091 } 1092 } 1093 else if (target.hasClass('new')){ 1094 if (month === 11){
1095 month = 0; 1096 year += 1; 1097 } 1098 else { 1099 month += 1; 1100 } 1101 } 1102 this._setDate(UTCDate(year, month, day)); 1103 } 1104 break; 1105 } 1106 } 1107 if (this.picker.is(':visible') && this._focused_from){ 1108 $(this._focused_from).focus(); 1109 } 1110 delete this._focused_from; 1111 }, 1112 1113 _toggle_multidate: function(date){ 1114 var ix = this.dates.contains(date); 1115 if (!date){ 1116 this.dates.clear(); 1117 } 1118 1119 if (ix !== -1){ 1120 if (this.o.multidate === true || this.o.multidate > 1 || this.o.toggleActive){ 1121 this.dates.remove(ix); 1122 } 1123 } else if (this.o.multidate === false) { 1124 this.dates.clear(); 1125 this.dates.push(date); 1126 } 1127 else { 1128 this.dates.push(date); 1129 } 1130 1131 if (typeof this.o.multidate === 'number') 1132 while (this.dates.length > this.o.multidate) 1133 this.dates.remove(0); 1134 }, 1135 1136 _setDate: function(date, which){ 1137 if (!which || which === 'date') 1138 this._toggle_multidate(date && new Date(date)); 1139 if (!which || which === 'view') 1140 this.viewDate = date && new Date(date); 1141 1142 this.fill(); 1143 this.setValue(); 1144 if (!which || which !== 'view') { 1145 this._trigger('changeDate'); 1146 } 1147 var element; 1148 if (this.isInput){ 1149 element = this.element; 1150 } 1151 else if (this.component){ 1152 element = this.element.find('input'); 1153 } 1154 if (element){ 1155 element.change(); 1156 } 1157 if (this.o.autoclose && (!which || which === 'date')){ 1158 this.hide(); 1159 } 1160 }, 1161 1162 moveMonth: function(date, dir){ 1163 if (!date) 1164 return undefined; 1165 if (!dir) 1166 return date; 1167 var new_date = new Date(date.valueOf()), 1168 day = new_date.getUTCDate(), 1169 month = new_date.getUTCMonth(), 1170 mag = Math.abs(dir), 1171 new_month, test; 1172 dir = dir > 0 ? 1 : -1; 1173 if (mag === 1){ 1174 test = dir === -1 1175 // If going back one month, make sure month is not current month 1176 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02) 1177 ? function(){ 1178 return new_date.getUTCMonth() === month; 1179 } 1180 // If going forward one month, make sure month is as expected 1181 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02) 1182 : function(){ 1183 return new_date.getUTCMonth() !== new_month; 1184 }; 1185 new_month = month + dir; 1186 new_date.setUTCMonth(new_month); 1187 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11 1188 if (new_month < 0 || new_month > 11) 1189 new_month = (new_month + 12) % 12; 1190 } 1191 else { 1192 // For magnitudes >1, move one month at a time... 1193 for (var i=0; i < mag; i++) 1194 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)... 1195 new_date = this.moveMonth(new_date, dir); 1196 // ...then reset the day, keeping it in the new month 1197 new_month = new_date.getUTCMonth(); 1198 new_date.setUTCDate(day); 1199 test = function(){ 1200 return new_month !== new_date.getUTCMonth(); 1201 }; 1202 } 1203 // Common date-resetting loop -- if date is beyond end of month, make it 1204 // end of month 1205 while (test()){ 1206 new_date.setUTCDate(--day); 1207 new_date.setUTCMonth(new_month); 1208 } 1209 return new_date; 1210 }, 1211 1212 moveYear: function(date, dir){ 1213 return this.moveMonth(date, dir*12); 1214 }, 1215 1216 dateWithinRange: function(date){ 1217 return date >= this.o.startDate && date <= this.o.endDate; 1218 }, 1219 1220 keydown: function(e){ 1221 if (!this.picker.is(':visible')){ 1222 if (e.keyCode === 27) // allow escape to hide and re-show picker 1223 this.show(); 1224 return; 1225 } 1226 var dateChanged = false, 1227 dir, newDate, newViewDate, 1228 focusDate = this.focusDate || this.viewDate; 1229 switch (e.keyCode){ 1230 case 27: // escape 1231 if (this.focusDate){ 1232 this.focusDate = null; 1233 this.viewDate = this.dates.get(-1) || this.viewDate; 1234 this.fill(); 1235 } 1236 else 1237 this.hide(); 1238 e.preventDefault(); 1239 break; 1240 case 37: // left 1241 case 39: // right 1242 if (!this.o.keyboardNavigation) 1243 break; 1244 dir = e.keyCode === 37 ? -1 : 1; 1245 if (e.ctrlKey){ 1246 newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir); 1247 newViewDate = this.moveYear(focusDate, dir); 1248 this._trigger('changeYear', this.viewDate); 1249 } 1250 else if (e.shiftKey){ 1251 newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir); 1252 newViewDate = this.moveMonth(focusDate, dir); 1253 this._trigger('changeMonth', this.viewDate); 1254 } 1255 else { 1256 newDate = new Date(this.dates.get(-1) || UTCToday()); 1257 newDate.setUTCDate(newDate.getUTCDate() + dir); 1258 newViewDate = new Date(focusDate); 1259 newViewDate.setUTCDate(focusDate.getUTCDate() + dir); 1260 } 1261 if (this.dateWithinRange(newViewDate)){ 1262 this.focusDate = this.viewDate = newViewDate; 1263 this.setValue(); 1264 this.fill(); 1265 e.preventDefault(); 1266 } 1267 break; 1268 case 38: // up 1269 case 40: // down 1270 if (!this.o.keyboardNavigation) 1271 break; 1272 dir = e.keyCode === 38 ? -1 : 1; 1273 if (e.ctrlKey){ 1274 newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir); 1275 newViewDate = this.moveYear(focusDate, dir); 1276 this._trigger('changeYear', this.viewDate); 1277 } 1278 else if (e.shiftKey){ 1279 newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir); 1280 newViewDate = this.moveMonth(focusDate, dir); 1281 this._trigger('changeMonth', this.viewDate); 1282 } 1283 else { 1284 newDate = new Date(this.dates.get(-1) || UTCToday()); 1285 newDate.setUTCDate(newDate.getUTCDate() + dir * 7); 1286 newViewDate = new Date(focusDate); 1287 newViewDate.setUTCDate(focusDate.getUTCDate() + dir * 7); 1288 } 1289 if (this.dateWithinRange(newViewDate)){ 1290 this.focusDate = this.viewDate = newViewDate; 1291 this.setValue(); 1292 this.fill(); 1293 e.preventDefault(); 1294 } 1295 break; 1296 case 32: // spacebar 1297 // Spacebar is used in manually typing dates in some formats. 1298 // As such, its behavior should not be hijacked. 1299 break; 1300 case 13: // enter 1301 focusDate = this.focusDate || this.dates.get(-1) || this.viewDate; 1302 if (this.o.keyboardNavigation) { 1303 this._toggle_multidate(focusDate); 1304 dateChanged = true; 1305 } 1306 this.focusDate = null; 1307 this.viewDate = this.dates.get(-1) || this.viewDate; 1308 this.setValue(); 1309 this.fill(); 1310 if (this.picker.is(':visible')){ 1311 e.preventDefault(); 1312 if (typeof e.stopPropagation === 'function') { 1313 e.stopPropagation(); // All modern browsers, IE9+ 1314 } else { 1315 e.cancelBubble = true; // IE6,7,8 ignore "stopPropagation" 1316 } 1317 if (this.o.autoclose) 1318 this.hide(); 1319 } 1320 break; 1321 case 9: // tab 1322 this.focusDate = null; 1323 this.viewDate = this.dates.get(-1) || this.viewDate; 1324 this.fill(); 1325 this.hide(); 1326 break; 1327 } 1328 if (dateChanged){ 1329 if (this.dates.length) 1330 this._trigger('changeDate'); 1331 else 1332 this._trigger('clearDate'); 1333 var element; 1334 if (this.isInput){ 1335 element = this.element; 1336 } 1337 else if (this.component){ 1338 element = this.element.find('input'); 1339 } 1340 if (element){ 1341 element.change(); 1342 } 1343 } 1344 }, 1345 1346 showMode: function(dir){ 1347 if (dir){ 1348 this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir)); 1349 } 1350 this.picker 1351 .children('div') 1352 .hide() 1353 .filter('.datepicker-' + DPGlobal.modes[this.viewMode].clsName) 1354 .css('display', 'block'); 1355 this.updateNavArrows(); 1356 } 1357 }; 1358 1359 var DateRangePicker = function(element, options){ 1360 this.element = $(element); 1361 this.inputs = $.map(options.inputs, function(i){ 1362 return i.jquery ? i[0] : i; 1363 }); 1364 delete options.inputs; 1365 1366 datepickerPlugin.call($(this.inputs), options) 1367 .on('changeDate', $.proxy(this.dateUpdated, this)); 1368 1369 this.pickers = $.map(this.inputs, function(i){ 1370 return $(i).data('datepicker'); 1371 }); 1372 this.updateDates(); 1373 }; 1374 DateRangePicker.prototype = { 1375 updateDates: function(){ 1376 this.dates = $.map(this.pickers, function(i){ 1377 return i.getUTCDate(); 1378 }); 1379 this.updateRanges(); 1380 }, 1381 updateRanges: function(){ 1382 var range = $.map(this.dates, function(d){ 1383 return d.valueOf(); 1384 }); 1385 $.each(this.pickers, function(i, p){ 1386 p.setRange(range); 1387 }); 1388 }, 1389 dateUpdated: function(e){ 1390 // `this.updating` is a workaround for preventing infinite recursion 1391 // between `changeDate` triggering and `setUTCDate` calling. Until 1392 // there is a better mechanism. 1393 if (this.updating) 1394 return; 1395 this.updating = true; 1396 1397 var dp = $(e.target).data('datepicker'), 1398 new_date = dp.getUTCDate(), 1399 i = $.inArray(e.target, this.inputs),
1400 j = i - 1, 1401 k = i + 1, 1402 l = this.inputs.length; 1403 if (i === -1) 1404 return; 1405 1406 $.each(this.pickers, function(i, p){ 1407 if (!p.getUTCDate()) 1408 p.setUTCDate(new_date); 1409 }); 1410 1411 if (new_date < this.dates[j]){ 1412 // Date being moved earlier/left 1413 while (j >= 0 && new_date < this.dates[j]){ 1414 this.pickers[j--].setUTCDate(new_date); 1415 } 1416 } 1417 else if (new_date > this.dates[k]){ 1418 // Date being moved later/right 1419 while (k < l && new_date > this.dates[k]){ 1420 this.pickers[k++].setUTCDate(new_date); 1421 } 1422 } 1423 this.updateDates(); 1424 1425 delete this.updating; 1426 }, 1427 remove: function(){ 1428 $.map(this.pickers, function(p){ p.remove(); }); 1429 delete this.element.data().datepicker; 1430 } 1431 }; 1432 1433 function opts_from_el(el, prefix){ 1434 // Derive options from element data-attrs 1435 var data = $(el).data(), 1436 out = {}, inkey, 1437 replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'); 1438 prefix = new RegExp('^' + prefix.toLowerCase()); 1439 function re_lower(_,a){ 1440 return a.toLowerCase(); 1441 } 1442 for (var key in data) 1443 if (prefix.test(key)){ 1444 inkey = key.replace(replace, re_lower); 1445 out[inkey] = data[key]; 1446 } 1447 return out; 1448 } 1449 1450 function opts_from_locale(lang){ 1451 // Derive options from locale plugins 1452 var out = {}; 1453 // Check if "de-DE" style date is available, if not language should 1454 // fallback to 2 letter code eg "de" 1455 if (!dates[lang]){ 1456 lang = lang.split('-')[0]; 1457 if (!dates[lang]) 1458 return; 1459 } 1460 var d = dates[lang]; 1461 $.each(locale_opts, function(i,k){ 1462 if (k in d) 1463 out[k] = d[k]; 1464 }); 1465 return out; 1466 } 1467 1468 var old = $.fn.datepicker; 1469 var datepickerPlugin = function(option){ 1470 var args = Array.apply(null, arguments); 1471 args.shift(); 1472 var internal_return; 1473 this.each(function(){ 1474 var $this = $(this), 1475 data = $this.data('datepicker'), 1476 options = typeof option === 'object' && option; 1477 if (!data){ 1478 var elopts = opts_from_el(this, 'date'), 1479 // Preliminary otions 1480 xopts = $.extend({}, defaults, elopts, options), 1481 locopts = opts_from_locale(xopts.language), 1482 // Options priority: js args, data-attrs, locales, defaults 1483 opts = $.extend({}, defaults, locopts, elopts, options); 1484 if ($this.hasClass('input-daterange') || opts.inputs){ 1485 var ropts = { 1486 inputs: opts.inputs || $this.find('input').toArray() 1487 }; 1488 $this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts)))); 1489 } 1490 else { 1491 $this.data('datepicker', (data = new Datepicker(this, opts))); 1492 } 1493 } 1494 if (typeof option === 'string' && typeof data[option] === 'function'){ 1495 internal_return = data[option].apply(data, args); 1496 if (internal_return !== undefined) 1497 return false; 1498 } 1499 }); 1500 if (internal_return !== undefined) 1501 return internal_return; 1502 else 1503 return this; 1504 }; 1505 $.fn.datepicker = datepickerPlugin; 1506 1507 var defaults = $.fn.datepicker.defaults = { 1508 autoclose: false, 1509 beforeShowDay: $.noop, 1510 beforeShowMonth: $.noop, 1511 calendarWeeks: false, 1512 clearBtn: false, 1513 toggleActive: false, 1514 daysOfWeekDisabled: [], 1515 datesDisabled: [], 1516 endDate: Infinity, 1517 forceParse: true, 1518 format: 'mm/dd/yyyy', 1519 keyboardNavigation: true, 1520 language: 'en', 1521 minViewMode: 0, 1522 multidate: false, 1523 multidateSeparator: ',', 1524 orientation: "auto", 1525 rtl: false, 1526 startDate: -Infinity, 1527 startView: 0, 1528 todayBtn: false, 1529 todayHighlight: false, 1530 weekStart: 0, 1531 disableTouchKeyboard: false, 1532 enableOnReadonly: true, 1533 container: 'body' 1534 }; 1535 var locale_opts = $.fn.datepicker.locale_opts = [ 1536 'format', 1537 'rtl', 1538 'weekStart' 1539 ]; 1540 $.fn.datepicker.Constructor = Datepicker; 1541 var dates = $.fn.datepicker.dates = { 1542 en: { 1543 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], 1544 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], 1545 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"], 1546 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
1546November", "December"], 1547 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], 1548 today: "Today", 1549 clear: "Clear" 1550 } 1551 }; 1552 1553 var DPGlobal = { 1554 modes: [ 1555 { 1556 clsName: 'days', 1557 navFnc: 'Month', 1558 navStep: 1 1559 }, 1560 { 1561 clsName: 'months', 1562 navFnc: 'FullYear', 1563 navStep: 1 1564 }, 1565 { 1566 clsName: 'years', 1567 navFnc: 'FullYear', 1568 navStep: 10 1569 }], 1570 isLeapYear: function(year){ 1571 return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); 1572 }, 1573 getDaysInMonth: function(year, month){ 1574 return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; 1575 }, 1576 validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g, 1577 nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g, 1578 parseFormat: function(format){ 1579 // IE treats \0 as a string end in inputs (truncating the value), 1580 // so it's a bad format delimiter, anyway 1581 var separators = format.replace(this.validParts, '\0').split('\0'), 1582 parts = format.match(this.validParts); 1583 if (!separators || !separators.length || !parts || parts.length === 0){ 1584 throw new Error("Invalid date format."); 1585 } 1586 return {separators: separators, parts: parts}; 1587 }, 1588 parseDate: function(date, format, language){ 1589 if (!date) 1590 return undefined; 1591 if (date instanceof Date) 1592 return date; 1593 if (typeof format === 'string') 1594 format = DPGlobal.parseFormat(format); 1595 var part_re = /([\-+]\d+)([dmwy])/, 1596 parts = date.match(/([\-+]\d+)([dmwy])/g), 1597 part, dir, i; 1598 if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)){ 1599 date = new Date(); 1600 for (i=0; i < parts.length; i++){ 1601 part = part_re.exec(parts[i]); 1602 dir = parseInt(part[1]); 1603 switch (part[2]){ 1604 case 'd': 1605 date.setUTCDate(date.getUTCDate() + dir); 1606 break; 1607 case 'm': 1608 date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir); 1609 break; 1610 case 'w': 1611 date.setUTCDate(date.getUTCDate() + dir * 7); 1612 break; 1613 case 'y': 1614 date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir); 1615 break; 1616 } 1617 } 1618 return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0); 1619 } 1620 parts = date && date.match(this.nonpunctuation) || []; 1621 date = new Date(); 1622 var parsed = {}, 1623 setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'], 1624 setters_map = { 1625 yyyy: function(d,v){ 1626 return d.setUTCFullYear(v); 1627 }, 1628 yy: function(d,v){ 1629 return d.setUTCFullYear(2000+v); 1630 }, 1631 m: function(d,v){ 1632 if (isNaN(d)) 1633 return d; 1634 v -= 1; 1635 while (v < 0) v += 12; 1636 v %= 12; 1637 d.setUTCMonth(v); 1638 while (d.getUTCMonth() !== v) 1639 d.setUTCDate(d.getUTCDate()-1); 1640 return d; 1641 }, 1642 d: function(d,v){ 1643 return d.setUTCDate(v); 1644 } 1645 }, 1646 val, filtered; 1647 setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m']; 1648 setters_map['dd'] = setters_map['d']; 1649 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); 1650 var fparts = format.parts.slice(); 1651 // Remove noop parts 1652 if (parts.length !== fparts.length){ 1653 fparts = $(fparts).filter(function(i,p){ 1654 return $.inArray(p, setters_order) !== -1; 1655 }).toArray(); 1656 } 1657 // Process remainder 1658 function match_part(){ 1659 var m = this.slice(0, parts[i].length), 1660 p = parts[i].slice(0, m.length); 1661 return m.toLowerCase() === p.toLowerCase(); 1662 } 1663 if (parts.length === fparts.length){ 1664 var cnt; 1665 for (i=0, cnt = fparts.length; i < cnt; i++){ 1666 val = parseInt(parts[i], 10); 1667 part = fparts[i]; 1668 if (isNaN(val)){ 1669 switch (part){ 1670 case 'MM': 1671 filtered = $(dates[language].months).filter(match_part); 1672 val = $.inArray(filtered[0], dates[language].months) + 1; 1673 break; 1674 case 'M': 1675 filtered = $(dates[language].monthsShort).filter(match_part); 1676 val = $.inArray(filtered[0], dates[language].monthsShort) + 1; 1677 break; 1678 } 1679 } 1680 parsed[part] = val; 1681 } 1682 var _date, s; 1683 for (i=0; i < setters_order.length; i++){ 1684 s = setters_order[i]; 1685 if (s in parsed && !isNaN(parsed[s])){ 1686 _date = new Date(date); 1687 setters_map[s](_date, parsed[s]); 1688 if (!isNaN(_date)) 1689 date = _date; 1690 } 1691 } 1692 } 1693 return date; 1694 }, 1695 formatDate: function(date, format, language){ 1696 if (!date) 1697 return ''; 1698 if (typeof format === 'string') 1699 format = DPGlobal.parseFormat(format); 1700 var val = { 1701 d: date.getUTCDate(), 1702 D: dates[language].daysShort[date.getUTCDay()], 1703 DD: dates[language].days[date.getUTCDay()], 1704 m: date.getUTCMonth() + 1, 1705 M: dates[language].monthsShort[date.getUTCMonth()], 1706 MM: dates[language].months[date.getUTCMonth()], 1707 yy: date.getUTCFullYear().toString().substring(2), 1708 yyyy: date.getUTCFullYear() 1709 }; 1710 val.dd = (val.d < 10 ? '0' : '') + val.d; 1711 val.mm = (val.m < 10 ? '0' : '') + val.m; 1712 date = []; 1713 var seps = $.extend([], format.separators); 1714 for (var i=0, cnt = format.parts.length; i <= cnt; i++){ 1715 if (seps.length) 1716 date.push(seps.shift()); 1717 date.push(val[format.parts[i]]); 1718 } 1719 return date.join(''); 1720 }, 1721 headTemplate: '<thead>'+ 1722 '<tr>'+ 1723 '<th class="prev">«</th>'+ 1724 '<th colspan="5" class="datepicker-switch"></th>'+ 1725 '<th class="next">»</th>'+ 1726 '</tr>'+ 1727 '</thead>', 1728 contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>', 1729 footTemplate: '<tfoot>'+ 1730 '<tr>'+ 1731 '<th colspan="7" class="today"></th>'+ 1732 '</tr>'+ 1733 '<tr>'+ 1734 '<th colspan="7" class="clear"></th>'+ 1735 '</tr>'+ 1736 '</tfoot>' 1737 }; 1738 DPGlobal.template = '<div class="datepicker">'+ 1739 '<div class="datepicker-days">'+ 1740 '<table class=" table-condensed">'+ 1741 DPGlobal.headTemplate+ 1742 '<tbody></tbody>'+ 1743 DPGlobal.footTemplate+ 1744 '</table>'+ 1745 '</div>'+ 1746 '<div class="datepicker-months">'+ 1747 '<table class="table-condensed">'+ 1748 DPGlobal.headTemplate+ 1749 DPGlobal.contTemplate+ 1750 DPGlobal.footTemplate+ 1751 '</table>'+ 1752 '</div>'+ 1753 '<div class="datepicker-years">'+ 1754 '<table class="table-condensed">'+ 1755 DPGlobal.headTemplate+ 1756 DPGlobal.contTemplate+ 1757 DPGlobal.footTemplate+ 1758 '</table>'+ 1759 '</div>'+ 1760 '</div>'; 1761 1762 $.fn.datepicker.DPGlobal = DPGlobal; 1763 1764 1765 /* DATEPICKER NO CONFLICT 1766 * =================== */ 1767 1768 $.fn.datepicker.noConflict = function(){ 1769 $.fn.datepicker = old; 1770 return this; 1771 }; 1772 1773 /* DATEPICKER VERSION 1774 * =================== */ 1775 $.fn.datepicker.version = "1.5.0"; 1776 1777 /* DATEPICKER DATA-API 1778 * ================== */ 1779 1780 $(document).on( 1781 'focus.datepicker.data-api click.datepicker.data-api',
1782 '[data-provide="datepicker"]', 1783 function(e){ 1784 var $this = $(this); 1785 if ($this.data('datepicker')) 1786 return; 1787 e.preventDefault(); 1788 // component click requires us to explicitly show it 1789 datepickerPlugin.call($this, 'show'); 1790 } 1791 ); 1792 $(function(){ 1793 datepickerPlugin.call($('[data-provide="datepicker-inline"]')); 1794 }); 1795 1796}(window.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.