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