|
47 | 47 | });
|
48 | 48 | } else {
|
49 | 49 | if (this.component){
|
| 50 | + // For components that are not readonly, allow keyboard nav |
| 51 | + this.element.find('input').on({ |
| 52 | + focus: $.proxy(this.show, this), |
| 53 | + blur: $.proxy(this._hide, this), |
| 54 | + keyup: $.proxy(this.update, this), |
| 55 | + keydown: $.proxy(this.keydown, this) |
| 56 | + }); |
| 57 | + |
50 | 58 | this.component.on('click', $.proxy(this.show, this));
|
51 | 59 | var element = this.element.find('input');
|
52 | 60 | element.on({
|
|
64 | 72 | this.autoclose = this.element.data('date-autoclose');
|
65 | 73 | }
|
66 | 74 |
|
67 |
| - switch(options.startView){ |
| 75 | + switch(options.startView || this.element.data('date-start-view')){ |
68 | 76 | case 2:
|
69 | 77 | case 'decade':
|
70 | 78 | this.viewMode = this.startViewMode = 2;
|
|
155 | 163 | },
|
156 | 164 |
|
157 | 165 | setValue: function() {
|
158 |
| - var formated = DPGlobal.formatDate(this.date, this.format, this.language); |
| 166 | + var formatted = DPGlobal.formatDate(this.date, this.format, this.language); |
159 | 167 | if (!this.isInput) {
|
160 | 168 | if (this.component){
|
161 |
| - this.element.find('input').prop('value', formated); |
| 169 | + this.element.find('input').prop('value', formatted); |
162 | 170 | }
|
163 |
| - this.element.data('date', formated); |
| 171 | + this.element.data('date', formatted); |
164 | 172 | } else {
|
165 |
| - this.element.prop('value', formated); |
| 173 | + this.element.prop('value', formatted); |
166 | 174 | }
|
167 | 175 | },
|
168 | 176 |
|
|
185 | 193 | },
|
186 | 194 |
|
187 | 195 | place: function(){
|
| 196 | + var zIndex = parseInt(this.element.parents().filter(function() { |
| 197 | + return $(this).css('z-index') != 'auto'; |
| 198 | + }).first().css('z-index'))+10; |
188 | 199 | var offset = this.component ? this.component.offset() : this.element.offset();
|
189 | 200 | this.picker.css({
|
190 | 201 | top: offset.top + this.height,
|
191 |
| - left: offset.left |
| 202 | + left: offset.left, |
| 203 | + zIndex: zIndex |
192 | 204 | });
|
193 | 205 | },
|
194 | 206 |
|
195 | 207 | update: function(){
|
196 | 208 | this.date = DPGlobal.parseDate(
|
197 |
| - this.isInput ? this.element.prop('value') : this.element.data('date'), |
| 209 | + this.isInput ? this.element.prop('value') : this.element.data('date') || this.element.find('input').prop('value'), |
198 | 210 | this.format, this.language
|
199 | 211 | );
|
200 | 212 | if (this.date < this.startDate) {
|
|
240 | 252 | this.updateNavArrows();
|
241 | 253 | this.fillMonths();
|
242 | 254 | var prevMonth = new Date(year, month-1, 28,0,0,0,0),
|
243 |
| - day = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth()); |
| 255 | + day = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth()), |
| 256 | + prevDate, dstDay = 0, date; |
244 | 257 | prevMonth.setDate(day);
|
245 | 258 | prevMonth.setDate(day - (prevMonth.getDay() - this.weekStart + 7)%7);
|
246 | 259 | var nextMonth = new Date(prevMonth);
|
247 | 260 | nextMonth.setDate(nextMonth.getDate() + 42);
|
248 | 261 | nextMonth = nextMonth.valueOf();
|
249 |
| - html = []; |
| 262 | + var html = []; |
250 | 263 | var clsName;
|
251 | 264 | while(prevMonth.valueOf() < nextMonth) {
|
252 | 265 | if (prevMonth.getDay() == this.weekStart) {
|
|
264 | 277 | if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate) {
|
265 | 278 | clsName += ' disabled';
|
266 | 279 | }
|
267 |
| - html.push('<td class="day'+clsName+'">'+prevMonth.getDate() + '</td>'); |
| 280 | + date = prevMonth.getDate(); |
| 281 | + if (dstDay == -1) date++; |
| 282 | + html.push('<td class="day'+clsName+'">'+date+ '</td>'); |
268 | 283 | if (prevMonth.getDay() == this.weekEnd) {
|
269 | 284 | html.push('</tr>');
|
270 | 285 | }
|
| 286 | + prevDate = prevMonth.getDate(); |
271 | 287 | prevMonth.setDate(prevMonth.getDate()+1);
|
| 288 | + if (prevMonth.getHours() != 0) { |
| 289 | + // Fix for DST bug: if we are no longer at start of day, a DST jump probably happened |
| 290 | + // We either fell back (eg, Jan 1 00:00 -> Jan 1 23:00) |
| 291 | + // or jumped forward (eg, Jan 1 00:00 -> Jan 2 01:00) |
| 292 | + // Unfortunately, I can think of no way to test this in the unit tests, as it depends |
| 293 | + // on the TZ of the client system. |
| 294 | + if (!dstDay) { |
| 295 | + // We are not currently handling a dst day (next round will deal with it) |
| 296 | + if (prevMonth.getDate() == prevDate) |
| 297 | + // We must compensate for fall-back |
| 298 | + dstDay = -1; |
| 299 | + else |
| 300 | + // We must compensate for a jump-ahead |
| 301 | + dstDay = +1; |
| 302 | + } |
| 303 | + else { |
| 304 | + // The last round was our dst day (hours are still non-zero) |
| 305 | + if (dstDay == -1) |
| 306 | + // For a fall-back, fast-forward to next midnight |
| 307 | + prevMonth.setHours(24); |
| 308 | + else |
| 309 | + // For a jump-ahead, just reset to 0 |
| 310 | + prevMonth.setHours(0); |
| 311 | + // Reset minutes, as some TZs may be off by portions of an hour |
| 312 | + prevMonth.setMinutes(0); |
| 313 | + dstDay = 0; |
| 314 | + } |
| 315 | + } |
272 | 316 | }
|
273 | 317 | this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
|
274 | 318 | var currentYear = this.date.getFullYear();
|
|
368 | 412 | break;
|
369 | 413 | case 'span':
|
370 | 414 | if (!target.is('.disabled')) {
|
| 415 | + this.viewDate.setDate(1); |
371 | 416 | if (target.is('.month')) {
|
372 | 417 | var month = target.parent().find('span').index(target);
|
373 | 418 | this.viewDate.setDate(1);
|
|
485 | 530 | return this.moveMonth(date, dir*12);
|
486 | 531 | },
|
487 | 532 |
|
| 533 | + dateWithinRange: function(date){ |
| 534 | + return date >= this.startDate && date <= this.endDate; |
| 535 | + }, |
| 536 | + |
488 | 537 | keydown: function(e){
|
489 | 538 | if (this.picker.is(':not(:visible)')){
|
490 | 539 | if (e.keyCode == 27) // allow escape to hide and re-show picker
|
491 | 540 | this.show();
|
492 | 541 | return;
|
493 | 542 | }
|
494 | 543 | var dateChanged = false,
|
495 |
| - dir, day, month; |
| 544 | + dir, day, month, |
| 545 | + newDate, newViewDate; |
496 | 546 | switch(e.keyCode){
|
497 | 547 | case 27: // escape
|
498 | 548 | this.hide();
|
|
502 | 552 | case 39: // right
|
503 | 553 | dir = e.keyCode == 37 ? -1 : 1;
|
504 | 554 | if (e.ctrlKey){
|
505 |
| - this.date = this.moveYear(this.date, dir); |
506 |
| - this.viewDate = this.moveYear(this.viewDate, dir); |
| 555 | + newDate = this.moveYear(this.date, dir); |
| 556 | + newViewDate = this.moveYear(this.viewDate, dir); |
507 | 557 | } else if (e.shiftKey){
|
508 |
| - this.date = this.moveMonth(this.date, dir); |
509 |
| - this.viewDate = this.moveMonth(this.viewDate, dir); |
| 558 | + newDate = this.moveMonth(this.date, dir); |
| 559 | + newViewDate = this.moveMonth(this.viewDate, dir); |
510 | 560 | } else {
|
511 |
| - this.date.setDate(this.date.getDate() + dir); |
512 |
| - this.viewDate.setDate(this.viewDate.getDate() + dir); |
| 561 | + newDate = new Date(this.date); |
| 562 | + newDate.setDate(this.date.getDate() + dir); |
| 563 | + newViewDate = new Date(this.viewDate); |
| 564 | + newViewDate.setDate(this.viewDate.getDate() + dir); |
| 565 | + } |
| 566 | + if (this.dateWithinRange(newDate)){ |
| 567 | + this.date = newDate; |
| 568 | + this.viewDate = newViewDate; |
| 569 | + this.setValue(); |
| 570 | + this.update(); |
| 571 | + e.preventDefault(); |
| 572 | + dateChanged = true; |
513 | 573 | }
|
514 |
| - this.setValue(); |
515 |
| - this.update(); |
516 |
| - e.preventDefault(); |
517 |
| - dateChanged = true; |
518 | 574 | break;
|
519 | 575 | case 38: // up
|
520 | 576 | case 40: // down
|
521 | 577 | dir = e.keyCode == 38 ? -1 : 1;
|
522 | 578 | if (e.ctrlKey){
|
523 |
| - this.date = this.moveYear(this.date, dir); |
524 |
| - this.viewDate = this.moveYear(this.viewDate, dir); |
| 579 | + newDate = this.moveYear(this.date, dir); |
| 580 | + newViewDate = this.moveYear(this.viewDate, dir); |
525 | 581 | } else if (e.shiftKey){
|
526 |
| - this.date = this.moveMonth(this.date, dir); |
527 |
| - this.viewDate = this.moveMonth(this.viewDate, dir); |
| 582 | + newDate = this.moveMonth(this.date, dir); |
| 583 | + newViewDate = this.moveMonth(this.viewDate, dir); |
528 | 584 | } else {
|
529 |
| - this.date.setDate(this.date.getDate() + dir * 7); |
530 |
| - this.viewDate.setDate(this.viewDate.getDate() + dir * 7); |
| 585 | + newDate = new Date(this.date); |
| 586 | + newDate.setDate(this.date.getDate() + dir * 7); |
| 587 | + newViewDate = new Date(this.viewDate); |
| 588 | + newViewDate.setDate(this.viewDate.getDate() + dir * 7); |
| 589 | + } |
| 590 | + if (this.dateWithinRange(newDate)){ |
| 591 | + this.date = newDate; |
| 592 | + this.viewDate = newViewDate; |
| 593 | + this.setValue(); |
| 594 | + this.update(); |
| 595 | + e.preventDefault(); |
| 596 | + dateChanged = true; |
531 | 597 | }
|
532 |
| - this.setValue(); |
533 |
| - this.update(); |
534 |
| - e.preventDefault(); |
535 |
| - dateChanged = true; |
536 | 598 | break;
|
537 | 599 | case 13: // enter
|
538 | 600 | this.hide();
|
|
654 | 716 | }
|
655 | 717 | return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
|
656 | 718 | }
|
657 |
| - var parts = date ? date.match(this.nonpunctuation) : [], |
| 719 | + var parts = date && date.match(this.nonpunctuation) || [], |
658 | 720 | date = new Date(),
|
659 |
| - val, filtered; |
| 721 | + parsed = {}, |
| 722 | + setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'], |
| 723 | + setters_map = { |
| 724 | + yyyy: function(d,v){ return d.setFullYear(v); }, |
| 725 | + yy: function(d,v){ return d.setFullYear(2000+v); }, |
| 726 | + m: function(d,v){ |
| 727 | + v -= 1; |
| 728 | + while (v<0) v += 12; |
| 729 | + v %= 12; |
| 730 | + d.setMonth(v); |
| 731 | + while (d.getMonth() != v) |
| 732 | + d.setDate(d.getDate()-1); |
| 733 | + return d; |
| 734 | + }, |
| 735 | + d: function(d,v){ return d.setDate(v); } |
| 736 | + }, |
| 737 | + val, filtered, part; |
| 738 | + setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m']; |
| 739 | + setters_map['dd'] = setters_map['d']; |
660 | 740 | date = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
|
661 | 741 | if (parts.length == format.parts.length) {
|
662 | 742 | for (var i=0, cnt = format.parts.length; i < cnt; i++) {
|
663 |
| - val = parseInt(parts[i], 10)||1; |
664 |
| - switch(format.parts[i]) { |
665 |
| - case 'MM': |
666 |
| - filtered = $(dates[language].months).filter(function(){ |
667 |
| - var m = this.slice(0, parts[i].length), |
668 |
| - p = parts[i].slice(0, m.length); |
669 |
| - return m == p; |
670 |
| - }); |
671 |
| - val = $.inArray(filtered[0], dates[language].months) + 1; |
672 |
| - break; |
673 |
| - case 'M': |
674 |
| - filtered = $(dates[language].monthsShort).filter(function(){ |
675 |
| - var m = this.slice(0, parts[i].length), |
676 |
| - p = parts[i].slice(0, m.length); |
677 |
| - return m == p; |
678 |
| - }); |
679 |
| - val = $.inArray(filtered[0], dates[language].monthsShort) + 1; |
680 |
| - break; |
681 |
| - } |
682 |
| - switch(format.parts[i]) { |
683 |
| - case 'dd': |
684 |
| - case 'd': |
685 |
| - date.setDate(val); |
686 |
| - break; |
687 |
| - case 'mm': |
688 |
| - case 'm': |
689 |
| - case 'MM': |
690 |
| - case 'M': |
691 |
| - date.setMonth(val - 1); |
692 |
| - break; |
693 |
| - case 'yy': |
694 |
| - date.setFullYear(2000 + val); |
695 |
| - break; |
696 |
| - case 'yyyy': |
697 |
| - date.setFullYear(val); |
698 |
| - break; |
| 743 | + val = parseInt(parts[i], 10); |
| 744 | + part = format.parts[i]; |
| 745 | + if (isNaN(val)) { |
| 746 | + switch(part) { |
| 747 | + case 'MM': |
| 748 | + filtered = $(dates[language].months).filter(function(){ |
| 749 | + var m = this.slice(0, parts[i].length), |
| 750 | + p = parts[i].slice(0, m.length); |
| 751 | + return m == p; |
| 752 | + }); |
| 753 | + val = $.inArray(filtered[0], dates[language].months) + 1; |
| 754 | + break; |
| 755 | + case 'M': |
| 756 | + filtered = $(dates[language].monthsShort).filter(function(){ |
| 757 | + var m = this.slice(0, parts[i].length), |
| 758 | + p = parts[i].slice(0, m.length); |
| 759 | + return m == p; |
| 760 | + }); |
| 761 | + val = $.inArray(filtered[0], dates[language].monthsShort) + 1; |
| 762 | + break; |
| 763 | + } |
699 | 764 | }
|
| 765 | + parsed[part] = val; |
| 766 | + } |
| 767 | + for (var i=0, s; i<setters_order.length; i++){ |
| 768 | + s = setters_order[i]; |
| 769 | + if (s in parsed) |
| 770 | + setters_map[s](date, parsed[s]) |
700 | 771 | }
|
701 | 772 | }
|
702 | 773 | return date;
|
|
751 | 822 | '</div>'+
|
752 | 823 | '</div>';
|
753 | 824 |
|
754 |
| -}( window.jQuery ) |
| 825 | +}( window.jQuery ); |
0 commit comments