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!function( $ ) { 22 23 function UTCDate(){ 24 return new Date(Date.UTC.apply(Date, arguments)); 25 } 26 function UTCToday(){ 27 var today = new Date(); 28 return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()); 29 } 30 31 // Picker object 32 33 var Datepicker = function(element, options) { 34 var that = this; 35 36 this.element = $(element); 37 this.language = options.language||this.element.data('date-language')||"en"; 38 this.language = this.language in dates ? this.language : this.language.split('-')[0]; //Check if "de-DE" style date is available, if not language should fallback to 2 letter code eg "de" 39 this.language = this.language in dates ? this.language : "en"; 40 this.isRTL = dates[this.language].rtl||false; 41 this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||dates[this.language].format||'mm/dd/yyyy'); 42 this.isInline = false; 43 this.isInput = this.element.is('input'); 44 this.component = this.element.is('.date') ? this.element.find('.add-on') : false; 45 this.hasInput = this.component && this.element.find('input').length; 46 if(this.component && this.component.length === 0) 47 this.component = false; 48 49 this._attachEvents(); 50 51 this.forceParse = true; 52 if ('forceParse' in options) { 53 this.forceParse = options.forceParse; 54 } else if ('dateForceParse' in this.element.data()) { 55 this.forceParse = this.element.data('date-force-parse'); 56 } 57 58 59 this.picker = $(DPGlobal.template) 60 .appendTo(this.isInline ? this.element : 'body') 61 .on({ 62 click: $.proxy(this.click, this), 63 mousedown: $.proxy(this.mousedown, this) 64 }); 65 66 if(this.isInline) { 67 this.picker.addClass('datepicker-inline'); 68 } else { 69 this.picker.addClass('datepicker-dropdown dropdown-menu'); 70 } 71 if (this.isRTL){ 72 this.picker.addClass('datepicker-rtl'); 73 this.picker.find('.prev i, .next i') 74 .toggleClass('icon-arrow-left icon-arrow-right'); 75 } 76 $(document).on('mousedown', function (e) { 77 // Clicked outside the datepicker, hide it 78 if ($(e.target).closest('.datepicker.datepicker-inline, .datepicker.datepicker-dropdown').length === 0) { 79 that.hide(); 80 } 81 }); 82 83 this.autoclose = false; 84 if ('autoclose' in options) { 85 this.autoclose = options.autoclose; 86 } else if ('dateAutoclose' in this.element.data()) { 87 this.autoclose = this.element.data('date-autoclose'); 88 } 89 90 this.keyboardNavigation = true; 91 if ('keyboardNavigation' in options) { 92 this.keyboardNavigation = options.keyboardNavigation; 93 } else if ('dateKeyboardNavigation' in this.element.data()) { 94 this.keyboardNavigation = this.element.data('date-keyboard-navigation'); 95 } 96 97 this.viewMode = this.startViewMode = 0; 98 switch(options.startView || this.element.data('date-start-view')){ 99 case 2: 100 case 'decade': 101 this.viewMode = this.startViewMode = 2; 102 break; 103 case 1: 104 case 'year': 105 this.viewMode = this.startViewMode = 1; 106 break; 107 } 108 109 this.minViewMode = options.minViewMode||this.element.data('date-min-view-mode')||0; 110 if (typeof this.minViewMode === 'string') { 111 switch (this.minViewMode) { 112 case 'months': 113 this.minViewMode = 1; 114 break; 115 case 'years': 116 this.minViewMode = 2; 117 break; 118 default: 119 this.minViewMode = 0; 120 break; 121 } 122 } 123 124 this.viewMode = this.startViewMode = Math.max(this.startViewMode, this.minViewMode); 125 126 this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false); 127 this.todayHighlight = (options.todayHighlight||this.element.data('date-today-highlight')||false); 128 129 this.calendarWeeks = false;
130 if ('calendarWeeks' in options) { 131 this.calendarWeeks = options.calendarWeeks; 132 } else if ('dateCalendarWeeks' in this.element.data()) { 133 this.calendarWeeks = this.element.data('date-calendar-weeks'); 134 } 135 if (this.calendarWeeks) 136 this.picker.find('tfoot th.today') 137 .attr('colspan', function(i, val){ 138 return parseInt(val) + 1; 139 }); 140 141 this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7); 142 this.weekEnd = ((this.weekStart + 6) % 7); 143 this.startDate = -Infinity; 144 this.endDate = Infinity; 145 this.daysOfWeekDisabled = []; 146 this.setStartDate(options.startDate||this.element.data('date-startdate')); 147 this.setEndDate(options.endDate||this.element.data('date-enddate')); 148 this.setDaysOfWeekDisabled(options.daysOfWeekDisabled||this.element.data('date-days-of-week-disabled')); 149 this.fillDow(); 150 this.fillMonths(); 151 this.update(); 152 this.showMode(); 153 154 if(this.isInline) { 155 this.show(); 156 } 157 }; 158 159 Datepicker.prototype = { 160 constructor: Datepicker, 161 162 _events: [], 163 _attachEvents: function(){ 164 this._detachEvents(); 165 if (this.isInput) { // single input 166 this._events = [ 167 [this.element, { 168 focus: $.proxy(this.show, this), 169 keyup: $.proxy(this.update, this), 170 keydown: $.proxy(this.keydown, this) 171 }] 172 ]; 173 } 174 else if (this.component && this.hasInput){ // component: input + button 175 this._events = [ 176 // For components that are not readonly, allow keyboard nav 177 [this.element.find('input'), { 178 focus: $.proxy(this.show, this), 179 keyup: $.proxy(this.update, this), 180 keydown: $.proxy(this.keydown, this) 181 }], 182 [this.component, { 183 click: $.proxy(this.show, this) 184 }] 185 ]; 186 } 187 else if (this.element.is('div')) { // inline datepicker 188 this.isInline = true; 189 } 190 else { 191 this._events = [ 192 [this.element, { 193 click: $.proxy(this.show, this) 194 }] 195 ]; 196 } 197 for (var i=0, el, ev; i<this._events.length; i++){ 198 el = this._events[i][0]; 199 ev = this._events[i][1]; 200 el.on(ev); 201 } 202 }, 203 _detachEvents: function(){ 204 for (var i=0, el, ev; i<this._events.length; i++){ 205 el = this._events[i][0]; 206 ev = this._events[i][1]; 207 el.off(ev); 208 } 209 this._events = []; 210 }, 211 212 show: function(e) { 213 this.picker.show(); 214 this.height = this.component ? this.component.outerHeight() : this.element.outerHeight(); 215 this.update(); 216 this.place(); 217 $(window).on('resize', $.proxy(this.place, this)); 218 if (e ) { 219 e.stopPropagation(); 220 e.preventDefault(); 221 } 222 this.element.trigger({ 223 type: 'show', 224 date: this.date 225 }); 226 }, 227 228 hide: function(e){ 229 if(this.isInline) return; 230 if (!this.picker.is(':visible')) return; 231 this.picker.hide(); 232 $(window).off('resize', this.place); 233 this.viewMode = this.startViewMode; 234 this.showMode(); 235 if (!this.isInput) { 236 $(document).off('mousedown', this.hide); 237 } 238 239 if ( 240 this.forceParse && 241 ( 242 this.isInput && this.element.val() || 243 this.hasInput && this.element.find('input').val() 244 ) 245 ) 246 this.setValue(); 247 this.element.trigger({ 248 type: 'hide', 249 date: this.date 250 }); 251 }, 252 253 remove: function() { 254 this._detachEvents(); 255 this.picker.remove(); 256 delete this.element.data().datepicker; 257 if (!this.isInput) { 258 delete this.element.data().date; 259 } 260 }, 261 262 getDate: function() { 263 var d = this.getUTCDate(); 264 return new Date(d.getTime() + (d.getTimezoneOffset()*60000)); 265 }, 266 267 getUTCDate: function() { 268 return this.date; 269 }, 270 271 setDate: function(d) { 272 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000))); 273 }, 274 275 setUTCDate: function(d) { 276 this.date = d; 277 this.setValue(); 278 }, 279 280 setValue: function() { 281 var formatted = this.getFormattedDate(); 282 if (!this.isInput) { 283 if (this.component){ 284 this.element.find('input').val(formatted); 285 } 286 this.element.data('date', formatted); 287 } else { 288 this.element.val(formatted); 289 } 290 }, 291 292 getFormattedDate: function(format) { 293 if (format === undefined) 294 format = this.format; 295 return DPGlobal.formatDate(this.date, format, this.language); 296 }, 297 298 setStartDate: function(startDate){ 299 this.startDate = startDate||-Infinity; 300 if (this.startDate !== -Infinity) { 301 this.startDate = DPGlobal.parseDate(this.startDate, this.format, this.language); 302 } 303 this.update(); 304 this.updateNavArrows(); 305 }, 306
307 setEndDate: function(endDate){ 308 this.endDate = endDate||Infinity; 309 if (this.endDate !== Infinity) { 310 this.endDate = DPGlobal.parseDate(this.endDate, this.format, this.language); 311 } 312 this.update(); 313 this.updateNavArrows(); 314 }, 315 316 setDaysOfWeekDisabled: function(daysOfWeekDisabled){ 317 this.daysOfWeekDisabled = daysOfWeekDisabled||[]; 318 if (!$.isArray(this.daysOfWeekDisabled)) { 319 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/); 320 } 321 this.daysOfWeekDisabled = $.map(this.daysOfWeekDisabled, function (d) { 322 return parseInt(d, 10); 323 }); 324 this.update(); 325 this.updateNavArrows(); 326 }, 327 328 place: function(){ 329 if(this.isInline) return; 330 var zIndex = parseInt(this.element.parents().filter(function() { 331 return $(this).css('z-index') != 'auto'; 332 }).first().css('z-index'))+10; 333 var offset = this.component ? this.component.parent().offset() : this.element.offset(); 334 var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(true); 335 this.picker.css({ 336 top: offset.top + height, 337 left: offset.left, 338 zIndex: zIndex 339 }); 340 }, 341 342 update: function(){ 343 var date, fromArgs = false; 344 if(arguments && arguments.length && (typeof arguments[0] === 'string' || arguments[0] instanceof Date)) { 345 date = arguments[0]; 346 fromArgs = true; 347 } else { 348 date = this.isInput ? this.element.val() : this.element.data('date') || this.element.find('input').val(); 349 } 350 351 this.date = DPGlobal.parseDate(date, this.format, this.language); 352 353 if(fromArgs) this.setValue(); 354 355 if (this.date < this.startDate) { 356 this.viewDate = new Date(this.startDate); 357 } else if (this.date > this.endDate) { 358 this.viewDate = new Date(this.endDate); 359 } else { 360 this.viewDate = new Date(this.date); 361 } 362 this.fill(); 363 }, 364 365 fillDow: function(){ 366 var dowCnt = this.weekStart, 367 html = '<tr>'; 368 if(this.calendarWeeks){ 369 var cell = '<th class="cw"> </th>'; 370 html += cell; 371 this.picker.find('.datepicker-days thead tr:first-child').prepend(cell); 372 } 373 while (dowCnt < this.weekStart + 7) { 374 html += '<th class="dow">'+dates[this.language].daysMin[(dowCnt++)%7]+'</th>'; 375 } 376 html += '</tr>'; 377 this.picker.find('.datepicker-days thead').append(html); 378 }, 379 380 fillMonths: function(){ 381 var html = '', 382 i = 0; 383 while (i < 12) { 384 html += '<span class="month">'+dates[this.language].monthsShort[i++]+'</span>'; 385 } 386 this.picker.find('.datepicker-months td').html(html); 387 }, 388 389 fill: function() { 390 var d = new Date(this.viewDate), 391 year = d.getUTCFullYear(), 392 month = d.getUTCMonth(), 393 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity, 394 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity, 395 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity, 396 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity, 397 currentDate = this.date && this.date.valueOf(), 398 today = new Date(); 399 this.picker.find('.datepicker-days thead th.switch') 400 .text(dates[this.language].months[month]+' '+year); 401 this.picker.find('tfoot th.today') 402 .text(dates[this.language].today) 403 .toggle(this.todayBtn !== false); 404 this.updateNavArrows(); 405 this.fillMonths(); 406 var prevMonth = UTCDate(year, month-1, 28,0,0,0,0), 407 day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth()); 408 prevMonth.setUTCDate(day); 409 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7); 410 var nextMonth = new Date(prevMonth); 411 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42); 412 nextMonth = nextMonth.valueOf(); 413 var html = []; 414 var clsName; 415 while(prevMonth.valueOf() < nextMonth) { 416 if (prevMonth.getUTCDay() == this.weekStart) { 417 html.push('<tr>'); 418 if(this.calendarWeeks){ 419 // ISO 8601: First week contains first thursday. 420 // ISO also states week starts on Monday, but we can be more abstract here. 421 var 422 // Start of current week: based on weekstart/current date 423 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5), 424 // Thursday of this week 425 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5), 426 // First Thursday of year, year from thursday 427 yth = new Date(+(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5), 428 // Calendar week: ms between thursdays, div ms per day, div 7 days 429 calWeek = (th - yth) / 864e5 / 7 + 1; 430 html.push('<td class="cw">'+ calWeek +'</td>'); 431 432 } 433 } 434 clsName = ''; 435 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) { 436 clsName += ' old'; 437 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) { 438 clsName += ' new'; 439 } 440 // Compare internal UTC date with local today, not UTC today 441 if (this.todayHighlight && 442 prevMonth.getUTCFullYear() == today.getFullYear() && 443 prevMonth.getUTCMonth() == today.getMonth() && 444 prevMonth.getUTCDate() == today.getDate()) { 445 clsName += ' today'; 446 } 447 if (currentDate && prevMonth.valueOf() == currentDate) { 448 clsName += ' active'; 449 } 450 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate || 451 $.inArray(prevMonth.getUTCDay(), this.daysOfWeekDisabled) !== -1) { 452 clsName += ' disabled'; 453 } 454 html.push('<td class="day'+clsName+'">'+prevMonth.getUTCDate() + '</td>'); 455 if (prevMonth.getUTCDay() == this.weekEnd) { 456 html.push('</tr>'); 457 } 458 prevMonth.setUTCDate(prevMonth.getUTCDate()+1); 459 } 460 this.picker.find('.datepicker-days tbody').empty().append(html.join('')); 461 var currentYear = this.date && this.date.getUTCFullYear(); 462 463 var months = this.picker.find('.datepicker-months') 464 .find('th:eq(1)') 465 .text(year) 466 .end() 467 .find('span').removeClass('active'); 468 if (currentYear && currentYear == year) { 469 months.eq(this.date.getUTCMonth()).addClass('active'); 470 } 471 if (year < startYear || year > endYear) { 472 months.addClass('disabled'); 473 } 474 if (year == startYear) { 475 months.slice(0, startMonth).addClass('disabled'); 476 } 477 if (year == endYear) { 478 months.slice(endMonth+1).addClass('disabled'); 479 } 480 481 html = ''; 482 year = parseInt(year/10, 10) * 10; 483 var yearCont = this.picker.find('.datepicker-years') 484 .find('th:eq(1)') 485 .text(year + '-' + (year + 9)) 486 .end() 487 .find('td'); 488 year -= 1;
489 for (var i = -1; i < 11; i++) { 490 html += '<span class="year'+(i == -1 || i == 10 ? ' old' : '')+(currentYear == year ? ' active' : '')+(year < startYear || year > endYear ? ' disabled' : '')+'">'+year+'</span>'; 491 year += 1; 492 } 493 yearCont.html(html); 494 }, 495 496 updateNavArrows: function() { 497 var d = new Date(this.viewDate), 498 year = d.getUTCFullYear(), 499 month = d.getUTCMonth(); 500 switch (this.viewMode) { 501 case 0: 502 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) { 503 this.picker.find('.prev').css({visibility: 'hidden'}); 504 } else { 505 this.picker.find('.prev').css({visibility: 'visible'}); 506 } 507 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) { 508 this.picker.find('.next').css({visibility: 'hidden'}); 509 } else { 510 this.picker.find('.next').css({visibility: 'visible'}); 511 } 512 break; 513 case 1: 514 case 2: 515 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) { 516 this.picker.find('.prev').css({visibility: 'hidden'}); 517 } else { 518 this.picker.find('.prev').css({visibility: 'visible'}); 519 } 520 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) { 521 this.picker.find('.next').css({visibility: 'hidden'}); 522 } else { 523 this.picker.find('.next').css({visibility: 'visible'}); 524 } 525 break; 526 } 527 }, 528 529 click: function(e) { 530 e.stopPropagation(); 531 e.preventDefault(); 532 var target = $(e.target).closest('span, td, th'); 533 if (target.length == 1) { 534 switch(target[0].nodeName.toLowerCase()) { 535 case 'th': 536 switch(target[0].className) { 537 case 'switch': 538 this.showMode(1); 539 break; 540 case 'prev': 541 case 'next': 542 var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1); 543 switch(this.viewMode){ 544 case 0: 545 this.viewDate = this.moveMonth(this.viewDate, dir); 546 break; 547 case 1: 548 case 2: 549 this.viewDate = this.moveYear(this.viewDate, dir); 550 break; 551 } 552 this.fill(); 553 break; 554 case 'today': 555 var date = new Date(); 556 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); 557 558 this.showMode(-2); 559 var which = this.todayBtn == 'linked' ? null : 'view'; 560 this._setDate(date, which); 561 break; 562 } 563 break; 564 case 'span': 565 if (!target.is('.disabled')) { 566 this.viewDate.setUTCDate(1); 567 if (target.is('.month')) { 568 var day = 1; 569 var month = target.parent().find('span').index(target); 570 var year = this.viewDate.getUTCFullYear(); 571 this.viewDate.setUTCMonth(month); 572 this.element.trigger({ 573 type: 'changeMonth', 574 date: this.viewDate 575 }); 576 if ( this.minViewMode == 1 ) { 577 this._setDate(UTCDate(year, month, day,0,0,0,0)); 578 } 579 } else { 580 var year = parseInt(target.text(), 10)||0; 581 var day = 1; 582 var month = 0; 583 this.viewDate.setUTCFullYear(year); 584 this.element.trigger({ 585 type: 'changeYear', 586 date: this.viewDate 587 }); 588 if ( this.minViewMode == 2 ) { 589 this._setDate(UTCDate(year, month, day,0,0,0,0)); 590 } 591 } 592 this.showMode(-1); 593 this.fill(); 594 } 595 break; 596 case 'td': 597 if (target.is('.day') && !target.is('.disabled')){ 598 var day = parseInt(target.text(), 10)||1; 599 var year = this.viewDate.getUTCFullYear(), 600 month = this.viewDate.getUTCMonth(); 601 if (target.is('.old')) { 602 if (month === 0) { 603 month = 11; 604 year -= 1; 605 } else { 606 month -= 1; 607 } 608 } else if (target.is('.new')) { 609 if (month == 11) { 610 month = 0; 611 year += 1; 612 } else { 613 month += 1; 614 } 615 } 616 this._setDate(UTCDate(year, month, day,0,0,0,0)); 617 } 618 break; 619 } 620 } 621 }, 622 623 _setDate: function(date, which){ 624 if (!which || which == 'date') 625 this.date = date; 626 if (!which || which == 'view') 627 this.viewDate = date; 628 this.fill(); 629 this.setValue(); 630 this.element.trigger({ 631 type: 'changeDate', 632 date: this.date 633 }); 634 var element; 635 if (this.isInput) { 636 element = this.element; 637 } else if (this.component){ 638 element = this.element.find('input'); 639 } 640 if (element) { 641 element.change(); 642 if (this.autoclose && (!which || which == 'date')) { 643 this.hide(); 644 } 645 } 646 }, 647 648 moveMonth: function(date, dir){ 649 if (!dir) return date; 650 var new_date = new Date(date.valueOf()), 651 day = new_date.getUTCDate(),
652 month = new_date.getUTCMonth(), 653 mag = Math.abs(dir), 654 new_month, test; 655 dir = dir > 0 ? 1 : -1; 656 if (mag == 1){ 657 test = dir == -1 658 // If going back one month, make sure month is not current month 659 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02) 660 ? function(){ return new_date.getUTCMonth() == month; } 661 // If going forward one month, make sure month is as expected 662 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02) 663 : function(){ return new_date.getUTCMonth() != new_month; }; 664 new_month = month + dir; 665 new_date.setUTCMonth(new_month); 666 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11 667 if (new_month < 0 || new_month > 11) 668 new_month = (new_month + 12) % 12; 669 } else { 670 // For magnitudes >1, move one month at a time... 671 for (var i=0; i<mag; i++) 672 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)... 673 new_date = this.moveMonth(new_date, dir); 674 // ...then reset the day, keeping it in the new month 675 new_month = new_date.getUTCMonth(); 676 new_date.setUTCDate(day); 677 test = function(){ return new_month != new_date.getUTCMonth(); }; 678 } 679 // Common date-resetting loop -- if date is beyond end of month, make it 680 // end of month 681 while (test()){ 682 new_date.setUTCDate(--day); 683 new_date.setUTCMonth(new_month); 684 } 685 return new_date; 686 }, 687 688 moveYear: function(date, dir){ 689 return this.moveMonth(date, dir*12); 690 }, 691 692 dateWithinRange: function(date){ 693 return date >= this.startDate && date <= this.endDate; 694 }, 695 696 keydown: function(e){ 697 if (this.picker.is(':not(:visible)')){ 698 if (e.keyCode == 27) // allow escape to hide and re-show picker 699 this.show(); 700 return; 701 } 702 var dateChanged = false, 703 dir, day, month, 704 newDate, newViewDate; 705 switch(e.keyCode){ 706 case 27: // escape 707 this.hide(); 708 e.preventDefault(); 709 break; 710 case 37: // left 711 case 39: // right 712 if (!this.keyboardNavigation) break; 713 dir = e.keyCode == 37 ? -1 : 1; 714 if (e.ctrlKey){ 715 newDate = this.moveYear(this.date, dir); 716 newViewDate = this.moveYear(this.viewDate, dir); 717 } else if (e.shiftKey){ 718 newDate = this.moveMonth(this.date, dir); 719 newViewDate = this.moveMonth(this.viewDate, dir); 720 } else { 721 newDate = new Date(this.date); 722 newDate.setUTCDate(this.date.getUTCDate() + dir); 723 newViewDate = new Date(this.viewDate); 724 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir); 725 } 726 if (this.dateWithinRange(newDate)){ 727 this.date = newDate; 728 this.viewDate = newViewDate; 729 this.setValue(); 730 this.update(); 731 e.preventDefault(); 732 dateChanged = true; 733 } 734 break; 735 case 38: // up 736 case 40: // down 737 if (!this.keyboardNavigation) break; 738 dir = e.keyCode == 38 ? -1 : 1; 739 if (e.ctrlKey){ 740 newDate = this.moveYear(this.date, dir); 741 newViewDate = this.moveYear(this.viewDate, dir); 742 } else if (e.shiftKey){ 743 newDate = this.moveMonth(this.date, dir); 744 newViewDate = this.moveMonth(this.viewDate, dir); 745 } else { 746 newDate = new Date(this.date); 747 newDate.setUTCDate(this.date.getUTCDate() + dir * 7); 748 newViewDate = new Date(this.viewDate); 749 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7); 750 } 751 if (this.dateWithinRange(newDate)){ 752 this.date = newDate; 753 this.viewDate = newViewDate; 754 this.setValue(); 755 this.update(); 756 e.preventDefault(); 757 dateChanged = true; 758 } 759 break; 760 case 13: // enter 761 this.hide(); 762 e.preventDefault(); 763 break; 764 case 9: // tab 765 this.hide(); 766 break; 767 } 768 if (dateChanged){ 769 this.element.trigger({ 770 type: 'changeDate', 771 date: this.date 772 }); 773 var element; 774 if (this.isInput) { 775 element = this.element; 776 } else if (this.component){ 777 element = this.element.find('input'); 778 } 779 if (element) { 780 element.change(); 781 } 782 } 783 }, 784 785 showMode: function(dir) { 786 if (dir) { 787 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir)); 788 } 789 /* 790 vitalets: fixing bug of very special conditions: 791 jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover. 792 Method show() does not set display css correctly and datepicker is not shown. 793 Changed to .css('display', 'block') solve the problem. 794 See https://github.com/vitalets/x-editable/issues/37 795 796 In jquery 1.7.2+ everything works fine. 797 */ 798 //this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show(); 799 this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block'); 800 this.updateNavArrows(); 801 } 802 }; 803 804 $.fn.datepicker = function ( option ) { 805 var args = Array.apply(null, arguments); 806 args.shift(); 807 return this.each(function () { 808 var $this = $(this), 809 data = $this.data('datepicker'), 810 options = typeof option == 'object' && option; 811 if (!data) { 812 $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options)))); 813 } 814 if (typeof option == 'string' && typeof data[option] == 'function') { 815 data[option].apply(data, args); 816 } 817 }); 818 }; 819 820 $.fn.datepicker.defaults = { 821 }; 822 $.fn.datepicker.Constructor = Datepicker; 823 var dates = $.fn.datepicker.dates = { 824 en: { 825 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], 826 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], 827 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], 828 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
828November", "December"], 829 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], 830 today: "Today" 831 } 832 }; 833 834 var DPGlobal = { 835 modes: [ 836 { 837 clsName: 'days', 838 navFnc: 'Month', 839 navStep: 1 840 }, 841 { 842 clsName: 'months', 843 navFnc: 'FullYear', 844 navStep: 1 845 }, 846 { 847 clsName: 'years', 848 navFnc: 'FullYear', 849 navStep: 10 850 }], 851 isLeapYear: function (year) { 852 return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); 853 }, 854 getDaysInMonth: function (year, month) { 855 return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; 856 }, 857 validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g, 858 nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g, 859 parseFormat: function(format){ 860 // IE treats \0 as a string end in inputs (truncating the value), 861 // so it's a bad format delimiter, anyway 862 var separators = format.replace(this.validParts, '\0').split('\0'), 863 parts = format.match(this.validParts); 864 if (!separators || !separators.length || !parts || parts.length === 0){ 865 throw new Error("Invalid date format."); 866 } 867 return {separators: separators, parts: parts}; 868 }, 869 parseDate: function(date, format, language) { 870 if (date instanceof Date) return date; 871 if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) { 872 var part_re = /([\-+]\d+)([dmwy])/, 873 parts = date.match(/([\-+]\d+)([dmwy])/g), 874 part, dir; 875 date = new Date(); 876 for (var i=0; i<parts.length; i++) { 877 part = part_re.exec(parts[i]); 878 dir = parseInt(part[1]); 879 switch(part[2]){ 880 case 'd': 881 date.setUTCDate(date.getUTCDate() + dir); 882 break; 883 case 'm': 884 date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir); 885 break; 886 case 'w': 887 date.setUTCDate(date.getUTCDate() + dir * 7); 888 break; 889 case 'y': 890 date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir); 891 break; 892 } 893 } 894 return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0); 895 } 896 var parts = date && date.match(this.nonpunctuation) || [], 897 date = new Date(), 898 parsed = {}, 899 setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'], 900 setters_map = { 901 yyyy: function(d,v){ return d.setUTCFullYear(v); }, 902 yy: function(d,v){ return d.setUTCFullYear(2000+v); }, 903 m: function(d,v){ 904 v -= 1; 905 while (v<0) v += 12; 906 v %= 12; 907 d.setUTCMonth(v); 908 while (d.getUTCMonth() != v) 909 d.setUTCDate(d.getUTCDate()-1); 910 return d; 911 }, 912 d: function(d,v){ return d.setUTCDate(v); } 913 }, 914 val, filtered, part; 915 setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m']; 916 setters_map['dd'] = setters_map['d']; 917 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); 918 var fparts = format.parts.slice(); 919 // Remove noop parts 920 if (parts.length != fparts.length) { 921 fparts = $(fparts).filter(function(i,p){ 922 return $.inArray(p, setters_order) !== -1; 923 }).toArray(); 924 } 925 // Process remainder 926 if (parts.length == fparts.length) { 927 for (var i=0, cnt = fparts.length; i < cnt; i++) { 928 val = parseInt(parts[i], 10); 929 part = fparts[i]; 930 if (isNaN(val)) { 931 switch(part) { 932 case 'MM': 933 filtered = $(dates[language].months).filter(function(){ 934 var m = this.slice(0, parts[i].length), 935 p = parts[i].slice(0, m.length); 936 return m == p; 937 }); 938 val = $.inArray(filtered[0], dates[language].months) + 1; 939 break; 940 case 'M': 941 filtered = $(dates[language].monthsShort).filter(function(){ 942 var m = this.slice(0, parts[i].length), 943 p = parts[i].slice(0, m.length); 944 return m == p; 945 }); 946 val = $.inArray(filtered[0], dates[language].monthsShort) + 1; 947 break; 948 } 949 } 950 parsed[part] = val; 951 } 952 for (var i=0, s; i<setters_order.length; i++){ 953 s = setters_order[i]; 954 if (s in parsed && !isNaN(parsed[s])) 955 setters_map[s](date, parsed[s]); 956 } 957 } 958 return date; 959 }, 960 formatDate: function(date, format, language){ 961 var val = { 962 d: date.getUTCDate(), 963 D: dates[language].daysShort[date.getUTCDay()], 964 DD: dates[language].days[date.getUTCDay()], 965 m: date.getUTCMonth() + 1, 966 M: dates[language].monthsShort[date.getUTCMonth()], 967 MM: dates[language].months[date.getUTCMonth()], 968 yy: date.getUTCFullYear().toString().substring(2), 969 yyyy: date.getUTCFullYear() 970 }; 971 val.dd = (val.d < 10 ? '0' : '') + val.d; 972 val.mm = (val.m < 10 ? '0' : '') + val.m; 973 var date = [], 974 seps = $.extend([], format.separators); 975 for (var i=0, cnt = format.parts.length; i < cnt; i++) { 976 if (seps.length) 977 date.push(seps.shift()); 978 date.push(val[format.parts[i]]); 979 } 980 return date.join(''); 981 }, 982 headTemplate: '<thead>'+ 983 '<tr>'+ 984 '<th class="prev"><i class="icon-arrow-left"/></th>'+ 985 '<th colspan="5" class="switch"></th>'+ 986 '<th class="next"><i class="icon-arrow-right"/></th>'+ 987 '</tr>'+ 988 '</thead>', 989 contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>', 990 footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr></tfoot>' 991 }; 992 DPGlobal.template = '<div class="datepicker">'+ 993 '<div class="datepicker-days">'+ 994 '<table class=" table-condensed">'+ 995 DPGlobal.headTemplate+ 996 '<tbody></tbody>'+ 997 DPGlobal.footTemplate+ 998 '</table>'+ 999 '</div>'+ 1000 '<div class="datepicker-months">'+ 1001 '<table class="table-condensed">'+ 1002 DPGlobal.headTemplate+ 1003 DPGlobal.contTemplate+ 1004 DPGlobal.footTemplate+ 1005 '</table>'+ 1006 '</div>'+ 1007 '<div class="datepicker-years">'+ 1008 '<table class="table-condensed">'+ 1009 DPGlobal.headTemplate+ 1010 DPGlobal.contTemplate+ 1011 DPGlobal.footTemplate+ 1012 '</table>'+ 1013 '</div>'+ 1014 '</div>'; 1015 1016 $.fn.datepicker.DPGlobal = DPGlobal; 1017 1018}( 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.