github.com/apremalal/vamps-core@v1.0.1-0.20161221121535-d430b56ec174/server/webapps/app/base/plugins/bootstrap-datepicker/js/bootstrap-datepicker.js (about) 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.splice(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 this.picker.find('.prev i, .next i') 115 .toggleClass('fa-angle-left fa-angle-right'); 116 } 117 118 this.viewMode = this.o.startView; 119 120 if (this.o.calendarWeeks) 121 this.picker.find('tfoot th.today') 122 .attr('colspan', function(i, val){ 123 return parseInt(val) + 1; 124 }); 125 126 this._allow_update = false; 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 // 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.isInline) 414 this.picker.appendTo('body'); 415 this.picker.show(); 416 this.place(); 417 this._attachSecondaryEvents(); 418 this._trigger('show'); 419 }, 420 421 hide: function(){ 422 if (this.isInline) 423 return; 424 if (!this.picker.is(':visible')) 425 return; 426 this.focusDate = null; 427 this.picker.hide().detach(); 428 this._detachSecondaryEvents(); 429 this.viewMode = this.o.startView; 430 this.showMode(); 431 432 if ( 433 this.o.forceParse && 434 ( 435 this.isInput && this.element.val() || 436 this.hasInput && this.element.find('input').val() 437 ) 438 ) 439 this.setValue(); 440 this._trigger('hide'); 441 }, 442 443 remove: function(){ 444 this.hide(); 445 this._detachEvents(); 446 this._detachSecondaryEvents(); 447 this.picker.remove(); 448 delete this.element.data().datepicker; 449 if (!this.isInput){ 450 delete this.element.data().date; 451 } 452 }, 453 454 _utc_to_local: function(utc){ 455 return utc && new Date(utc.getTime() + (utc.getTimezoneOffset()*60000)); 456 }, 457 _local_to_utc: function(local){ 458 return local && new Date(local.getTime() - (local.getTimezoneOffset()*60000)); 459 }, 460 _zero_time: function(local){ 461 return local && new Date(local.getFullYear(), local.getMonth(), local.getDate()); 462 }, 463 _zero_utc_time: function(utc){ 464 return utc && new Date(Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate())); 465 }, 466 467 getDates: function(){ 468 return $.map(this.dates, this._utc_to_local); 469 }, 470 471 getUTCDates: function(){ 472 return $.map(this.dates, function(d){ 473 return new Date(d); 474 }); 475 }, 476 477 getDate: function(){ 478 return this._utc_to_local(this.getUTCDate()); 479 }, 480 481 getUTCDate: function(){ 482 return new Date(this.dates.get(-1)); 483 }, 484 485 setDates: function(){ 486 var args = $.isArray(arguments[0]) ? arguments[0] : arguments; 487 this.update.apply(this, args); 488 this._trigger('changeDate'); 489 this.setValue(); 490 }, 491 492 setUTCDates: function(){ 493 var args = $.isArray(arguments[0]) ? arguments[0] : arguments; 494 this.update.apply(this, $.map(args, this._utc_to_local)); 495 this._trigger('changeDate'); 496 this.setValue(); 497 }, 498 499 setDate: alias('setDates'), 500 setUTCDate: alias('setUTCDates'), 501 502 setValue: function(){ 503 var formatted = this.getFormattedDate(); 504 if (!this.isInput){ 505 if (this.component){ 506 this.element.find('input').val(formatted).change(); 507 } 508 } 509 else { 510 this.element.val(formatted).change(); 511 } 512 }, 513 514 getFormattedDate: function(format){ 515 if (format === undefined) 516 format = this.o.format; 517 518 var lang = this.o.language; 519 return $.map(this.dates, function(d){ 520 return DPGlobal.formatDate(d, format, lang); 521 }).join(this.o.multidateSeparator); 522 }, 523 524 setStartDate: function(startDate){ 525 this._process_options({startDate: startDate}); 526 this.update(); 527 this.updateNavArrows(); 528 }, 529 530 setEndDate: function(endDate){ 531 this._process_options({endDate: endDate}); 532 this.update(); 533 this.updateNavArrows(); 534 }, 535 536 setDaysOfWeekDisabled: function(daysOfWeekDisabled){ 537 this._process_options({daysOfWeekDisabled: daysOfWeekDisabled}); 538 this.update(); 539 this.updateNavArrows(); 540 }, 541 542 place: function(){ 543 if (this.isInline) 544 return; 545 var calendarWidth = this.picker.outerWidth(), 546 calendarHeight = this.picker.outerHeight(), 547 visualPadding = 10, 548 windowWidth = $window.width(), 549 windowHeight = $window.height(), 550 scrollTop = $window.scrollTop(); 551 552 var zIndex = parseInt(this.element.parents().filter(function(){ 553 return $(this).css('z-index') !== 'auto'; 554 }).first().css('z-index'))+10; 555 var offset = this.component ? this.component.parent().offset() : this.element.offset(); 556 var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false); 557 var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false); 558 var left = offset.left, 559 top = offset.top; 560 561 this.picker.removeClass( 562 'datepicker-orient-top datepicker-orient-bottom '+ 563 'datepicker-orient-right datepicker-orient-left' 564 ); 565 566 if (this.o.orientation.x !== 'auto'){ 567 this.picker.addClass('datepicker-orient-' + this.o.orientation.x); 568 if (this.o.orientation.x === 'right') 569 left -= calendarWidth - width; 570 } 571 // auto x orientation is best-placement: if it crosses a window 572 // edge, fudge it sideways 573 else { 574 // Default to left 575 this.picker.addClass('datepicker-orient-left'); 576 if (offset.left < 0) 577 left -= offset.left - visualPadding; 578 else if (offset.left + calendarWidth > windowWidth) 579 left = windowWidth - calendarWidth - visualPadding; 580 } 581 582 // auto y orientation is best-situation: top or bottom, no fudging, 583 // decision based on which shows more of the calendar 584 var yorient = this.o.orientation.y, 585 top_overflow, bottom_overflow; 586 if (yorient === 'auto'){ 587 top_overflow = -scrollTop + offset.top - calendarHeight; 588 bottom_overflow = scrollTop + windowHeight - (offset.top + height + calendarHeight); 589 if (Math.max(top_overflow, bottom_overflow) === bottom_overflow) 590 yorient = 'top'; 591 else 592 yorient = 'bottom'; 593 } 594 this.picker.addClass('datepicker-orient-' + yorient); 595 if (yorient === 'top') 596 top += height; 597 else 598 top -= calendarHeight + parseInt(this.picker.css('padding-top')); 599 600 this.picker.css({ 601 top: top, 602 left: left, 603 zIndex: zIndex 604 }); 605 }, 606 607 _allow_update: true, 608 update: function(){ 609 if (!this._allow_update) 610 return; 611 612 var oldDates = this.dates.copy(), 613 dates = [], 614 fromArgs = false; 615 if (arguments.length){ 616 $.each(arguments, $.proxy(function(i, date){ 617 if (date instanceof Date) 618 date = this._local_to_utc(date); 619 dates.push(date); 620 }, this)); 621 fromArgs = true; 622 } 623 else { 624 dates = this.isInput 625 ? this.element.val() 626 : this.element.data('date') || this.element.find('input').val(); 627 if (dates && this.o.multidate) 628 dates = dates.split(this.o.multidateSeparator); 629 else 630 dates = [dates]; 631 delete this.element.data().date; 632 } 633 634 dates = $.map(dates, $.proxy(function(date){ 635 return DPGlobal.parseDate(date, this.o.format, this.o.language); 636 }, this)); 637 dates = $.grep(dates, $.proxy(function(date){ 638 return ( 639 date < this.o.startDate || 640 date > this.o.endDate || 641 !date 642 ); 643 }, this), true); 644 this.dates.replace(dates); 645 646 if (this.dates.length) 647 this.viewDate = new Date(this.dates.get(-1)); 648 else if (this.viewDate < this.o.startDate) 649 this.viewDate = new Date(this.o.startDate); 650 else if (this.viewDate > this.o.endDate) 651 this.viewDate = new Date(this.o.endDate); 652 653 if (fromArgs){ 654 // setting date by clicking 655 this.setValue(); 656 } 657 else if (dates.length){ 658 // setting date by typing 659 if (String(oldDates) !== String(this.dates)) 660 this._trigger('changeDate'); 661 } 662 if (!this.dates.length && oldDates.length) 663 this._trigger('clearDate'); 664 665 this.fill(); 666 }, 667 668 fillDow: function(){ 669 var dowCnt = this.o.weekStart, 670 html = '<tr>'; 671 if (this.o.calendarWeeks){ 672 var cell = '<th class="cw"> </th>'; 673 html += cell; 674 this.picker.find('.datepicker-days thead tr:first-child').prepend(cell); 675 } 676 while (dowCnt < this.o.weekStart + 7){ 677 html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>'; 678 } 679 html += '</tr>'; 680 this.picker.find('.datepicker-days thead').append(html); 681 }, 682 683 fillMonths: function(){ 684 var html = '', 685 i = 0; 686 while (i < 12){ 687 html += '<span class="month">'+dates[this.o.language].monthsShort[i++]+'</span>'; 688 } 689 this.picker.find('.datepicker-months td').html(html); 690 }, 691 692 setRange: function(range){ 693 if (!range || !range.length) 694 delete this.range; 695 else 696 this.range = $.map(range, function(d){ 697 return d.valueOf(); 698 }); 699 this.fill(); 700 }, 701 702 getClassNames: function(date){ 703 var cls = [], 704 year = this.viewDate.getUTCFullYear(), 705 month = this.viewDate.getUTCMonth(), 706 today = new Date(); 707 if (date.getUTCFullYear() < year || (date.getUTCFullYear() === year && date.getUTCMonth() < month)){ 708 cls.push('old'); 709 } 710 else if (date.getUTCFullYear() > year || (date.getUTCFullYear() === year && date.getUTCMonth() > month)){ 711 cls.push('new'); 712 } 713 if (this.focusDate && date.valueOf() === this.focusDate.valueOf()) 714 cls.push('focused'); 715 // Compare internal UTC date with local today, not UTC today 716 if (this.o.todayHighlight && 717 date.getUTCFullYear() === today.getFullYear() && 718 date.getUTCMonth() === today.getMonth() && 719 date.getUTCDate() === today.getDate()){ 720 cls.push('today'); 721 } 722 if (this.dates.contains(date) !== -1) 723 cls.push('active'); 724 if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate || 725 $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1){ 726 cls.push('disabled'); 727 } 728 if (this.range){ 729 if (date > this.range[0] && date < this.range[this.range.length-1]){ 730 cls.push('range'); 731 } 732 if ($.inArray(date.valueOf(), this.range) !== -1){ 733 cls.push('selected'); 734 } 735 } 736 return cls; 737 }, 738 739 fill: function(){ 740 var d = new Date(this.viewDate), 741 year = d.getUTCFullYear(), 742 month = d.getUTCMonth(), 743 startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity, 744 startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity, 745 endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity, 746 endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity, 747 todaytxt = dates[this.o.language].today || dates['en'].today || '', 748 cleartxt = dates[this.o.language].clear || dates['en'].clear || '', 749 tooltip; 750 this.picker.find('.datepicker-days thead th.datepicker-switch') 751 .text(dates[this.o.language].months[month]+' '+year); 752 this.picker.find('tfoot th.today') 753 .text(todaytxt) 754 .toggle(this.o.todayBtn !== false); 755 this.picker.find('tfoot th.clear') 756 .text(cleartxt) 757 .toggle(this.o.clearBtn !== false); 758 this.updateNavArrows(); 759 this.fillMonths(); 760 var prevMonth = UTCDate(year, month-1, 28), 761 day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth()); 762 prevMonth.setUTCDate(day); 763 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7); 764 var nextMonth = new Date(prevMonth); 765 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42); 766 nextMonth = nextMonth.valueOf(); 767 var html = []; 768 var clsName; 769 while (prevMonth.valueOf() < nextMonth){ 770 if (prevMonth.getUTCDay() === this.o.weekStart){ 771 html.push('<tr>'); 772 if (this.o.calendarWeeks){ 773 // ISO 8601: First week contains first thursday. 774 // ISO also states week starts on Monday, but we can be more abstract here. 775 var 776 // Start of current week: based on weekstart/current date 777 ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5), 778 // Thursday of this week 779 th = new Date(Number(ws) + (7 + 4 - ws.getUTCDay()) % 7 * 864e5), 780 // First Thursday of year, year from thursday 781 yth = new Date(Number(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5), 782 // Calendar week: ms between thursdays, div ms per day, div 7 days 783 calWeek = (th - yth) / 864e5 / 7 + 1; 784 html.push('<td class="cw">'+ calWeek +'</td>'); 785 786 } 787 } 788 clsName = this.getClassNames(prevMonth); 789 clsName.push('day'); 790 791 if (this.o.beforeShowDay !== $.noop){ 792 var before = this.o.beforeShowDay(this._utc_to_local(prevMonth)); 793 if (before === undefined) 794 before = {}; 795 else if (typeof(before) === 'boolean') 796 before = {enabled: before}; 797 else if (typeof(before) === 'string') 798 before = {classes: before}; 799 if (before.enabled === false) 800 clsName.push('disabled'); 801 if (before.classes) 802 clsName = clsName.concat(before.classes.split(/\s+/)); 803 if (before.tooltip) 804 tooltip = before.tooltip; 805 } 806 807 clsName = $.unique(clsName); 808 html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + '>'+prevMonth.getUTCDate() + '</td>'); 809 if (prevMonth.getUTCDay() === this.o.weekEnd){ 810 html.push('</tr>'); 811 } 812 prevMonth.setUTCDate(prevMonth.getUTCDate()+1); 813 } 814 this.picker.find('.datepicker-days tbody').empty().append(html.join('')); 815 816 var months = this.picker.find('.datepicker-months') 817 .find('th:eq(1)') 818 .text(year) 819 .end() 820 .find('span').removeClass('active'); 821 822 $.each(this.dates, function(i, d){ 823 if (d.getUTCFullYear() === year) 824 months.eq(d.getUTCMonth()).addClass('active'); 825 }); 826 827 if (year < startYear || year > endYear){ 828 months.addClass('disabled'); 829 } 830 if (year === startYear){ 831 months.slice(0, startMonth).addClass('disabled'); 832 } 833 if (year === endYear){ 834 months.slice(endMonth+1).addClass('disabled'); 835 } 836 837 html = ''; 838 year = parseInt(year/10, 10) * 10; 839 var yearCont = this.picker.find('.datepicker-years') 840 .find('th:eq(1)') 841 .text(year + '-' + (year + 9)) 842 .end() 843 .find('td'); 844 year -= 1; 845 var years = $.map(this.dates, function(d){ 846 return d.getUTCFullYear(); 847 }), 848 classes; 849 for (var i = -1; i < 11; i++){ 850 classes = ['year']; 851 if (i === -1) 852 classes.push('old'); 853 else if (i === 10) 854 classes.push('new'); 855 if ($.inArray(year, years) !== -1) 856 classes.push('active'); 857 if (year < startYear || year > endYear) 858 classes.push('disabled'); 859 html += '<span class="' + classes.join(' ') + '">'+year+'</span>'; 860 year += 1; 861 } 862 yearCont.html(html); 863 }, 864 865 updateNavArrows: function(){ 866 if (!this._allow_update) 867 return; 868 869 var d = new Date(this.viewDate), 870 year = d.getUTCFullYear(), 871 month = d.getUTCMonth(); 872 switch (this.viewMode){ 873 case 0: 874 if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()){ 875 this.picker.find('.prev').css({visibility: 'hidden'}); 876 } 877 else { 878 this.picker.find('.prev').css({visibility: 'visible'}); 879 } 880 if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()){ 881 this.picker.find('.next').css({visibility: 'hidden'}); 882 } 883 else { 884 this.picker.find('.next').css({visibility: 'visible'}); 885 } 886 break; 887 case 1: 888 case 2: 889 if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()){ 890 this.picker.find('.prev').css({visibility: 'hidden'}); 891 } 892 else { 893 this.picker.find('.prev').css({visibility: 'visible'}); 894 } 895 if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()){ 896 this.picker.find('.next').css({visibility: 'hidden'}); 897 } 898 else { 899 this.picker.find('.next').css({visibility: 'visible'}); 900 } 901 break; 902 } 903 }, 904 905 click: function(e){ 906 e.preventDefault(); 907 var target = $(e.target).closest('span, td, th'), 908 year, month, day; 909 if (target.length === 1){ 910 switch (target[0].nodeName.toLowerCase()){ 911 case 'th': 912 switch (target[0].className){ 913 case 'datepicker-switch': 914 this.showMode(1); 915 break; 916 case 'prev': 917 case 'next': 918 var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1); 919 switch (this.viewMode){ 920 case 0: 921 this.viewDate = this.moveMonth(this.viewDate, dir); 922 this._trigger('changeMonth', this.viewDate); 923 break; 924 case 1: 925 case 2: 926 this.viewDate = this.moveYear(this.viewDate, dir); 927 if (this.viewMode === 1) 928 this._trigger('changeYear', this.viewDate); 929 break; 930 } 931 this.fill(); 932 break; 933 case 'today': 934 var date = new Date(); 935 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); 936 937 this.showMode(-2); 938 var which = this.o.todayBtn === 'linked' ? null : 'view'; 939 this._setDate(date, which); 940 break; 941 case 'clear': 942 var element; 943 if (this.isInput) 944 element = this.element; 945 else if (this.component) 946 element = this.element.find('input'); 947 if (element) 948 element.val("").change(); 949 this.update(); 950 this._trigger('changeDate'); 951 if (this.o.autoclose) 952 this.hide(); 953 break; 954 } 955 break; 956 case 'span': 957 if (!target.is('.disabled')){ 958 this.viewDate.setUTCDate(1); 959 if (target.is('.month')){ 960 day = 1; 961 month = target.parent().find('span').index(target); 962 year = this.viewDate.getUTCFullYear(); 963 this.viewDate.setUTCMonth(month); 964 this._trigger('changeMonth', this.viewDate); 965 if (this.o.minViewMode === 1){ 966 this._setDate(UTCDate(year, month, day)); 967 } 968 } 969 else { 970 day = 1; 971 month = 0; 972 year = parseInt(target.text(), 10)||0; 973 this.viewDate.setUTCFullYear(year); 974 this._trigger('changeYear', this.viewDate); 975 if (this.o.minViewMode === 2){ 976 this._setDate(UTCDate(year, month, day)); 977 } 978 } 979 this.showMode(-1); 980 this.fill(); 981 } 982 break; 983 case 'td': 984 if (target.is('.day') && !target.is('.disabled')){ 985 day = parseInt(target.text(), 10)||1; 986 year = this.viewDate.getUTCFullYear(); 987 month = this.viewDate.getUTCMonth(); 988 if (target.is('.old')){ 989 if (month === 0){ 990 month = 11; 991 year -= 1; 992 } 993 else { 994 month -= 1; 995 } 996 } 997 else if (target.is('.new')){ 998 if (month === 11){ 999 month = 0; 1000 year += 1; 1001 } 1002 else { 1003 month += 1; 1004 } 1005 } 1006 this._setDate(UTCDate(year, month, day)); 1007 } 1008 break; 1009 } 1010 } 1011 if (this.picker.is(':visible') && this._focused_from){ 1012 $(this._focused_from).focus(); 1013 } 1014 delete this._focused_from; 1015 }, 1016 1017 _toggle_multidate: function(date){ 1018 var ix = this.dates.contains(date); 1019 if (!date){ 1020 this.dates.clear(); 1021 } 1022 else if (ix !== -1){ 1023 this.dates.remove(ix); 1024 } 1025 else { 1026 this.dates.push(date); 1027 } 1028 if (typeof this.o.multidate === 'number') 1029 while (this.dates.length > this.o.multidate) 1030 this.dates.remove(0); 1031 }, 1032 1033 _setDate: function(date, which){ 1034 if (!which || which === 'date') 1035 this._toggle_multidate(date && new Date(date)); 1036 if (!which || which === 'view') 1037 this.viewDate = date && new Date(date); 1038 1039 this.fill(); 1040 this.setValue(); 1041 this._trigger('changeDate'); 1042 var element; 1043 if (this.isInput){ 1044 element = this.element; 1045 } 1046 else if (this.component){ 1047 element = this.element.find('input'); 1048 } 1049 if (element){ 1050 element.change(); 1051 } 1052 if (this.o.autoclose && (!which || which === 'date')){ 1053 this.hide(); 1054 } 1055 }, 1056 1057 moveMonth: function(date, dir){ 1058 if (!date) 1059 return undefined; 1060 if (!dir) 1061 return date; 1062 var new_date = new Date(date.valueOf()), 1063 day = new_date.getUTCDate(), 1064 month = new_date.getUTCMonth(), 1065 mag = Math.abs(dir), 1066 new_month, test; 1067 dir = dir > 0 ? 1 : -1; 1068 if (mag === 1){ 1069 test = dir === -1 1070 // If going back one month, make sure month is not current month 1071 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02) 1072 ? function(){ 1073 return new_date.getUTCMonth() === month; 1074 } 1075 // If going forward one month, make sure month is as expected 1076 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02) 1077 : function(){ 1078 return new_date.getUTCMonth() !== new_month; 1079 }; 1080 new_month = month + dir; 1081 new_date.setUTCMonth(new_month); 1082 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11 1083 if (new_month < 0 || new_month > 11) 1084 new_month = (new_month + 12) % 12; 1085 } 1086 else { 1087 // For magnitudes >1, move one month at a time... 1088 for (var i=0; i < mag; i++) 1089 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)... 1090 new_date = this.moveMonth(new_date, dir); 1091 // ...then reset the day, keeping it in the new month 1092 new_month = new_date.getUTCMonth(); 1093 new_date.setUTCDate(day); 1094 test = function(){ 1095 return new_month !== new_date.getUTCMonth(); 1096 }; 1097 } 1098 // Common date-resetting loop -- if date is beyond end of month, make it 1099 // end of month 1100 while (test()){ 1101 new_date.setUTCDate(--day); 1102 new_date.setUTCMonth(new_month); 1103 } 1104 return new_date; 1105 }, 1106 1107 moveYear: function(date, dir){ 1108 return this.moveMonth(date, dir*12); 1109 }, 1110 1111 dateWithinRange: function(date){ 1112 return date >= this.o.startDate && date <= this.o.endDate; 1113 }, 1114 1115 keydown: function(e){ 1116 if (this.picker.is(':not(:visible)')){ 1117 if (e.keyCode === 27) // allow escape to hide and re-show picker 1118 this.show(); 1119 return; 1120 } 1121 var dateChanged = false, 1122 dir, newDate, newViewDate, 1123 focusDate = this.focusDate || this.viewDate; 1124 switch (e.keyCode){ 1125 case 27: // escape 1126 if (this.focusDate){ 1127 this.focusDate = null; 1128 this.viewDate = this.dates.get(-1) || this.viewDate; 1129 this.fill(); 1130 } 1131 else 1132 this.hide(); 1133 e.preventDefault(); 1134 break; 1135 case 37: // left 1136 case 39: // right 1137 if (!this.o.keyboardNavigation) 1138 break; 1139 dir = e.keyCode === 37 ? -1 : 1; 1140 if (e.ctrlKey){ 1141 newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir); 1142 newViewDate = this.moveYear(focusDate, dir); 1143 this._trigger('changeYear', this.viewDate); 1144 } 1145 else if (e.shiftKey){ 1146 newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir); 1147 newViewDate = this.moveMonth(focusDate, dir); 1148 this._trigger('changeMonth', this.viewDate); 1149 } 1150 else { 1151 newDate = new Date(this.dates.get(-1) || UTCToday()); 1152 newDate.setUTCDate(newDate.getUTCDate() + dir); 1153 newViewDate = new Date(focusDate); 1154 newViewDate.setUTCDate(focusDate.getUTCDate() + dir); 1155 } 1156 if (this.dateWithinRange(newDate)){ 1157 this.focusDate = this.viewDate = newViewDate; 1158 this.setValue(); 1159 this.fill(); 1160 e.preventDefault(); 1161 } 1162 break; 1163 case 38: // up 1164 case 40: // down 1165 if (!this.o.keyboardNavigation) 1166 break; 1167 dir = e.keyCode === 38 ? -1 : 1; 1168 if (e.ctrlKey){ 1169 newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir); 1170 newViewDate = this.moveYear(focusDate, dir); 1171 this._trigger('changeYear', this.viewDate); 1172 } 1173 else if (e.shiftKey){ 1174 newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir); 1175 newViewDate = this.moveMonth(focusDate, dir); 1176 this._trigger('changeMonth', this.viewDate); 1177 } 1178 else { 1179 newDate = new Date(this.dates.get(-1) || UTCToday()); 1180 newDate.setUTCDate(newDate.getUTCDate() + dir * 7); 1181 newViewDate = new Date(focusDate); 1182 newViewDate.setUTCDate(focusDate.getUTCDate() + dir * 7); 1183 } 1184 if (this.dateWithinRange(newDate)){ 1185 this.focusDate = this.viewDate = newViewDate; 1186 this.setValue(); 1187 this.fill(); 1188 e.preventDefault(); 1189 } 1190 break; 1191 case 32: // spacebar 1192 // Spacebar is used in manually typing dates in some formats. 1193 // As such, its behavior should not be hijacked. 1194 break; 1195 case 13: // enter 1196 focusDate = this.focusDate || this.dates.get(-1) || this.viewDate; 1197 this._toggle_multidate(focusDate); 1198 dateChanged = true; 1199 this.focusDate = null; 1200 this.viewDate = this.dates.get(-1) || this.viewDate; 1201 this.setValue(); 1202 this.fill(); 1203 if (this.picker.is(':visible')){ 1204 e.preventDefault(); 1205 if (this.o.autoclose) 1206 this.hide(); 1207 } 1208 break; 1209 case 9: // tab 1210 this.focusDate = null; 1211 this.viewDate = this.dates.get(-1) || this.viewDate; 1212 this.fill(); 1213 this.hide(); 1214 break; 1215 } 1216 if (dateChanged){ 1217 if (this.dates.length) 1218 this._trigger('changeDate'); 1219 else 1220 this._trigger('clearDate'); 1221 var element; 1222 if (this.isInput){ 1223 element = this.element; 1224 } 1225 else if (this.component){ 1226 element = this.element.find('input'); 1227 } 1228 if (element){ 1229 element.change(); 1230 } 1231 } 1232 }, 1233 1234 showMode: function(dir){ 1235 if (dir){ 1236 this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir)); 1237 } 1238 this.picker 1239 .find('>div') 1240 .hide() 1241 .filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName) 1242 .css('display', 'block'); 1243 this.updateNavArrows(); 1244 } 1245 }; 1246 1247 var DateRangePicker = function(element, options){ 1248 this.element = $(element); 1249 this.inputs = $.map(options.inputs, function(i){ 1250 return i.jquery ? i[0] : i; 1251 }); 1252 delete options.inputs; 1253 1254 $(this.inputs) 1255 .datepicker(options) 1256 .bind('changeDate', $.proxy(this.dateUpdated, this)); 1257 1258 this.pickers = $.map(this.inputs, function(i){ 1259 return $(i).data('datepicker'); 1260 }); 1261 this.updateDates(); 1262 }; 1263 DateRangePicker.prototype = { 1264 updateDates: function(){ 1265 this.dates = $.map(this.pickers, function(i){ 1266 return i.getUTCDate(); 1267 }); 1268 this.updateRanges(); 1269 }, 1270 updateRanges: function(){ 1271 var range = $.map(this.dates, function(d){ 1272 return d.valueOf(); 1273 }); 1274 $.each(this.pickers, function(i, p){ 1275 p.setRange(range); 1276 }); 1277 }, 1278 dateUpdated: function(e){ 1279 // `this.updating` is a workaround for preventing infinite recursion 1280 // between `changeDate` triggering and `setUTCDate` calling. Until 1281 // there is a better mechanism. 1282 if (this.updating) 1283 return; 1284 this.updating = true; 1285 1286 var dp = $(e.target).data('datepicker'), 1287 new_date = dp.getUTCDate(), 1288 i = $.inArray(e.target, this.inputs), 1289 l = this.inputs.length; 1290 if (i === -1) 1291 return; 1292 1293 $.each(this.pickers, function(i, p){ 1294 if (!p.getUTCDate()) 1295 p.setUTCDate(new_date); 1296 }); 1297 1298 if (new_date < this.dates[i]){ 1299 // Date being moved earlier/left 1300 while (i >= 0 && new_date < this.dates[i]){ 1301 this.pickers[i--].setUTCDate(new_date); 1302 } 1303 } 1304 else if (new_date > this.dates[i]){ 1305 // Date being moved later/right 1306 while (i < l && new_date > this.dates[i]){ 1307 this.pickers[i++].setUTCDate(new_date); 1308 } 1309 } 1310 this.updateDates(); 1311 1312 delete this.updating; 1313 }, 1314 remove: function(){ 1315 $.map(this.pickers, function(p){ p.remove(); }); 1316 delete this.element.data().datepicker; 1317 } 1318 }; 1319 1320 function opts_from_el(el, prefix){ 1321 // Derive options from element data-attrs 1322 var data = $(el).data(), 1323 out = {}, inkey, 1324 replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'); 1325 prefix = new RegExp('^' + prefix.toLowerCase()); 1326 function re_lower(_,a){ 1327 return a.toLowerCase(); 1328 } 1329 for (var key in data) 1330 if (prefix.test(key)){ 1331 inkey = key.replace(replace, re_lower); 1332 out[inkey] = data[key]; 1333 } 1334 return out; 1335 } 1336 1337 function opts_from_locale(lang){ 1338 // Derive options from locale plugins 1339 var out = {}; 1340 // Check if "de-DE" style date is available, if not language should 1341 // fallback to 2 letter code eg "de" 1342 if (!dates[lang]){ 1343 lang = lang.split('-')[0]; 1344 if (!dates[lang]) 1345 return; 1346 } 1347 var d = dates[lang]; 1348 $.each(locale_opts, function(i,k){ 1349 if (k in d) 1350 out[k] = d[k]; 1351 }); 1352 return out; 1353 } 1354 1355 var old = $.fn.datepicker; 1356 $.fn.datepicker = function(option){ 1357 var args = Array.apply(null, arguments); 1358 args.shift(); 1359 var internal_return; 1360 this.each(function(){ 1361 var $this = $(this), 1362 data = $this.data('datepicker'), 1363 options = typeof option === 'object' && option; 1364 if (!data){ 1365 var elopts = opts_from_el(this, 'date'), 1366 // Preliminary otions 1367 xopts = $.extend({}, defaults, elopts, options), 1368 locopts = opts_from_locale(xopts.language), 1369 // Options priority: js args, data-attrs, locales, defaults 1370 opts = $.extend({}, defaults, locopts, elopts, options); 1371 if ($this.is('.input-daterange') || opts.inputs){ 1372 var ropts = { 1373 inputs: opts.inputs || $this.find('input').toArray() 1374 }; 1375 $this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts)))); 1376 } 1377 else { 1378 $this.data('datepicker', (data = new Datepicker(this, opts))); 1379 } 1380 } 1381 if (typeof option === 'string' && typeof data[option] === 'function'){ 1382 internal_return = data[option].apply(data, args); 1383 if (internal_return !== undefined) 1384 return false; 1385 } 1386 }); 1387 if (internal_return !== undefined) 1388 return internal_return; 1389 else 1390 return this; 1391 }; 1392 1393 var defaults = $.fn.datepicker.defaults = { 1394 autoclose: false, 1395 beforeShowDay: $.noop, 1396 calendarWeeks: false, 1397 clearBtn: false, 1398 daysOfWeekDisabled: [], 1399 endDate: Infinity, 1400 forceParse: true, 1401 format: 'mm/dd/yyyy', 1402 keyboardNavigation: true, 1403 language: 'en', 1404 minViewMode: 0, 1405 multidate: false, 1406 multidateSeparator: ',', 1407 orientation: "auto", 1408 rtl: false, 1409 startDate: -Infinity, 1410 startView: 0, 1411 todayBtn: false, 1412 todayHighlight: false, 1413 weekStart: 0 1414 }; 1415 var locale_opts = $.fn.datepicker.locale_opts = [ 1416 'format', 1417 'rtl', 1418 'weekStart' 1419 ]; 1420 $.fn.datepicker.Constructor = Datepicker; 1421 var dates = $.fn.datepicker.dates = { 1422 en: { 1423 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], 1424 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], 1425 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], 1426 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], 1427 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], 1428 today: "Today", 1429 clear: "Clear" 1430 } 1431 }; 1432 1433 var DPGlobal = { 1434 modes: [ 1435 { 1436 clsName: 'days', 1437 navFnc: 'Month', 1438 navStep: 1 1439 }, 1440 { 1441 clsName: 'months', 1442 navFnc: 'FullYear', 1443 navStep: 1 1444 }, 1445 { 1446 clsName: 'years', 1447 navFnc: 'FullYear', 1448 navStep: 10 1449 }], 1450 isLeapYear: function(year){ 1451 return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); 1452 }, 1453 getDaysInMonth: function(year, month){ 1454 return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; 1455 }, 1456 validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g, 1457 nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g, 1458 parseFormat: function(format){ 1459 // IE treats \0 as a string end in inputs (truncating the value), 1460 // so it's a bad format delimiter, anyway 1461 var separators = format.replace(this.validParts, '\0').split('\0'), 1462 parts = format.match(this.validParts); 1463 if (!separators || !separators.length || !parts || parts.length === 0){ 1464 throw new Error("Invalid date format."); 1465 } 1466 return {separators: separators, parts: parts}; 1467 }, 1468 parseDate: function(date, format, language){ 1469 if (!date) 1470 return undefined; 1471 if (date instanceof Date) 1472 return date; 1473 if (typeof format === 'string') 1474 format = DPGlobal.parseFormat(format); 1475 var part_re = /([\-+]\d+)([dmwy])/, 1476 parts = date.match(/([\-+]\d+)([dmwy])/g), 1477 part, dir, i; 1478 if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)){ 1479 date = new Date(); 1480 for (i=0; i < parts.length; i++){ 1481 part = part_re.exec(parts[i]); 1482 dir = parseInt(part[1]); 1483 switch (part[2]){ 1484 case 'd': 1485 date.setUTCDate(date.getUTCDate() + dir); 1486 break; 1487 case 'm': 1488 date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir); 1489 break; 1490 case 'w': 1491 date.setUTCDate(date.getUTCDate() + dir * 7); 1492 break; 1493 case 'y': 1494 date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir); 1495 break; 1496 } 1497 } 1498 return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0); 1499 } 1500 parts = date && date.match(this.nonpunctuation) || []; 1501 date = new Date(); 1502 var parsed = {}, 1503 setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'], 1504 setters_map = { 1505 yyyy: function(d,v){ 1506 return d.setUTCFullYear(v); 1507 }, 1508 yy: function(d,v){ 1509 return d.setUTCFullYear(2000+v); 1510 }, 1511 m: function(d,v){ 1512 if (isNaN(d)) 1513 return d; 1514 v -= 1; 1515 while (v < 0) v += 12; 1516 v %= 12; 1517 d.setUTCMonth(v); 1518 while (d.getUTCMonth() !== v) 1519 d.setUTCDate(d.getUTCDate()-1); 1520 return d; 1521 }, 1522 d: function(d,v){ 1523 return d.setUTCDate(v); 1524 } 1525 }, 1526 val, filtered; 1527 setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m']; 1528 setters_map['dd'] = setters_map['d']; 1529 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); 1530 var fparts = format.parts.slice(); 1531 // Remove noop parts 1532 if (parts.length !== fparts.length){ 1533 fparts = $(fparts).filter(function(i,p){ 1534 return $.inArray(p, setters_order) !== -1; 1535 }).toArray(); 1536 } 1537 // Process remainder 1538 function match_part(){ 1539 var m = this.slice(0, parts[i].length), 1540 p = parts[i].slice(0, m.length); 1541 return m === p; 1542 } 1543 if (parts.length === fparts.length){ 1544 var cnt; 1545 for (i=0, cnt = fparts.length; i < cnt; i++){ 1546 val = parseInt(parts[i], 10); 1547 part = fparts[i]; 1548 if (isNaN(val)){ 1549 switch (part){ 1550 case 'MM': 1551 filtered = $(dates[language].months).filter(match_part); 1552 val = $.inArray(filtered[0], dates[language].months) + 1; 1553 break; 1554 case 'M': 1555 filtered = $(dates[language].monthsShort).filter(match_part); 1556 val = $.inArray(filtered[0], dates[language].monthsShort) + 1; 1557 break; 1558 } 1559 } 1560 parsed[part] = val; 1561 } 1562 var _date, s; 1563 for (i=0; i < setters_order.length; i++){ 1564 s = setters_order[i]; 1565 if (s in parsed && !isNaN(parsed[s])){ 1566 _date = new Date(date); 1567 setters_map[s](_date, parsed[s]); 1568 if (!isNaN(_date)) 1569 date = _date; 1570 } 1571 } 1572 } 1573 return date; 1574 }, 1575 formatDate: function(date, format, language){ 1576 if (!date) 1577 return ''; 1578 if (typeof format === 'string') 1579 format = DPGlobal.parseFormat(format); 1580 var val = { 1581 d: date.getUTCDate(), 1582 D: dates[language].daysShort[date.getUTCDay()], 1583 DD: dates[language].days[date.getUTCDay()], 1584 m: date.getUTCMonth() + 1, 1585 M: dates[language].monthsShort[date.getUTCMonth()], 1586 MM: dates[language].months[date.getUTCMonth()], 1587 yy: date.getUTCFullYear().toString().substring(2), 1588 yyyy: date.getUTCFullYear() 1589 }; 1590 val.dd = (val.d < 10 ? '0' : '') + val.d; 1591 val.mm = (val.m < 10 ? '0' : '') + val.m; 1592 date = []; 1593 var seps = $.extend([], format.separators); 1594 for (var i=0, cnt = format.parts.length; i <= cnt; i++){ 1595 if (seps.length) 1596 date.push(seps.shift()); 1597 date.push(val[format.parts[i]]); 1598 } 1599 return date.join(''); 1600 }, 1601 headTemplate: '<thead>'+ 1602 '<tr>'+ 1603 '<th class="prev"><i class="fa fa-angle-left"></i></th>'+ 1604 '<th colspan="5" class="datepicker-switch"></th>'+ 1605 '<th class="next"><i class="fa fa-angle-right"></i></th>'+ 1606 '</tr>'+ 1607 '</thead>', 1608 contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>', 1609 footTemplate: '<tfoot>'+ 1610 '<tr>'+ 1611 '<th colspan="7" class="today"></th>'+ 1612 '</tr>'+ 1613 '<tr>'+ 1614 '<th colspan="7" class="clear"></th>'+ 1615 '</tr>'+ 1616 '</tfoot>' 1617 }; 1618 DPGlobal.template = '<div class="datepicker">'+ 1619 '<div class="datepicker-days">'+ 1620 '<table class=" table-condensed">'+ 1621 DPGlobal.headTemplate+ 1622 '<tbody></tbody>'+ 1623 DPGlobal.footTemplate+ 1624 '</table>'+ 1625 '</div>'+ 1626 '<div class="datepicker-months">'+ 1627 '<table class="table-condensed">'+ 1628 DPGlobal.headTemplate+ 1629 DPGlobal.contTemplate+ 1630 DPGlobal.footTemplate+ 1631 '</table>'+ 1632 '</div>'+ 1633 '<div class="datepicker-years">'+ 1634 '<table class="table-condensed">'+ 1635 DPGlobal.headTemplate+ 1636 DPGlobal.contTemplate+ 1637 DPGlobal.footTemplate+ 1638 '</table>'+ 1639 '</div>'+ 1640 '</div>'; 1641 1642 $.fn.datepicker.DPGlobal = DPGlobal; 1643 1644 1645 /* DATEPICKER NO CONFLICT 1646 * =================== */ 1647 1648 $.fn.datepicker.noConflict = function(){ 1649 $.fn.datepicker = old; 1650 return this; 1651 }; 1652 1653 1654 /* DATEPICKER DATA-API 1655 * ================== */ 1656 1657 $(document).on( 1658 'focus.datepicker.data-api click.datepicker.data-api', 1659 '[data-provide="datepicker"]', 1660 function(e){ 1661 var $this = $(this); 1662 if ($this.data('datepicker')) 1663 return; 1664 e.preventDefault(); 1665 // component click requires us to explicitly show it 1666 $this.datepicker('show'); 1667 } 1668 ); 1669 $(function(){ 1670 $('[data-provide="datepicker-inline"]').datepicker(); 1671 }); 1672 1673 }(window.jQuery));