vendor: 1,998 bytes, lines 1-60
1/* ========================================================= 2 * bootstrap-datepicker.js 3 * http://www.eyecon.ro/bootstrap-datepicker 4 * ========================================================= 5 * Copyright 2012 Stefan Petre 6 * Improvements by Andrew Rowls 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * ========================================================= */ 20 21 22(function( $ ) { 23 24 function UTCDate(){ 25 return new Date(Date.UTC.apply(Date, arguments)); 26 } 27 function UTCToday(){ 28 var today = new Date(); 29 return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()); 30 } 31 32 // Picker object 33 34 var Datepicker = function(element, options) { 35 var that = this; 36 37 this._process_options(options); 38 39 this.element = $(element); 40 this.isInline = false; 41 this.isInput = this.element.is('input'); 42 this.component = this.element.is('.date') ? this.element.find('.add-on, .btn, .input-group-addon') : false; 43 this.hasInput = this.component && this.element.find('input').length; 44 if(this.component && this.component.length === 0) 45 this.component = false; 46 47 this.picker = $(DPGlobal.template); 48 this._buildEvents(); 49 this._attachEvents(); 50 51 if(this.isInline) { 52 this.picker.addClass('datepicker-inline').appendTo(this.element); 53 } else { 54 this.picker.addClass('datepicker-dropdown dropdown-menu'); 55 } 56 57 if (this.o.rtl){ 58 this.picker.addClass('datepicker-rtl'); 59 this.picker.find('.prev i, .next i') 60 .toggleClass('glyphicon glyphicon-arrow-left glyph
vendor: 26,199 bytes, lines 60-962
60icon-arrow-right'); 61 } 62 63 64 this.viewMode = this.o.startView; 65 66 if (this.o.calendarWeeks) 67 this.picker.find('tfoot th.today') 68 .attr('colspan', function(i, val){ 69 return parseInt(val) + 1; 70 }); 71 72 this._allow_update = false; 73 74 this.setStartDate(this.o.startDate); 75 this.setEndDate(this.o.endDate); 76 this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled); 77 78 this.fillDow(); 79 this.fillMonths(); 80 81 this._allow_update = true; 82 83 this.update(); 84 this.showMode(); 85 86 if(this.isInline) { 87 this.show(); 88 } 89 }; 90 91 Datepicker.prototype = { 92 constructor: Datepicker, 93 94 _process_options: function(opts){ 95 // Store raw options for reference 96 this._o = $.extend({}, this._o, opts); 97 // Processed options 98 var o = this.o = $.extend({}, this._o); 99 100 // Check if "de-DE" style date is available, if not language should 101 // fallback to 2 letter code eg "de" 102 var lang = o.language; 103 if (!dates[lang]) { 104 lang = lang.split('-')[0]; 105 if (!dates[lang]) 106 lang = defaults.language; 107 } 108 o.language = lang; 109 110 switch(o.startView){ 111 case 2: 112 case 'decade': 113 o.startView = 2; 114 break; 115 case 1: 116 case 'year': 117 o.startView = 1; 118 break; 119 default: 120 o.startView = 0; 121 } 122 123 switch (o.minViewMode) { 124 case 1: 125 case 'months': 126 o.minViewMode = 1; 127 break; 128 case 2: 129 case 'years': 130 o.minViewMode = 2; 131 break; 132 default: 133 o.minViewMode = 0; 134 } 135 136 o.startView = Math.max(o.startView, o.minViewMode); 137 138 o.weekStart %= 7; 139 o.weekEnd = ((o.weekStart + 6) % 7); 140 141 var format = DPGlobal.parseFormat(o.format) 142 if (o.startDate !== -Infinity) { 143 o.startDate = DPGlobal.parseDate(o.startDate, format, o.language); 144 } 145 if (o.endDate !== Infinity) { 146 o.endDate = DPGlobal.parseDate(o.endDate, format, o.language); 147 } 148 149 o.daysOfWeekDisabled = o.daysOfWeekDisabled||[]; 150 if (!$.isArray(o.daysOfWeekDisabled)) 151 o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/); 152 o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function (d) { 153 return parseInt(d, 10); 154 }); 155 }, 156 _events: [], 157 _secondaryEvents: [], 158 _applyEvents: function(evs){ 159 for (var i=0, el, ev; i<evs.length; i++){ 160 el = evs[i][0]; 161 ev = evs[i][1]; 162 el.on(ev); 163 } 164 }, 165 _unapplyEvents: function(evs){ 166 for (var i=0, el, ev; i<evs.length; i++){ 167 el = evs[i][0]; 168 ev = evs[i][1]; 169 el.off(ev); 170 } 171 }, 172 _buildEvents: function(){ 173 if (this.isInput) { // single input 174 this._events = [ 175 [this.element, { 176 focus: $.proxy(this.show, this), 177 keyup: $.proxy(this.update, this), 178 keydown: $.proxy(this.keydown, this) 179 }] 180 ]; 181 } 182 else if (this.component && this.hasInput){ // component: input + button 183 this._events = [ 184 // For components that are not readonly, allow keyboard nav 185 [this.element.find('input'), { 186 focus: $.proxy(this.show, this), 187 keyup: $.proxy(this.update, this), 188 keydown: $.proxy(this.keydown, this) 189 }], 190 [this.component, { 191 click: $.proxy(this.show, this) 192 }] 193 ]; 194 } 195 else if (this.element.is('div')) { // inline datepicker 196 this.isInline = true; 197 } 198 else { 199 this._events = [ 200 [this.element, { 201 click: $.proxy(this.show, this) 202 }] 203 ]; 204 } 205 206 this._secondaryEvents = [ 207 [this.picker, { 208 click: $.proxy(this.click, this) 209 }], 210 [$(window), { 211 resize: $.proxy(this.place, this) 212 }], 213 [$(document), { 214 mousedown: $.proxy(function (e) { 215 // Clicked outside the datepicker, hide it 216 if (!( 217 this.element.is(e.target) || 218 this.element.find(e.target).size() || 219 this.picker.is(e.target) || 220 this.picker.find(e.target).size() 221 )) { 222 this.hide(); 223 } 224 }, this) 225 }] 226 ]; 227 }, 228 _attachEvents: function(){ 229 this._detachEvents(); 230 this._applyEvents(this._events); 231 }, 232 _detachEvents: function(){ 233 this._unapplyEvents(this._events); 234 }, 235 _attachSecondaryEvents: function(){ 236 this._detachSecondaryEvents(); 237 this._applyEvents(this._secondaryEvents); 238 }, 239 _detachSecondaryEvents: function(){ 240 this._unapplyEvents(this._secondaryEvents); 241 }, 242 _trigger: function(event, altdate){ 243 var date = altdate || this.date, 244 local_date = new Date(date.getTime() + (date.getTimezoneOffset()*60000)); 245 246 this.element.trigger({ 247 type: event, 248 date: local_date, 249 format: $.proxy(function(altformat){ 250 var format = altformat || this.o.format; 251 return DPGlobal.formatDate(date, format, this.o.language); 252 }, this) 253 }); 254 }, 255 256 show: function(e) { 257 if (!this.isInline) 258 this.picker.appendTo('body'); 259 this.picker.show(); 260 this.height = this.component ? this.component.outerHeight() : this.element.outerHeight(); 261 this.place(); 262 this._attachSecondaryEvents(); 263 if (e) { 264 e.preventDefault(); 265 } 266 this._trigger('show'); 267 }, 268 269 hide: function(e){ 270 if(this.isInline) return; 271 if (!this.picker.is(':visible')) return; 272 this.picker.hide().detach(); 273 this._detachSecondaryEvents(); 274 this.viewMode = this.o.startView; 275 this.showMode(); 276 277 if ( 278 this.o.forceParse && 279 ( 280 this.isInput && this.element.val() || 281 this.hasInput && this.element.find('input').val() 282 ) 283 ) 284 this.setValue(); 285 this._trigger('hide'); 286 }, 287 288 remove: function() { 289 this.hide(); 290 this._detachEvents(); 291 this._detachSecondaryEvents(); 292 this.picker.remove(); 293 delete this.element.data().datepicker; 294 if (!this.isInput) { 295 delete this.element.data().date; 296 } 297 }, 298 299 getDate: function() { 300 var d = this.getUTCDate(); 301 return new Date(d.getTime() + (d.getTimezoneOffset()*60000)); 302 }, 303 304 getUTCDate: function() { 305 return this.date; 306 }, 307 308 setDate: function(d) { 309 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000))); 310 }, 311 312 setUTCDate: function(d) { 313 this.date = d; 314 this.setValue(); 315 }, 316 317 setValue: function() { 318 var formatted = this.getFormattedDate(); 319 if (!this.isInput) { 320 if (this.component){ 321 this.element.find('input').val(formatted); 322 } 323 } else { 324 this.element.val(formatted); 325 } 326 }, 327 328 getFormattedDate: function(format) { 329 if (format === undefined) 330 format = this.o.format; 331 return DPGlobal.formatDate(this.date, format, this.o.language); 332 }, 333 334 setStartDate: function(startDate){ 335 this._process_options({startDate: startDate}); 336 this.update(); 337 this.updateNavArrows(); 338 }, 339 340 setEndDate: function(endDate){ 341 this._process_options({endDate: endDate}); 342 this.update(); 343 this.updateNavArrows(); 344 }, 345 346 setDaysOfWeekDisabled: function(daysOfWeekDisabled){ 347 this._process_options({daysOfWeekDisabled: daysOfWeekDisabled}); 348 this.update(); 349 this.updateNavArrows(); 350 }, 351 352 place: function(){ 353 if(this.isInline) return; 354 var zIndex = parseInt(this.element.parents().filter(function() { 355 return $(this).css('z-index') != 'auto'; 356 }).first().css('z-index'))+10; 357 var offset = this.component ? this.component.parent().offset() : this.element.offset(); 358 var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(true); 359 this.picker.css({ 360 top: offset.top + height, 361 left: offset.left, 362 zIndex: zIndex 363 }); 364 }, 365 366 _allow_update: true, 367 update: function(){ 368 if (!this._allow_update) return; 369 370 var date, fromArgs = false; 371 if(arguments && arguments.length && (typeof arguments[0] === 'string' || arguments[0] instanceof Date)) { 372 date = arguments[0]; 373 fromArgs = true; 374 } else { 375 date = this.isInput ? this.element.val() : this.element.data('date') || this.element.find('input').val(); 376 delete this.element.data().date; 377 } 378 379 this.date = DPGlobal.parseDate(date, this.o.format, this.o.language); 380 381 if(fromArgs) this.setValue(); 382 383 if (this.date < this.o.startDate) { 384 this.viewDate = new Date(this.o.startDate); 385 } else if (this.date > this.o.endDate) { 386 this.viewDate = new Date(this.o.endDate); 387 } else { 388 this.viewDate = new Date(this.date); 389 } 390 this.fill(); 391 }, 392 393 fillDow: function(){ 394 var dowCnt = this.o.weekStart, 395 html = '<tr>'; 396 if(this.o.calendarWeeks){ 397 var cell = '<th class="cw"> </th>'; 398 html += cell; 399 this.picker.find('.datepicker-days thead tr:first-child').prepend(cell); 400 } 401 while (dowCnt < this.o.weekStart + 7) { 402 html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>'; 403 } 404 html += '</tr>'; 405 this.picker.find('.datepicker-days thead').append(html); 406 }, 407 408 fillMonths: function(){ 409 var html = '', 410 i = 0; 411 while (i < 12) { 412 html += '<span class="month">'+dates[this.o.language].monthsShort[i++]+'</span>'; 413 } 414 this.picker.find('.datepicker-months td').html(html); 415 }, 416 417 setRange: function(range){ 418 if (!range || !range.length) 419 delete this.range; 420 else 421 this.range = $.map(range, function(d){ return d.valueOf(); }); 422 this.fill(); 423 }, 424 425 getClassNames: function(date){ 426 var cls = [], 427 year = this.viewDate.getUTCFullYear(), 428 month = this.viewDate.getUTCMonth(), 429 currentDate = this.date.valueOf(), 430 today = new Date(); 431 if (date.getUTCFullYear() < year || (date.getUTCFullYear() == year && date.getUTCMonth() < month)) { 432 cls.push('old'); 433 } else if (date.getUTCFullYear() > year || (date.getUTCFullYear() == year && date.getUTCMonth() > month)) { 434 cls.push('new'); 435 } 436 // Compare internal UTC date with local today, not UTC today 437 if (this.o.todayHighlight && 438 date.getUTCFullYear() == today.getFullYear() && 439 date.getUTCMonth() == today.getMonth() && 440 date.getUTCDate() == today.getDate()) { 441 cls.push('today'); 442 } 443 if (currentDate && date.valueOf() == currentDate) { 444 cls.push('active'); 445 } 446 if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate || 447 $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1) { 448 cls.push('disabled'); 449 } 450 if (this.range){ 451 if (date > this.range[0] && date < this.range[this.range.length-1]){ 452 cls.push('range'); 453 } 454 if ($.inArray(date.valueOf(), this.range) != -1){ 455 cls.push('selected'); 456 } 457 } 458 return cls; 459 }, 460 461 fill: function() { 462 var d = new Date(this.viewDate), 463 year = d.getUTCFullYear(), 464 month = d.getUTCMonth(), 465 startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity, 466 startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity, 467 endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity, 468 endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity, 469 currentDate = this.date && this.date.valueOf(), 470 tooltip; 471 this.picker.find('.datepicker-days thead th.datepicker-switch') 472 .text(dates[this.o.language].months[month]+' '+year); 473 this.picker.find('tfoot th.today') 474 .text(dates[this.o.language].today) 475 .toggle(this.o.todayBtn !== false); 476 this.picker.find('tfoot th.clear') 477 .text(dates[this.o.language].clear) 478 .toggle(this.o.clearBtn !== false); 479 this.updateNavArrows(); 480 this.fillMonths(); 481 var prevMonth = UTCDate(year, month-1, 28,0,0,0,0), 482 day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth()); 483 prevMonth.setUTCDate(day); 484 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7); 485 var nextMonth = new Date(prevMonth); 486 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42); 487 nextMonth = nextMonth.valueOf(); 488 var html = []; 489 var clsName; 490 while(prevMonth.valueOf() < nextMonth) { 491 if (prevMonth.getUTCDay() == this.o.weekStart) { 492 html.push('<tr>'); 493 if(this.o.calendarWeeks){ 494 // ISO 8601: First week contains first thursday. 495 // ISO also states week starts on Monday, but we can be more abstract here. 496 var 497 // Start of current week: based on weekstart/current date 498 ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5), 499 // Thursday of this week 500 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5), 501 // First Thursday of year, year from thursday 502 yth = new Date(+(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5), 503 // Calendar week: ms between thursdays, div ms per day, div 7 days 504 calWeek = (th - yth) / 864e5 / 7 + 1; 505 html.push('<td class="cw">'+ calWeek +'</td>'); 506 507 } 508 } 509 clsName = this.getClassNames(prevMonth); 510 clsName.push('day'); 511 512 var before = this.o.beforeShowDay(prevMonth); 513 if (before === undefined) 514 before = {}; 515 else if (typeof(before) === 'boolean') 516 before = {enabled: before}; 517 else if (typeof(before) === 'string') 518 before = {classes: before}; 519 if (before.enabled === false) 520 clsName.push('disabled'); 521 if (before.classes) 522 clsName = clsName.concat(before.classes.split(/\s+/)); 523 if (before.tooltip) 524 tooltip = before.tooltip; 525 526 clsName = $.unique(clsName); 527 html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + '>'+prevMonth.getUTCDate() + '</td>'); 528 if (prevMonth.getUTCDay() == this.o.weekEnd) { 529 html.push('</tr>'); 530 } 531 prevMonth.setUTCDate(prevMonth.getUTCDate()+1); 532 } 533 this.picker.find('.datepicker-days tbody').empty().append(html.join('')); 534 var currentYear = this.date && this.date.getUTCFullYear(); 535 536 var months = this.picker.find('.datepicker-months') 537 .find('th:eq(1)') 538 .text(year) 539 .end() 540 .find('span').removeClass('active'); 541 if (currentYear && currentYear == year) { 542 months.eq(this.date.getUTCMonth()).addClass('active'); 543 } 544 if (year < startYear || year > endYear) { 545 months.addClass('disabled'); 546 } 547 if (year == startYear) { 548 months.slice(0, startMonth).addClass('disabled'); 549 } 550 if (year == endYear) { 551 months.slice(endMonth+1).addClass('disabled'); 552 } 553 554 html = ''; 555 year = parseInt(year/10, 10) * 10; 556 var yearCont = this.picker.find('.datepicker-years') 557 .find('th:eq(1)') 558 .text(year + '-' + (year + 9)) 559 .end() 560 .find('td'); 561 year -= 1; 562 for (var i = -1; i < 11; i++) { 563 html += '<span class="year'+(i == -1 ? ' old' : i == 10 ? ' new' : '')+(currentYear == year ? ' active' : '')+(year < startYear || year > endYear ? ' disabled' : '')+'">'+year+'</span>'; 564 year += 1; 565 } 566 yearCont.html(html); 567 }, 568 569 updateNavArrows: function() { 570 if (!this._allow_update) return; 571 572 var d = new Date(this.viewDate), 573 year = d.getUTCFullYear(), 574 month = d.getUTCMonth(); 575 switch (this.viewMode) { 576 case 0: 577 if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()) { 578 this.picker.find('.prev').css({visibility: 'hidden'}); 579 } else { 580 this.picker.find('.prev').css({visibility: 'visible'}); 581 } 582 if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()) { 583 this.picker.find('.next').css({visibility: 'hidden'}); 584 } else { 585 this.picker.find('.next').css({visibility: 'visible'}); 586 } 587 break; 588 case 1: 589 case 2: 590 if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()) { 591 this.picker.find('.prev').css({visibility: 'hidden'}); 592 } else { 593 this.picker.find('.prev').css({visibility: 'visible'}); 594 } 595 if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()) { 596 this.picker.find('.next').css({visibility: 'hidden'}); 597 } else { 598 this.picker.find('.next').css({visibility: 'visible'}); 599 } 600 break; 601 } 602 }, 603 604 click: function(e) { 605 e.preventDefault(); 606 var target = $(e.target).closest('span, td, th'); 607 if (target.length == 1) { 608 switch(target[0].nodeName.toLowerCase()) { 609 case 'th': 610 switch(target[0].className) { 611 case 'datepicker-switch': 612 this.showMode(1); 613 break; 614 case 'prev': 615 case 'next': 616 var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1); 617 switch(this.viewMode){ 618 case 0: 619 this.viewDate = this.moveMonth(this.viewDate, dir); 620 break; 621 case 1: 622 case 2: 623 this.viewDate = this.moveYear(this.viewDate, dir); 624 break; 625 } 626 this.fill(); 627 break; 628 case 'today': 629 var date = new Date(); 630 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); 631 632 this.showMode(-2); 633 var which = this.o.todayBtn == 'linked' ? null : 'view'; 634 this._setDate(date, which); 635 break; 636 case 'clear': 637 var element; 638 if (this.isInput) 639 element = this.element; 640 else if (this.component) 641 element = this.element.find('input'); 642 if (element) 643 element.val("").change(); 644 this._trigger('changeDate'); 645 this.update(); 646 if (this.o.autoclose) 647 this.hide(); 648 break; 649 } 650 break; 651 case 'span': 652 if (!target.is('.disabled')) { 653 this.viewDate.setUTCDate(1); 654 if (target.is('.month')) { 655 var day = 1; 656 var month = target.parent().find('span').index(target); 657 var year = this.viewDate.getUTCFullYear(); 658 this.viewDate.setUTCMonth(month); 659 this._trigger('changeMonth', this.viewDate); 660 if (this.o.minViewMode === 1) { 661 this._setDate(UTCDate(year, month, day,0,0,0,0)); 662 } 663 } else { 664 var year = parseInt(target.text(), 10)||0; 665 var day = 1; 666 var month = 0; 667 this.viewDate.setUTCFullYear(year); 668 this._trigger('changeYear', this.viewDate); 669 if (this.o.minViewMode === 2) { 670 this._setDate(UTCDate(year, month, day,0,0,0,0)); 671 } 672 } 673 this.showMode(-1); 674 this.fill(); 675 } 676 break; 677 case 'td': 678 if (target.is('.day') && !target.is('.disabled')){ 679 var day = parseInt(target.text(), 10)||1; 680 var year = this.viewDate.getUTCFullYear(), 681 month = this.viewDate.getUTCMonth(); 682 if (target.is('.old')) { 683 if (month === 0) { 684 month = 11; 685 year -= 1; 686 } else { 687 month -= 1; 688 } 689 } else if (target.is('.new')) { 690 if (month == 11) { 691 month = 0; 692 year += 1; 693 } else { 694 month += 1; 695 } 696 } 697 this._setDate(UTCDate(year, month, day,0,0,0,0)); 698 } 699 break; 700 } 701 } 702 }, 703 704 _setDate: function(date, which){ 705 if (!which || which == 'date') 706 this.date = new Date(date); 707 if (!which || which == 'view') 708 this.viewDate = new Date(date); 709 this.fill(); 710 this.setValue(); 711 this._trigger('changeDate'); 712 var element; 713 if (this.isInput) { 714 element = this.element; 715 } else if (this.component){ 716 element = this.element.find('input'); 717 } 718 if (element) { 719 element.change(); 720 if (this.o.autoclose && (!which || which == 'date')) { 721 this.hide(); 722 } 723 } 724 }, 725 726 moveMonth: function(date, dir){ 727 if (!dir) return date; 728 var new_date = new Date(date.valueOf()), 729 day = new_date.getUTCDate(), 730 month = new_date.getUTCMonth(), 731 mag = Math.abs(dir), 732 new_month, test; 733 dir = dir > 0 ? 1 : -1; 734 if (mag == 1){ 735 test = dir == -1 736 // If going back one month, make sure month is not current month 737 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02) 738 ? function(){ return new_date.getUTCMonth() == month; } 739 // If going forward one month, make sure month is as expected 740 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02) 741 : function(){ return new_date.getUTCMonth() != new_month; }; 742 new_month = month + dir; 743 new_date.setUTCMonth(new_month); 744 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11 745 if (new_month < 0 || new_month > 11) 746 new_month = (new_month + 12) % 12; 747 } else { 748 // For magnitudes >1, move one month at a time... 749 for (var i=0; i<mag; i++) 750 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)... 751 new_date = this.moveMonth(new_date, dir); 752 // ...then reset the day, keeping it in the new month 753 new_month = new_date.getUTCMonth(); 754 new_date.setUTCDate(day); 755 test = function(){ return new_month != new_date.getUTCMonth(); }; 756 } 757 // Common date-resetting loop -- if date is beyond end of month, make it 758 // end of month 759 while (test()){ 760 new_date.setUTCDate(--day); 761 new_date.setUTCMonth(new_month); 762 } 763 return new_date; 764 }, 765 766 moveYear: function(date, dir){ 767 return this.moveMonth(date, dir*12); 768 }, 769 770 dateWithinRange: function(date){ 771 return date >= this.o.startDate && date <= this.o.endDate; 772 }, 773 774 keydown: function(e){ 775 if (this.picker.is(':not(:visible)')){ 776 if (e.keyCode == 27) // allow escape to hide and re-show picker 777 this.show(); 778 return; 779 } 780 var dateChanged = false, 781 dir, day, month, 782 newDate, newViewDate; 783 switch(e.keyCode){ 784 case 27: // escape 785 this.hide(); 786 e.preventDefault(); 787 break; 788 case 37: // left 789 case 39: // right 790 if (!this.o.keyboardNavigation) break; 791 dir = e.keyCode == 37 ? -1 : 1; 792 if (e.ctrlKey){ 793 newDate = this.moveYear(this.date, dir); 794 newViewDate = this.moveYear(this.viewDate, dir); 795 } else if (e.shiftKey){ 796 newDate = this.moveMonth(this.date, dir); 797 newViewDate = this.moveMonth(this.viewDate, dir); 798 } else { 799 newDate = new Date(this.date); 800 newDate.setUTCDate(this.date.getUTCDate() + dir); 801 newViewDate = new Date(this.viewDate); 802 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir); 803 } 804 if (this.dateWithinRange(newDate)){ 805 this.date = newDate; 806 this.viewDate = newViewDate; 807 this.setValue(); 808 this.update(); 809 e.preventDefault(); 810 dateChanged = true; 811 } 812 break; 813 case 38: // up 814 case 40: // down 815 if (!this.o.keyboardNavigation) break; 816 dir = e.keyCode == 38 ? -1 : 1; 817 if (e.ctrlKey){ 818 newDate = this.moveYear(this.date, dir); 819 newViewDate = this.moveYear(this.viewDate, dir); 820 } else if (e.shiftKey){ 821 newDate = this.moveMonth(this.date, dir); 822 newViewDate = this.moveMonth(this.viewDate, dir); 823 } else { 824 newDate = new Date(this.date); 825 newDate.setUTCDate(this.date.getUTCDate() + dir * 7); 826 newViewDate = new Date(this.viewDate); 827 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7); 828 } 829 if (this.dateWithinRange(newDate)){ 830 this.date = newDate; 831 this.viewDate = newViewDate; 832 this.setValue(); 833 this.update(); 834 e.preventDefault(); 835 dateChanged = true; 836 } 837 break; 838 case 13: // enter 839 this.hide(); 840 e.preventDefault(); 841 break; 842 case 9: // tab 843 this.hide(); 844 break; 845 } 846 if (dateChanged){ 847 this._trigger('changeDate'); 848 var element; 849 if (this.isInput) { 850 element = this.element; 851 } else if (this.component){ 852 element = this.element.find('input'); 853 } 854 if (element) { 855 element.change(); 856 } 857 } 858 }, 859 860 showMode: function(dir) { 861 if (dir) { 862 this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir)); 863 } 864 /* 865 vitalets: fixing bug of very special conditions: 866 jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover. 867 Method show() does not set display css correctly and datepicker is not shown. 868 Changed to .css('display', 'block') solve the problem. 869 See https://github.com/vitalets/x-editable/issues/37 870 871 In jquery 1.7.2+ everything works fine. 872 */ 873 //this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show(); 874 this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block'); 875 this.updateNavArrows(); 876 } 877 }; 878 879 var DateRangePicker = function(element, options){ 880 this.element = $(element); 881 this.inputs = $.map(options.inputs, function(i){ return i.jquery ? i[0] : i; }); 882 delete options.inputs; 883 884 $(this.inputs) 885 .datepicker(options) 886 .bind('changeDate', $.proxy(this.dateUpdated, this)); 887 888 this.pickers = $.map(this.inputs, function(i){ return $(i).data('datepicker'); }); 889 this.updateDates(); 890 }; 891 DateRangePicker.prototype = { 892 updateDates: function(){ 893 this.dates = $.map(this.pickers, function(i){ return i.date; }); 894 this.updateRanges(); 895 }, 896 updateRanges: function(){ 897 var range = $.map(this.dates, function(d){ return d.valueOf(); }); 898 $.each(this.pickers, function(i, p){ 899 p.setRange(range); 900 }); 901 }, 902 dateUpdated: function(e){ 903 var dp = $(e.target).data('datepicker'), 904 new_date = dp.getUTCDate(), 905 i = $.inArray(e.target, this.inputs), 906 l = this.inputs.length; 907 if (i == -1) return; 908 909 if (new_date < this.dates[i]){ 910 // Date being moved earlier/left 911 while (i>=0 && new_date < this.dates[i]){ 912 this.pickers[i--].setUTCDate(new_date); 913 } 914 } 915 else if (new_date > this.dates[i]){ 916 // Date being moved later/right 917 while (i<l && new_date > this.dates[i]){ 918 this.pickers[i++].setUTCDate(new_date); 919 } 920 } 921 this.updateDates(); 922 }, 923 remove: function(){ 924 $.map(this.pickers, function(p){ p.remove(); }); 925 delete this.element.data().datepicker; 926 } 927 }; 928 929 function opts_from_el(el, prefix){ 930 // Derive options from element data-attrs 931 var data = $(el).data(), 932 out = {}, inkey, 933 replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'), 934 prefix = new RegExp('^' + prefix.toLowerCase()); 935 for (var key in data) 936 if (prefix.test(key)){ 937 inkey = key.replace(replace, function(_,a){ return a.toLowerCase(); }); 938 out[inkey] = data[key]; 939 } 940 return out; 941 } 942 943 function opts_from_locale(lang){ 944 // Derive options from locale plugins 945 var out = {}; 946 // Check if "de-DE" style date is available, if not language should 947 // fallback to 2 letter code eg "de" 948 if (!dates[lang]) { 949 lang = lang.split('-')[0] 950 if (!dates[lang]) 951 return; 952 } 953 var d = dates[lang]; 954 $.each(locale_opts, function(i,k){ 955 if (k in d) 956 out[k] = d[k]; 957 }); 958 return out; 959 } 960 961 var old = $.fn.datepicker; 962
vendor: 8,839 bytes, lines 962-1245
962$.fn.datepicker = function ( option ) { 963 var args = Array.apply(null, arguments); 964 args.shift(); 965 var internal_return, 966 this_return; 967 this.each(function () { 968 var $this = $(this), 969 data = $this.data('datepicker'), 970 options = typeof option == 'object' && option; 971 if (!data) { 972 var elopts = opts_from_el(this, 'date'), 973 // Preliminary otions 974 xopts = $.extend({}, defaults, elopts, options), 975 locopts = opts_from_locale(xopts.language), 976 // Options priority: js args, data-attrs, locales, defaults 977 opts = $.extend({}, defaults, locopts, elopts, options); 978 if ($this.is('.input-daterange') || opts.inputs){ 979 var ropts = { 980 inputs: opts.inputs || $this.find('input').toArray() 981 }; 982 $this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts)))); 983 } 984 else{ 985 $this.data('datepicker', (data = new Datepicker(this, opts))); 986 } 987 } 988 if (typeof option == 'string' && typeof data[option] == 'function') { 989 internal_return = data[option].apply(data, args); 990 if (internal_return !== undefined) 991 return false; 992 } 993 }); 994 if (internal_return !== undefined) 995 return internal_return; 996 else 997 return this; 998 }; 999 1000 var defaults = $.fn.datepicker.defaults = { 1001 autoclose: false, 1002 beforeShowDay: $.noop, 1003 calendarWeeks: false, 1004 clearBtn: false, 1005 daysOfWeekDisabled: [], 1006 endDate: Infinity, 1007 forceParse: true, 1008 format: 'mm/dd/yyyy', 1009 keyboardNavigation: true, 1010 language: 'en', 1011 minViewMode: 0, 1012 rtl: false, 1013 startDate: -Infinity, 1014 startView: 0, 1015 todayBtn: false, 1016 todayHighlight: false, 1017 weekStart: 0 1018 }; 1019 var locale_opts = $.fn.datepicker.locale_opts = [ 1020 'format', 1021 'rtl', 1022 'weekStart' 1023 ]; 1024 $.fn.datepicker.Constructor = Datepicker; 1025 var dates = $.fn.datepicker.dates = { 1026 en: { 1027 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], 1028 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], 1029 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], 1030 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], 1031 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], 1032 today: "Today", 1033 clear: "Clear" 1034 } 1035 }; 1036 1037 var DPGlobal = { 1038 modes: [ 1039 { 1040 clsName: 'days', 1041 navFnc: 'Month', 1042 navStep: 1 1043 }, 1044 { 1045 clsName: 'months', 1046 navFnc: 'FullYear', 1047 navStep: 1 1048 }, 1049 { 1050 clsName: 'years', 1051 navFnc: 'FullYear', 1052 navStep: 10 1053 }], 1054 isLeapYear: function (year) { 1055 return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); 1056 }, 1057 getDaysInMonth: function (year, month) { 1058 return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; 1059 }, 1060 validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g, 1061 nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g, 1062 parseFormat: function(format){ 1063 // IE treats \0 as a string end in inputs (truncating the value), 1064 // so it's a bad format delimiter, anyway 1065 var separators = format.replace(this.validParts, '\0').split('\0'), 1066 parts = format.match(this.validParts); 1067 if (!separators || !separators.length || !parts || parts.length === 0){ 1068 throw new Error("Invalid date format."); 1069 } 1070 return {separators: separators, parts: parts}; 1071 }, 1072 parseDate: function(date, format, language) { 1073 if (date instanceof Date) return date; 1074 if (typeof format === 'string') 1075 format = DPGlobal.parseFormat(format); 1076 if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) { 1077 var part_re = /([\-+]\d+)([dmwy])/, 1078 parts = date.match(/([\-+]\d+)([dmwy])/g), 1079 part, dir; 1080 date = new Date(); 1081 for (var i=0; i<parts.length; i++) { 1082 part = part_re.exec(parts[i]); 1083 dir = parseInt(part[1]); 1084 switch(part[2]){ 1085 case 'd': 1086 date.setUTCDate(date.getUTCDate() + dir); 1087 break; 1088 case 'm': 1089 date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir); 1090 break; 1091 case 'w': 1092 date.setUTCDate(date.getUTCDate() + dir * 7); 1093 break; 1094 case 'y': 1095 date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir); 1096 break; 1097 } 1098 } 1099 return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0); 1100 } 1101 var parts = date && date.match(this.nonpunctuation) || [], 1102 date = new Date(), 1103 parsed = {}, 1104 setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'], 1105 setters_map = { 1106 yyyy: function(d,v){ return d.setUTCFullYear(v); }, 1107 yy: function(d,v){ return d.setUTCFullYear(2000+v); }, 1108 m: function(d,v){ 1109 if (isNaN(d)) 1110 return d; 1111 v -= 1; 1112 while (v<0) v += 12; 1113 v %= 12; 1114 d.setUTCMonth(v); 1115 while (d.getUTCMonth() != v) 1116 d.setUTCDate(d.getUTCDate()-1); 1117 return d; 1118 }, 1119 d: function(d,v){ return d.setUTCDate(v); } 1120 }, 1121 val, filtered, part; 1122 setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m']; 1123 setters_map['dd'] = setters_map['d']; 1124 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); 1125 var fparts = format.parts.slice(); 1126 // Remove noop parts 1127 if (parts.length != fparts.length) { 1128 fparts = $(fparts).filter(function(i,p){ 1129 return $.inArray(p, setters_order) !== -1; 1130 }).toArray(); 1131 } 1132 // Process remainder 1133 if (parts.length == fparts.length) { 1134 for (var i=0, cnt = fparts.length; i < cnt; i++) { 1135 val = parseInt(parts[i], 10); 1136 part = fparts[i]; 1137 if (isNaN(val)) { 1138 switch(part) { 1139 case 'MM': 1140 filtered = $(dates[language].months).filter(function(){ 1141 var m = this.slice(0, parts[i].length), 1142 p = parts[i].slice(0, m.length); 1143 return m == p; 1144 }); 1145 val = $.inArray(filtered[0], dates[language].months) + 1; 1146 break; 1147 case 'M': 1148 filtered = $(dates[language].monthsShort).filter(function(){ 1149 var m = this.slice(0, parts[i].length), 1150 p = parts[i].slice(0, m.length); 1151 return m == p; 1152 }); 1153 val = $.inArray(filtered[0], dates[language].monthsShort) + 1; 1154 break; 1155 } 1156 } 1157 parsed[part] = val; 1158 } 1159 for (var i=0, _date, s; i<setters_order.length; i++){ 1160 s = setters_order[i]; 1161 if (s in parsed && !isNaN(parsed[s])){ 1162 _date = new Date(date); 1163 setters_map[s](_date, parsed[s]); 1164 if (!isNaN(_date)) 1165 date = _date; 1166 } 1167 } 1168 } 1169 return date; 1170 }, 1171 formatDate: function(date, format, language){ 1172 if (typeof format === 'string') 1173 format = DPGlobal.parseFormat(format); 1174 var val = { 1175 d: date.getUTCDate(), 1176 D: dates[language].daysShort[date.getUTCDay()], 1177 DD: dates[language].days[date.getUTCDay()], 1178 m: date.getUTCMonth() + 1, 1179 M: dates[language].monthsShort[date.getUTCMonth()], 1180 MM: dates[language].months[date.getUTCMonth()], 1181 yy: date.getUTCFullYear().toString().substring(2), 1182 yyyy: date.getUTCFullYear() 1183 }; 1184 val.dd = (val.d < 10 ? '0' : '') + val.d; 1185 val.mm = (val.m < 10 ? '0' : '') + val.m; 1186 var date = [], 1187 seps = $.extend([], format.separators); 1188 for (var i=0, cnt = format.parts.length; i <= cnt; i++) { 1189 if (seps.length) 1190 date.push(seps.shift()); 1191 date.push(val[format.parts[i]]); 1192 } 1193 return date.join(''); 1194 }, 1195 headTemplate: '<thead>'+ 1196 '<tr>'+ 1197 '<th class="prev"><i class="icon-arrow-left"/></th>'+ 1198 '<th colspan="5" class="datepicker-switch"></th>'+ 1199 '<th class="next"><i class="icon-arrow-right"/></th>'+ 1200 '</tr>'+ 1201 '</thead>', 1202 contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>', 1203 footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr><tr><th colspan="7" class="clear"></th></tr></tfoot>' 1204 }; 1205 DPGlobal.template = '<div class="datepicker">'+ 1206 '<div class="datepicker-days">'+ 1207 '<table class=" table-condensed">'+ 1208 DPGlobal.headTemplate+ 1209 '<tbody></tbody>'+ 1210 DPGlobal.footTemplate+ 1211 '</table>'+ 1212 '</div>'+ 1213 '<div class="datepicker-months">'+ 1214 '<table class="table-condensed">'+ 1215 DPGlobal.headTemplate+ 1216 DPGlobal.contTemplate+ 1217 DPGlobal.footTemplate+ 1218 '</table>'+ 1219 '</div>'+ 1220 '<div class="datepicker-years">'+ 1221 '<table class="table-condensed">'+ 1222 DPGlobal.headTemplate+ 1223 DPGlobal.contTemplate+ 1224 DPGlobal.footTemplate+ 1225 '</table>'+ 1226 '</div>'+ 1227 '</div>'; 1228 1229 $.fn.datepicker.DPGlobal = DPGlobal; 1230 1231 1232 /* DATEPICKER NO CONFLICT 1233 * =================== */ 1234 1235 $.fn.datepicker.noConflict = function(){ 1236 $.fn.datepicker = old; 1237 return this; 1238 }; 1239 1240 1241 /* DATEPICKER DATA-API 1242 * ================== */ 1243 1244 $(document).on( 1245 'focus.datepicker.data-api click.datepicker.data-api',
1246 '[data-provide="datepicker"]', 1247 function(e){ 1248 var $this = $(this); 1249 if ($this.data('datepicker')) return; 1250 e.preventDefault(); 1251 // component click requires us to explicitly show it 1252 $this.datepicker('show'); 1253 } 1254 ); 1255 $(function(){ 1256 $('[data-provide="datepicker-inline"]').datepicker(); 1257 }); 1258 1259}( 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.