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 31 function UTCToday() { 32 var today = new Date(); 33 return UTCDate(today.getFullYear(), today.getMonth(), today.getDate()); 34 } 35 36 function alias(method) { 37 return function () { 38 return this[method].apply(this, arguments); 39 }; 40 } 41 42 var DateArray = (function () { 43 var extras = { 44 get: function (i) { 45 return this.slice(i)[0]; 46 }, 47 contains: function (d) { 48 // Array.indexOf is not cross-browser; 49 // $.inArray doesn't work with Dates 50 var val = d && d.valueOf(); 51 for (var i = 0, l = this.length; i < l; i++) 52 if (this[i].valueOf() === val) 53 return i; 54 return -1; 55 }, 56 remove: function (i) { 57 this.splice(i, 1); 58 }, 59 replace: function (new_array) { 60 if (!new_array) 61 return; 62 if (!$.isArray(new_array)) 63 new_array = [new_array]; 64 this.clear(); 65 this.push.apply(this, new_array); 66 }, 67 clear: function () { 68 this.splice(0); 69 }, 70 copy: function () { 71 var a = new DateArray(); 72 a.replace(this); 73 return a; 74 } 75 }; 76 77 return function () { 78 var a = []; 79 a.push.apply(a, arguments); 80 $.extend(a, extras); 81 return a; 82 }; 83 })(); 84 85 86 // Picker object 87 88 var Datepicker = function (element, options) { 89 this.dates = new DateArray(); 90 this.viewDate = UTCToday(); 91 this.focusDate = null; 92 93 this._process_options(options); 94 95 this.element = $(element); 96 this.isInline = false; 97 this.isInput = this.element.is('input'); 98 this.component = this.element.is('.date') ? this.element.find('.add-on, .input-group-addon, .btn') : false; 99 this.hasInput = this.component && this.element.find('input').length; 100 if (this.component && this.component.length === 0) 101 this.component = false; 102 103 this.picker = $(DPGlobal.template); 104 this._buildEvents(); 105 this._attachEvents(); 106 107 if (this.isInline) { 108 this.picker.addClass('datepicker-inline').appendTo(this.element); 109 } 110 else { 111 this.picker.addClass('datepicker-dropdown dropdown-menu'); 112 } 113 114 if (this.o.rtl) { 115 this.picker.addClass('datepicker-rtl'); 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')) + 1151; 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 || !date 641 ); 642 }, this), true); 643 this.dates.replace(dates); 644 645 if (this.dates.length) 646 this.viewDate = new Date(this.dates.get(-1)); 647 else if (this.viewDate < this.o.startDate) 648 this.viewDate = new Date(this.o.startDate); 649 else if (this.viewDate > this.o.endDate) 650 this.viewDate = new Date(this.o.endDate); 651 652 if (fromArgs) { 653 // setting date by clicking 654 this.setValue(); 655 } 656 else if (dates.length) { 657 // setting date by typing 658 if (String(oldDates) !== String(this.dates)) 659 this._trigger('changeDate'); 660 } 661 if (!this.dates.length && oldDates.length) 662 this._trigger('clearDate'); 663 664 this.fill(); 665 }, 666 667 fillDow: function () { 668 var dowCnt = this.o.weekStart, 669 html = '<tr>'; 670 if (this.o.calendarWeeks) { 671 var cell = '<th class="cw"> </th>'; 672 html += cell; 673 this.picker.find('.datepicker-days thead tr:first-child').prepend(cell); 674 } 675 while (dowCnt < this.o.weekStart + 7) { 676 html += '<th class="dow">' + dates[this.o.language].daysMin[(dowCnt++) % 7] + '</th>'; 677 } 678 html += '</tr>'; 679 this.picker.find('.datepicker-days thead').append(html); 680 }, 681 682 fillMonths: function () { 683 var html = '', 684 i = 0; 685 while (i < 12) { 686 html += '<span class="month">' + dates[this.o.language].monthsShort[i++] + '</span>'; 687 } 688 this.picker.find('.datepicker-months td').html(html); 689 }, 690 691 setRange: function (range) { 692 if (!range || !range.length) 693 delete this.range; 694 else 695 this.range = $.map(range, function (d) { 696 return d.valueOf(); 697 }); 698 this.fill(); 699 }, 700 701 getClassNames: function (date) { 702 var cls = [], 703 year = this.viewDate.getUTCFullYear(), 704 month = this.viewDate.getUTCMonth(), 705 today = new Date(); 706 if (date.getUTCFullYear() < year || (date.getUTCFullYear() === year && date.getUTCMonth() < month)) { 707 cls.push('old'); 708 } 709 else if (date.getUTCFullYear() > year || (date.getUTCFullYear() === year && date.getUTCMonth() > month)) { 710 cls.push('new'); 711 } 712 if (this.focusDate && date.valueOf() === this.focusDate.valueOf()) 713 cls.push('focused'); 714 // Compare internal UTC date with local today, not UTC today 715 if (this.o.todayHighlight && 716 date.getUTCFullYear() === today.getFullYear() && 717 date.getUTCMonth() === today.getMonth() && 718 date.getUTCDate() === today.getDate()) { 719 cls.push('today'); 720 } 721 if (this.dates.contains(date) !== -1) 722 cls.push('active'); 723 if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate || 724 $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1) { 725 cls.push('disabled'); 726 } 727 if (this.range) { 728 if (date > this.range[0] && date < this.range[this.range.length - 1]) { 729 cls.push('range'); 730 } 731 if ($.inArray(date.valueOf(), this.range) !== -1) { 732 cls.push('selected'); 733 } 734 } 735 return cls; 736 }, 737 738 fill: function () { 739 var d = new Date(this.viewDate), 740 year = d.getUTCFullYear(),
741 month = d.getUTCMonth(), 742 startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity, 743 startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity, 744 endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity, 745 endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity, 746 todaytxt = dates[this.o.language].today || dates['en'].today || '', 747 cleartxt = dates[this.o.language].clear || dates['en'].clear || '', 748 tooltip; 749 this.picker.find('.datepicker-days thead th.datepicker-switch') 750 .text(dates[this.o.language].months[month] + ' ' + year); 751 this.picker.find('tfoot th.today') 752 .text(todaytxt) 753 .toggle(this.o.todayBtn !== false); 754 this.picker.find('tfoot th.clear') 755 .text(cleartxt) 756 .toggle(this.o.clearBtn !== false); 757 this.updateNavArrows(); 758 this.fillMonths(); 759 var prevMonth = UTCDate(year, month - 1, 28), 760 day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth()); 761 prevMonth.setUTCDate(day); 762 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7) % 7); 763 var nextMonth = new Date(prevMonth); 764 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42); 765 nextMonth = nextMonth.valueOf(); 766 var html = []; 767 var clsName; 768 while (prevMonth.valueOf() < nextMonth) { 769 if (prevMonth.getUTCDay() === this.o.weekStart) { 770 html.push('<tr>'); 771 if (this.o.calendarWeeks) { 772 // ISO 8601: First week contains first thursday. 773 // ISO also states week starts on Monday, but we can be more abstract here. 774 var 775 // Start of current week: based on weekstart/current date 776 ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5), 777 // Thursday of this week 778 th = new Date(Number(ws) + (7 + 4 - ws.getUTCDay()) % 7 * 864e5), 779 // First Thursday of year, year from thursday 780 yth = new Date(Number(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay()) % 7 * 864e5), 781 // Calendar week: ms between thursdays, div ms per day, div 7 days 782 calWeek = (th - yth) / 864e5 / 7 + 1; 783 html.push('<td class="cw">' + calWeek + '</td>'); 784 785 } 786 } 787 clsName = this.getClassNames(prevMonth); 788 clsName.push('day'); 789 790 if (this.o.beforeShowDay !== $.noop) { 791 var before = this.o.beforeShowDay(this._utc_to_local(prevMonth)); 792 if (before === undefined) 793 before = {}; 794 else if (typeof(before) === 'boolean') 795 before = {enabled: before}; 796 else if (typeof(before) === 'string') 797 before = {classes: before}; 798 if (before.enabled === false) 799 clsName.push('disabled'); 800 if (before.classes) 801 clsName = clsName.concat(before.classes.split(/\s+/)); 802 if (before.tooltip) 803 tooltip = before.tooltip; 804 } 805 806 clsName = $.unique(clsName); 807 html.push('<td class="' + clsName.join(' ') + '"' + (tooltip ? ' title="' + tooltip + '"' : '') + '>' + prevMonth.getUTCDate() + '</td>'); 808 if (prevMonth.getUTCDay() === this.o.weekEnd) { 809 html.push('</tr>'); 810 } 811 prevMonth.setUTCDate(prevMonth.getUTCDate() + 1); 812 } 813 this.picker.find('.datepicker-days tbody').empty().append(html.join('')); 814 815 var months = this.picker.find('.datepicker-months') 816 .find('th:eq(1)') 817 .text(year) 818 .end() 819 .find('span').removeClass('active'); 820 821 $.each(this.dates, function (i, d) { 822 if (d.getUTCFullYear() === year) 823 months.eq(d.getUTCMonth()).addClass('active'); 824 }); 825 826 if (year < startYear || year > endYear) { 827 months.addClass('disabled'); 828 } 829 if (year === startYear) { 830 months.slice(0, startMonth).addClass('disabled'); 831 } 832 if (year === endYear) { 833 months.slice(endMonth + 1).addClass('disabled'); 834 } 835 836 html = ''; 837 year = parseInt(year / 10, 10) * 10; 838 var yearCont = this.picker.find('.datepicker-years') 839 .find('th:eq(1)') 840 .text(year + '-' + (year + 9)) 841 .end() 842 .find('td'); 843 year -= 1; 844 var years = $.map(this.dates, function (d) { 845 return d.getUTCFullYear(); 846 }), 847 classes;
848 for (var i = -1; i < 11; i++) { 849 classes = ['year']; 850 if (i === -1) 851 classes.push('old'); 852 else if (i === 10) 853 classes.push('new'); 854 if ($.inArray(year, years) !== -1) 855 classes.push('active'); 856 if (year < startYear || year > endYear) 857 classes.push('disabled'); 858 html += '<span class="' + classes.join(' ') + '">' + year + '</span>'; 859 year += 1; 860 } 861 yearCont.html(html); 862 }, 863 864 updateNavArrows: function () { 865 if (!this._allow_update) 866 return; 867 868 var d = new Date(this.viewDate), 869 year = d.getUTCFullYear(), 870 month = d.getUTCMonth(); 871 switch (this.viewMode) { 872 case 0: 873 if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()) { 874 this.picker.find('.prev').css({visibility: 'hidden'}); 875 } 876 else { 877 this.picker.find('.prev').css({visibility: 'visible'}); 878 } 879 if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()) { 880 this.picker.find('.next').css({visibility: 'hidden'}); 881 } 882 else { 883 this.picker.find('.next').css({visibility: 'visible'}); 884 } 885 break; 886 case 1: 887 case 2: 888 if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()) { 889 this.picker.find('.prev').css({visibility: 'hidden'}); 890 } 891 else { 892 this.picker.find('.prev').css({visibility: 'visible'}); 893 } 894 if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()) { 895 this.picker.find('.next').css({visibility: 'hidden'}); 896 } 897 else { 898 this.picker.find('.next').css({visibility: 'visible'}); 899 } 900 break; 901 } 902 }, 903 904 click: function (e) { 905 e.preventDefault(); 906 var target = $(e.target).closest('span, td, th'), 907 year, month, day; 908 if (target.length === 1) { 909 switch (target[0].nodeName.toLowerCase()) { 910 case 'th': 911 switch (target[0].className) { 912 case 'datepicker-switch': 913 this.showMode(1); 914 break; 915 case 'prev': 916 case 'next': 917 var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1); 918 switch (this.viewMode) { 919 case 0: 920 this.viewDate = this.moveMonth(this.viewDate, dir); 921 this._trigger('changeMonth', this.viewDate); 922 break; 923 case 1: 924 case 2: 925 this.viewDate = this.moveYear(this.viewDate, dir); 926 if (this.viewMode === 1) 927 this._trigger('changeYear', this.viewDate); 928 break; 929 } 930 this.fill(); 931 break; 932 case 'today': 933 var date = new Date(); 934 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); 935 936 this.showMode(-2); 937 var which = this.o.todayBtn === 'linked' ? null : 'view'; 938 this._setDate(date, which); 939 break; 940 case 'clear': 941 var element; 942 if (this.isInput) 943 element = this.element; 944 else if (this.component) 945 element = this.element.find('input'); 946 if (element) 947 element.val("").change(); 948 this.update(); 949 this._trigger('changeDate'); 950 if (this.o.autoclose) 951 this.hide(); 952 break; 953 } 954 break; 955 case 'span': 956 if (!target.is('.disabled')) { 957 this.viewDate.setUTCDate(1); 958 if (target.is('.month')) { 959 day = 1;
960 month = target.parent().find('span').index(target); 961 year = this.viewDate.getUTCFullYear(); 962 this.viewDate.setUTCMonth(month); 963 this._trigger('changeMonth', this.viewDate); 964 if (this.o.minViewMode === 1) { 965 this._setDate(UTCDate(year, month, day)); 966 } 967 } 968 else { 969 day = 1; 970 month = 0; 971 year = parseInt(target.text(), 10) || 0; 972 this.viewDate.setUTCFullYear(year); 973 this._trigger('changeYear', this.viewDate); 974 if (this.o.minViewMode === 2) { 975 this._setDate(UTCDate(year, month, day)); 976 } 977 } 978 this.showMode(-1); 979 this.fill(); 980 } 981 break; 982 case 'td': 983 if (target.is('.day') && !target.is('.disabled')) { 984 day = parseInt(target.text(), 10) || 1; 985 year = this.viewDate.getUTCFullYear(); 986 month = this.viewDate.getUTCMonth(); 987 if (target.is('.old')) { 988 if (month === 0) { 989 month = 11; 990 year -= 1; 991 } 992 else { 993 month -= 1; 994 } 995 } 996 else if (target.is('.new')) { 997 if (month === 11) { 998 month = 0; 999 year += 1; 1000 } 1001 else { 1002 month += 1; 1003 } 1004 } 1005 this._setDate(UTCDate(year, month, day)); 1006 } 1007 break; 1008 } 1009 } 1010 if (this.picker.is(':visible') && this._focused_from) { 1011 $(this._focused_from).focus(); 1012 } 1013 delete this._focused_from; 1014 }, 1015 1016 _toggle_multidate: function (date) { 1017 var ix = this.dates.contains(date); 1018 if (!date) { 1019 this.dates.clear(); 1020 } 1021 else if (ix !== -1) { 1022 this.dates.remove(ix); 1023 } 1024 else { 1025 this.dates.push(date); 1026 } 1027 if (typeof this.o.multidate === 'number') 1028 while (this.dates.length > this.o.multidate) 1029 this.dates.remove(0); 1030 }, 1031 1032 _setDate: function (date, which) { 1033 if (!which || which === 'date') 1034 this._toggle_multidate(date && new Date(date)); 1035 if (!which || which === 'view') 1036 this.viewDate = date && new Date(date); 1037 1038 this.fill(); 1039 this.setValue(); 1040 this._trigger('changeDate'); 1041 var element; 1042 if (this.isInput) { 1043 element = this.element; 1044 } 1045 else if (this.component) { 1046 element = this.element.find('input'); 1047 } 1048 if (element) { 1049 element.change(); 1050 } 1051 if (this.o.autoclose && (!which || which === 'date')) { 1052 this.hide(); 1053 } 1054 }, 1055 1056 moveMonth: function (date, dir) { 1057 if (!date) 1058 return undefined; 1059 if (!dir) 1060 return date; 1061 var new_date = new Date(date.valueOf()), 1062 day = new_date.getUTCDate(),
1063 month = new_date.getUTCMonth(), 1064 mag = Math.abs(dir), 1065 new_month, test; 1066 dir = dir > 0 ? 1 : -1; 1067 if (mag === 1) { 1068 test = dir === -1 1069 // If going back one month, make sure month is not current month 1070 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02) 1071 ? function () { 1072 return new_date.getUTCMonth() === month; 1073 } 1074 // If going forward one month, make sure month is as expected 1075 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02) 1076 : function () { 1077 return new_date.getUTCMonth() !== new_month; 1078 }; 1079 new_month = month + dir; 1080 new_date.setUTCMonth(new_month); 1081 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11 1082 if (new_month < 0 || new_month > 11) 1083 new_month = (new_month + 12) % 12; 1084 } 1085 else { 1086 // For magnitudes >1, move one month at a time... 1087 for (var i = 0; i < mag; i++) 1088 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)... 1089 new_date = this.moveMonth(new_date, dir); 1090 // ...then reset the day, keeping it in the new month 1091 new_month = new_date.getUTCMonth(); 1092 new_date.setUTCDate(day); 1093 test = function () { 1094 return new_month !== new_date.getUTCMonth(); 1095 }; 1096 } 1097 // Common date-resetting loop -- if date is beyond end of month, make it 1098 // end of month 1099 while (test()) { 1100 new_date.setUTCDate(--day); 1101 new_date.setUTCMonth(new_month); 1102 } 1103 return new_date; 1104 }, 1105 1106 moveYear: function (date, dir) { 1107 return this.moveMonth(date, dir * 12); 1108 }, 1109 1110 dateWithinRange: function (date) { 1111 return date >= this.o.startDate && date <= this.o.endDate; 1112 }, 1113 1114 keydown: function (e) { 1115 if (this.picker.is(':not(:visible)')) { 1116 if (e.keyCode === 27) // allow escape to hide and re-show picker 1117 this.show(); 1118 return; 1119 } 1120 var dateChanged = false, 1121 dir, newDate, newViewDate, 1122 focusDate = this.focusDate || this.viewDate; 1123 switch (e.keyCode) { 1124 case 27: // escape 1125 if (this.focusDate) { 1126 this.focusDate = null; 1127 this.viewDate = this.dates.get(-1) || this.viewDate; 1128 this.fill(); 1129 } 1130 else 1131 this.hide(); 1132 e.preventDefault(); 1133 break; 1134 case 37: // left 1135 case 39: // right 1136 if (!this.o.keyboardNavigation) 1137 break; 1138 dir = e.keyCode === 37 ? -1 : 1; 1139 if (e.ctrlKey) { 1140 newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir); 1141 newViewDate = this.moveYear(focusDate, dir); 1142 this._trigger('changeYear', this.viewDate); 1143 } 1144 else if (e.shiftKey) { 1145 newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir); 1146 newViewDate = this.moveMonth(focusDate, dir); 1147 this._trigger('changeMonth', this.viewDate); 1148 } 1149 else { 1150 newDate = new Date(this.dates.get(-1) || UTCToday()); 1151 newDate.setUTCDate(newDate.getUTCDate() + dir); 1152 newViewDate = new Date(focusDate); 1153 newViewDate.setUTCDate(focusDate.getUTCDate() + dir); 1154 } 1155 if (this.dateWithinRange(newDate)) { 1156 this.focusDate = this.viewDate = newViewDate; 1157 this.setValue(); 1158 this.fill(); 1159 e.preventDefault(); 1160 } 1161 break; 1162 case 38: // up 1163 case 40: // down 1164 if (!this.o.keyboardNavigation) 1165 break; 1166 dir = e.keyCode === 38 ? -1 : 1; 1167 if (e.ctrlKey) { 1168 newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir); 1169 newViewDate = this.moveYear(focusDate, dir); 1170 this._trigger('changeYear', this.viewDate); 1171 } 1172 else if (e.shiftKey) { 1173 newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir); 1174 newViewDate = this.moveMonth(focusDate, dir); 1175 this._trigger('changeMonth', this.viewDate); 1176 } 1177 else { 1178 newDate = new Date(this.dates.get(-1) || UTCToday()); 1179 newDate.setUTCDate(newDate.getUTCDate() + dir * 7); 1180 newViewDate = new Date(focusDate); 1181 newViewDate.setUTCDate(focusDate.getUTCDate() + dir * 7); 1182 } 1183 if (this.dateWithinRange(newDate)) { 1184 this.focusDate = this.viewDate = newViewDate; 1185 this.setValue(); 1186 this.fill(); 1187 e.preventDefault(); 1188 } 1189 break; 1190 case 32: // spacebar 1191 // Spacebar is used in manually typing dates in some formats. 1192 // As such, its behavior should not be hijacked. 1193 break; 1194 case 13: // enter 1195 focusDate = this.focusDate || this.dates.get(-1) || this.viewDate; 1196 this._toggle_multidate(focusDate); 1197 dateChanged = true; 1198 this.focusDate = null; 1199 this.viewDate = this.dates.get(-1) || this.viewDate; 1200 this.setValue(); 1201 this.fill(); 1202 if (this.picker.is(':visible')) { 1203 e.preventDefault(); 1204 if (this.o.autoclose) 1205 this.hide(); 1206 } 1207 break; 1208 case 9: // tab 1209 this.focusDate = null; 1210 this.viewDate = this.dates.get(-1) || this.viewDate; 1211 this.fill(); 1212 this.hide(); 1213 break; 1214 } 1215 if (dateChanged) { 1216 if (this.dates.length) 1217 this._trigger('changeDate'); 1218 else 1219 this._trigger('clearDate'); 1220 var element; 1221 if (this.isInput) { 1222 element = this.element; 1223 } 1224 else if (this.component) { 1225 element = this.element.find('input'); 1226 } 1227 if (element) { 1228 element.change(); 1229 } 1230 } 1231 }, 1232 1233 showMode: function (dir) { 1234 if (dir) { 1235 this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir)); 1236 } 1237 this.picker 1238 .find('>div') 1239 .hide() 1240 .filter('.datepicker-' + DPGlobal.modes[this.viewMode].clsName) 1241 .css('display', 'block'); 1242 this.updateNavArrows(); 1243 } 1244 }; 1245 1246 var DateRangePicker = function (element, options) { 1247 this.element = $(element); 1248 this.inputs = $.map(options.inputs, function (i) { 1249 return i.jquery ? i[0] : i; 1250 }); 1251 delete options.inputs; 1252 1253 $(this.inputs) 1254 .datepicker(options) 1255 .bind('changeDate', $.proxy(this.dateUpdated, this)); 1256 1257 this.pickers = $.map(this.inputs, function (i) { 1258 return $(i).data('datepicker'); 1259 }); 1260 this.updateDates(); 1261 }; 1262 DateRangePicker.prototype = { 1263 updateDates: function () { 1264 this.dates = $.map(this.pickers, function (i) { 1265 return i.getUTCDate(); 1266 }); 1267 this.updateRanges(); 1268 }, 1269 updateRanges: function () { 1270 var range = $.map(this.dates, function (d) { 1271 return d.valueOf(); 1272 }); 1273 $.each(this.pickers, function (i, p) { 1274 p.setRange(range); 1275 }); 1276 }, 1277 dateUpdated: function (e) { 1278 // `this.updating` is a workaround for preventing infinite recursion 1279 // between `changeDate` triggering and `setUTCDate` calling. Until 1280 // there is a better mechanism. 1281 if (this.updating) 1282 return; 1283 this.updating = true; 1284 1285 var dp = $(e.target).data('datepicker'), 1286 new_date = dp.getUTCDate(), 1287 i = $.inArray(e.target, this.inputs), 1288 l = this.inputs.length; 1289 if (i === -1) 1290 return; 1291 1292 $.each(this.pickers, function (i, p) { 1293 if (!p.getUTCDate()) 1294 p.setUTCDate(new_date); 1295 }); 1296 1297 if (new_date < this.dates[i]) { 1298 // Date being moved earlier/left 1299 while (i >= 0 && new_date < this.dates[i]) { 1300 this.pickers[i--].setUTCDate(new_date); 1301 } 1302 } 1303 else if (new_date > this.dates[i]) { 1304 // Date being moved later/right 1305 while (i < l && new_date > this.dates[i]) { 1306 this.pickers[i++].setUTCDate(new_date); 1307 } 1308 } 1309 this.updateDates(); 1310 1311 delete this.updating; 1312 }, 1313 remove: function () { 1314 $.map(this.pickers, function (p) { 1315 p.remove(); 1316 }); 1317 delete this.element.data().datepicker; 1318 } 1319 }; 1320 1321 function opts_from_el(el, prefix) { 1322 // Derive options from element data-attrs 1323 var data = $(el).data(), 1324 out = {}, inkey, 1325 replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'); 1326 prefix = new RegExp('^' + prefix.toLowerCase()); 1327 function re_lower(_, a) { 1328 return a.toLowerCase(); 1329 } 1330 1331 for (var key in data) 1332 if (prefix.test(key)) { 1333 inkey = key.replace(replace, re_lower); 1334 out[inkey] = data[key]; 1335 } 1336 return out; 1337 } 1338 1339 function opts_from_locale(lang) { 1340 // Derive options from locale plugins 1341 var out = {}; 1342 // Check if "de-DE" style date is available, if not language should 1343 // fallback to 2 letter code eg "de" 1344 if (!dates[lang]) { 1345 lang = lang.split('-')[0]; 1346 if (!dates[lang]) 1347 return; 1348 } 1349 var d = dates[lang]; 1350 $.each(locale_opts, function (i, k) { 1351 if (k in d) 1352 out[k] = d[k]; 1353 }); 1354 return out; 1355 } 1356 1357 var old = $.fn.datepicker; 1358 $.fn.datepicker = function (option) { 1359 var args = Array.apply(null, arguments); 1360 args.shift(); 1361 var internal_return; 1362 this.each(function () { 1363 var $this = $(this), 1364 data = $this.data('datepicker'), 1365 options = typeof option === 'object' && option; 1366 if (!data) { 1367 var elopts = opts_from_el(this, 'date'), 1368 // Preliminary otions 1369 xopts = $.extend({}, defaults, elopts, options), 1370 locopts = opts_from_locale(xopts.language), 1371 // Options priority: js args, data-attrs, locales, defaults 1372 opts = $.extend({}, defaults, locopts, elopts, options); 1373 if ($this.is('.input-daterange') || opts.inputs) { 1374 var ropts = { 1375 inputs: opts.inputs || $this.find('input').toArray() 1376 }; 1377 $this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts)))); 1378 } 1379 else { 1380 $this.data('datepicker', (data = new Datepicker(this, opts))); 1381 } 1382 } 1383 if (typeof option === 'string' && typeof data[option] === 'function') { 1384 internal_return = data[option].apply(data, args); 1385 if (internal_return !== undefined) 1386 return false;
vendor: 1,348 bytes, lines 1387-1428
1387 } 1388 }); 1389 if (internal_return !== undefined) 1390 return internal_return; 1391 else 1392 return this; 1393 }; 1394 1395 var defaults = $.fn.datepicker.defaults = { 1396 autoclose: false, 1397 beforeShowDay: $.noop, 1398 calendarWeeks: false, 1399 clearBtn: false, 1400 daysOfWeekDisabled: [], 1401 endDate: Infinity, 1402 forceParse: true, 1403 format: 'mm/dd/yyyy', 1404 keyboardNavigation: true, 1405 language: 'en', 1406 minViewMode: 0, 1407 multidate: false, 1408 multidateSeparator: ',', 1409 orientation: "auto", 1410 rtl: false, 1411 startDate: -Infinity, 1412 startView: 0, 1413 todayBtn: false, 1414 todayHighlight: false, 1415 weekStart: 0 1416 }; 1417 var locale_opts = $.fn.datepicker.locale_opts = [ 1418 'format', 1419 'rtl', 1420 'weekStart' 1421 ]; 1422 $.fn.datepicker.Constructor = Datepicker; 1423 var dates = $.fn.datepicker.dates = { 1424 en: { 1425 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], 1426 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], 1427 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], 1428 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
1428November", "December"], 1429 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], 1430 today: "Today", 1431 clear: "Clear" 1432 } 1433 }; 1434 1435 var DPGlobal = { 1436 modes: [ 1437 { 1438 clsName: 'days', 1439 navFnc: 'Month', 1440 navStep: 1 1441 }, 1442 { 1443 clsName: 'months', 1444 navFnc: 'FullYear', 1445 navStep: 1 1446 }, 1447 { 1448 clsName: 'years', 1449 navFnc: 'FullYear', 1450 navStep: 10 1451 }], 1452 isLeapYear: function (year) { 1453 return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); 1454 }, 1455 getDaysInMonth: function (year, month) { 1456 return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; 1457 }, 1458 validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g, 1459 nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g, 1460 parseFormat: function (format) { 1461 // IE treats \0 as a string end in inputs (truncating the value), 1462 // so it's a bad format delimiter, anyway 1463 var separators = format.replace(this.validParts, '\0').split('\0'), 1464 parts = format.match(this.validParts); 1465 if (!separators || !separators.length || !parts || parts.length === 0) { 1466 throw new Error("Invalid date format."); 1467 } 1468 return {separators: separators, parts: parts}; 1469 }, 1470 parseDate: function (date, format, language) { 1471 if (!date) 1472 return undefined; 1473 if (date instanceof Date) 1474 return date; 1475 if (typeof format === 'string') 1476 format = DPGlobal.parseFormat(format); 1477 var part_re = /([\-+]\d+)([dmwy])/, 1478 parts = date.match(/([\-+]\d+)([dmwy])/g), 1479 part, dir, i; 1480 if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) { 1481 date = new Date(); 1482 for (i = 0; i < parts.length; i++) { 1483 part = part_re.exec(parts[i]); 1484 dir = parseInt(part[1]); 1485 switch (part[2]) { 1486 case 'd': 1487 date.setUTCDate(date.getUTCDate() + dir); 1488 break; 1489 case 'm': 1490 date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir); 1491 break; 1492 case 'w': 1493 date.setUTCDate(date.getUTCDate() + dir * 7); 1494 break; 1495 case 'y': 1496 date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir); 1497 break; 1498 } 1499 } 1500 return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0); 1501 } 1502 parts = date && date.match(this.nonpunctuation) || []; 1503 date = new Date(); 1504 var parsed = {}, 1505 setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'], 1506 setters_map = { 1507 yyyy: function (d, v) { 1508 return d.setUTCFullYear(v); 1509 }, 1510 yy: function (d, v) { 1511 return d.setUTCFullYear(2000 + v); 1512 }, 1513 m: function (d, v) { 1514 if (isNaN(d)) 1515 return d; 1516 v -= 1; 1517 while (v < 0) v += 12; 1518 v %= 12; 1519 d.setUTCMonth(v); 1520 while (d.getUTCMonth() !== v) 1521 d.setUTCDate(d.getUTCDate() - 1); 1522 return d; 1523 }, 1524 d: function (d, v) { 1525 return d.setUTCDate(v); 1526 } 1527 }, 1528 val, filtered; 1529 setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m']; 1530 setters_map['dd'] = setters_map['d']; 1531 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); 1532 var fparts = format.parts.slice(); 1533 // Remove noop parts 1534 if (parts.length !== fparts.length) { 1535 fparts = $(fparts).filter(function (i, p) { 1536 return $.inArray(p, setters_order) !== -1; 1537 }).toArray(); 1538 } 1539 // Process remainder 1540 function match_part() { 1541 var m = this.slice(0, parts[i].length), 1542 p = parts[i].slice(0, m.length); 1543 return m === p; 1544 } 1545 1546 if (parts.length === fparts.length) { 1547 var cnt; 1548 for (i = 0, cnt = fparts.length; i < cnt; i++) { 1549 val = parseInt(parts[i], 10); 1550 part = fparts[i]; 1551 if (isNaN(val)) { 1552 switch (part) { 1553 case 'MM': 1554 filtered = $(dates[language].months).filter(match_part); 1555 val = $.inArray(filtered[0], dates[language].months) + 1; 1556 break; 1557 case 'M': 1558 filtered = $(dates[language].monthsShort).filter(match_part); 1559 val = $.inArray(filtered[0], dates[language].monthsShort) + 1; 1560 break; 1561 } 1562 } 1563 parsed[part] = val; 1564 } 1565 var _date, s; 1566 for (i = 0; i < setters_order.length; i++) { 1567 s = setters_order[i]; 1568 if (s in parsed && !isNaN(parsed[s])) { 1569 _date = new Date(date); 1570 setters_map[s](_date, parsed[s]); 1571 if (!isNaN(_date)) 1572 date = _date; 1573 } 1574 } 1575 } 1576 return date; 1577 }, 1578 formatDate: function (date, format, language) { 1579 if (!date) 1580 return ''; 1581 if (typeof format === 'string') 1582 format = DPGlobal.parseFormat(format); 1583 var val = { 1584 d: date.getUTCDate(), 1585 D: dates[language].daysShort[date.getUTCDay()], 1586 DD: dates[language].days[date.getUTCDay()], 1587 m: date.getUTCMonth() + 1, 1588 M: dates[language].monthsShort[date.getUTCMonth()], 1589 MM: dates[language].months[date.getUTCMonth()], 1590 yy: date.getUTCFullYear().toString().substring(2), 1591 yyyy: date.getUTCFullYear() 1592 }; 1593 val.dd = (val.d < 10 ? '0' : '') + val.d; 1594 val.mm = (val.m < 10 ? '0' : '') + val.m; 1595 date = []; 1596 var seps = $.extend([], format.separators); 1597 for (var i = 0, cnt = format.parts.length; i <= cnt; i++) { 1598 if (seps.length) 1599 date.push(seps.shift()); 1600 date.push(val[format.parts[i]]); 1601 } 1602 return date.join(''); 1603 }, 1604 headTemplate: '<thead>' + 1605 '<tr>' + 1606 '<th class="prev">«</th>' + 1607 '<th colspan="5" class="datepicker-switch"></th>' + 1608 '<th class="next">»</th>' + 1609 '</tr>' + 1610 '</thead>', 1611 contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>', 1612 footTemplate: '<tfoot>' + 1613 '<tr>' + 1614 '<th colspan="7" class="today"></th>' + 1615 '</tr>' + 1616 '<tr>' + 1617 '<th colspan="7" class="clear"></th>' + 1618 '</tr>' + 1619 '</tfoot>' 1620 }; 1621 DPGlobal.template = '<div class="datepicker">' + 1622 '<div class="datepicker-days">' + 1623 '<table class=" table-condensed">' + 1624 DPGlobal.headTemplate + 1625 '<tbody></tbody>' + 1626 DPGlobal.footTemplate + 1627 '</table>' + 1628 '</div>' + 1629 '<div class="datepicker-months">' + 1630 '<table class="table-condensed">' + 1631 DPGlobal.headTemplate + 1632 DPGlobal.contTemplate + 1633 DPGlobal.footTemplate + 1634 '</table>' + 1635 '</div>' + 1636 '<div class="datepicker-years">' + 1637 '<table class="table-condensed">' + 1638 DPGlobal.headTemplate + 1639 DPGlobal.contTemplate + 1640 DPGlobal.footTemplate + 1641 '</table>' + 1642 '</div>' + 1643 '</div>'; 1644 1645 $.fn.datepicker.DPGlobal = DPGlobal; 1646 1647 1648 /* DATEPICKER NO CONFLICT 1649 * =================== */ 1650 1651 $.fn.datepicker.noConflict = function () { 1652 $.fn.datepicker = old; 1653 return this; 1654 }; 1655 1656 1657 /* DATEPICKER DATA-API 1658 * ================== */ 1659 1660 $(document).on(
1661 'focus.datepicker.data-api click.datepicker.data-api', 1662 '[data-provide="datepicker"]', 1663 function (e) { 1664 var $this = $(this); 1665 if ($this.data('datepicker')) 1666 return; 1667 e.preventDefault(); 1668 // component click requires us to explicitly show it 1669 $this.datepicker('show'); 1670 } 1671 ); 1672 $(function () { 1673 $('[data-provide="datepicker-inline"]').datepicker(); 1674 }); 1675 1676}(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.