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