readonly for date fields should not show popup
[roojs1] / Roo / bootstrap / form / DateField.js
1 /*
2  * - LGPL
3  *
4  * DateField
5  * 
6  */
7
8 /**
9  * @class Roo.bootstrap.form.DateField
10  * @extends Roo.bootstrap.form.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  * @cfg {Boolean} singleMode default false (true | false)
22  * 
23  * @cfg {Boolean} keyboardNavigation default true
24  * @cfg {String} language default en
25  * 
26  * @constructor
27  * Create a new DateField
28  * @param {Object} config The config object
29  */
30  
31 Roo.bootstrap.form.DateField = function(config){
32     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
33      this.addEvents({
34             /**
35              * @event show
36              * Fires when this field show.
37              * @param {Roo.bootstrap.form.DateField} this
38              * @param {Mixed} date The date value
39              */
40             show : true,
41             /**
42              * @event show
43              * Fires when this field hide.
44              * @param {Roo.bootstrap.form.DateField} this
45              * @param {Mixed} date The date value
46              */
47             hide : true,
48             /**
49              * @event select
50              * Fires when select a date.
51              * @param {Roo.bootstrap.form.DateField} this
52              * @param {Mixed} date The date value
53              */
54             select : true,
55             /**
56              * @event beforeselect
57              * Fires when before select a date.
58              * @param {Roo.bootstrap.form.DateField} this
59              * @param {Mixed} date The date value
60              */
61             beforeselect : true
62         });
63 };
64
65 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
66     
67     /**
68      * @cfg {String} format
69      * The default date format string which can be overriden for localization support.  The format must be
70      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
71      */
72     format : "m/d/y",
73     
74     weekStart : 0,
75     
76     viewMode : '',
77     
78     minViewMode : '',
79     
80     todayHighlight : false,
81     
82     todayBtn: false,
83     
84     language: 'en',
85     
86     keyboardNavigation: true,
87     
88     calendarWeeks: false,
89     
90     startDate: -Infinity,
91     
92     endDate: Infinity,
93     
94     daysOfWeekDisabled: [],
95     
96     _events: [],
97     
98     singleMode : false,
99
100     hiddenField : false,
101     
102     UTCDate: function()
103     {
104         return new Date(Date.UTC.apply(Date, arguments));
105     },
106     
107     UTCToday: function()
108     {
109         var today = new Date();
110         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
111     },
112     
113     getDate: function() {
114             var d = this.getUTCDate();
115             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
116     },
117     
118     getUTCDate: function() {
119             return this.date;
120     },
121     
122     setDate: function(d) {
123             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
124     },
125     
126     setUTCDate: function(d) {
127             this.date = d;
128             this.setValue(this.date);
129     },
130
131     translateDates: function(lang) 
132     {
133         var translation = Roo.bootstrap.form.DateField.dates[lang] = {
134             days: [],
135             daysShort: [],
136             daysMin: [],
137             months: [],
138             monthsShort: []
139         };
140
141         var locale = lang.replace('_', '-');
142
143         var is_latin = [ 'zh-hk', 'zh-cn', 'jp', 'ko' ].indexOf(locale.toLowerCase()) < 0; 
144                  
145
146         // fill days
147         for(var i = 0; i < 7; i++) {
148             var date = new Date(2020, 0, 5 + i);
149
150             var day = new Intl.DateTimeFormat(locale, {
151                 weekday : 'long'
152             }).format(date);
153
154             var dayShort = new Intl.DateTimeFormat(locale, {
155                 weekday : 'short'
156             }).format(date);
157
158             var dayMin = new Intl.DateTimeFormat(locale, {
159                 weekday : 'narrow'
160             }).format(date);
161
162             if(is_latin) {
163                 dayShort = day.substring(0, 3);
164                 dayMin = day.substring(0, 2);
165             }
166             
167             translation.days.push(day);
168             translation.daysShort.push(dayShort);
169             translation.daysMin.push(dayMin);
170         }
171
172         // fill months
173         for(var i = 0; i < 12; i++) {
174             var date = new Date(2020, i);
175
176             var month = new Intl.DateTimeFormat(locale, {
177                 month : 'long'
178             }).format(date);
179
180             var monthShort = new Intl.DateTimeFormat(locale, {
181                 month : 'short'
182             }).format(date);
183
184             if(is_latin) {
185                 monthShort = month.substring(0, 3);
186             }
187
188             translation.months.push(month);
189             translation.monthsShort.push(monthShort);
190         }
191     },
192         
193     onRender: function(ct, position)
194     {
195         
196         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
197
198         this.translateDates(this.language);
199         
200         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
201         this.format = this.format || 'm/d/y';
202         this.isInline = false;
203         this.isInput = true;
204         this.component = this.el.select('.add-on', true).first() || false;
205         this.component = (this.component && this.component.length === 0) ? false : this.component;
206         this.hasInput = this.component && this.inputEl().length;
207         
208         if (typeof(this.minViewMode === 'string')) {
209             switch (this.minViewMode) {
210                 case 'months':
211                     this.minViewMode = 1;
212                     break;
213                 case 'years':
214                     this.minViewMode = 2;
215                     break;
216                 default:
217                     this.minViewMode = 0;
218                     break;
219             }
220         }
221         
222         if (typeof(this.viewMode === 'string')) {
223             switch (this.viewMode) {
224                 case 'months':
225                     this.viewMode = 1;
226                     break;
227                 case 'years':
228                     this.viewMode = 2;
229                     break;
230                 default:
231                     this.viewMode = 0;
232                     break;
233             }
234         }
235                 
236         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
237         
238 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
239         
240         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
241         
242         this.picker().on('mousedown', this.onMousedown, this);
243         this.picker().on('click', this.onClick, this);
244         
245         this.picker().addClass('datepicker-dropdown');
246         
247         this.startViewMode = this.viewMode;
248         
249         if(this.singleMode){
250             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
251                 v.setVisibilityMode(Roo.Element.DISPLAY);
252                 v.hide();
253             });
254             
255             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
256                 v.setStyle('width', '189px');
257             });
258         }
259         
260         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
261             v.dom.innerHTML = Roo.bootstrap.form.DateField.todayText;
262         });
263                         
264         
265         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
266         
267         this.setStartDate(this.startDate);
268         this.setEndDate(this.endDate);
269         
270         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
271         
272         this.fillDow();
273         this.fillMonths();
274         this.update();
275         this.showMode();
276         
277         if(this.isInline) {
278             this.showPopup();
279         }
280
281         this.hiddenField = this.inputEl().insertSibling(
282             {tag : 'input', type : 'hidden', name : this.name},
283             'before',
284             true
285         );
286         this.inputEl().dom.setAttribute('name', this.name + '____hidden___');
287
288     },
289     
290     picker : function()
291     {
292         return this.pickerEl;
293 //        return this.el.select('.datepicker', true).first();
294     },
295     
296     fillDow: function()
297     {
298         var dowCnt = this.weekStart;
299         
300         var dow = {
301             tag: 'tr',
302             cn: [
303                 
304             ]
305         };
306         
307         while (dowCnt < this.weekStart + 7) {
308             dow.cn.push({
309                 tag: 'th',
310                 cls: 'dow',
311                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
312             });
313         }
314         
315         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
316     },
317     
318     fillMonths: function()
319     {    
320         var i = 0;
321         var months = this.picker().select('>.datepicker-months td', true).first();
322         
323         months.dom.innerHTML = '';
324         
325         while (i < 12) {
326             var month = {
327                 tag: 'span',
328                 cls: 'month',
329                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
330             };
331             
332             months.createChild(month);
333         }
334         
335     },
336     
337     update: function()
338     {
339         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;
340         
341         if (this.date < this.startDate) {
342             this.viewDate = new Date(this.startDate);
343         } else if (this.date > this.endDate) {
344             this.viewDate = new Date(this.endDate);
345         } else {
346             this.viewDate = new Date(this.date);
347         }
348         
349         this.fill();
350     },
351     
352     fill: function() 
353     {
354         var d = new Date(this.viewDate),
355                 year = d.getUTCFullYear(),
356                 month = d.getUTCMonth(),
357                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
358                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
359                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
360                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
361                 currentDate = this.date && this.date.valueOf(),
362                 today = this.UTCToday();
363         
364         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
365     
366         this.updateNavArrows();
367         this.fillMonths();
368                                                 
369         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
370         
371         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
372          
373         prevMonth.setUTCDate(day);
374         
375         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
376         
377         var nextMonth = new Date(prevMonth);
378         
379         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
380         
381         nextMonth = nextMonth.valueOf();
382         
383         var fillMonths = false;
384         
385         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
386         
387         while(prevMonth.valueOf() <= nextMonth) {
388             var clsName = '';
389             
390             if (prevMonth.getUTCDay() === this.weekStart) {
391                 if(fillMonths){
392                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
393                 }
394                     
395                 fillMonths = {
396                     tag: 'tr',
397                     cn: []
398                 };
399             }
400             
401             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
402                 clsName += ' old';
403             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
404                 clsName += ' new';
405             }
406             if (this.todayHighlight &&
407                 prevMonth.getUTCFullYear() == today.getFullYear() &&
408                 prevMonth.getUTCMonth() == today.getMonth() &&
409                 prevMonth.getUTCDate() == today.getDate()) {
410                 clsName += ' today';
411             }
412             
413             if (currentDate && prevMonth.valueOf() === currentDate) {
414                 clsName += ' active';
415             }
416             
417             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
418                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
419                     clsName += ' disabled';
420             }
421             
422             fillMonths.cn.push({
423                 tag: 'td',
424                 cls: 'day ' + clsName,
425                 html: prevMonth.getDate()
426             });
427             
428             prevMonth.setDate(prevMonth.getDate()+1);
429         }
430           
431         var currentYear = this.date && this.date.getUTCFullYear();
432         var currentMonth = this.date && this.date.getUTCMonth();
433         
434         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
435         
436         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
437             v.removeClass('active');
438             
439             if(currentYear === year && k === currentMonth){
440                 v.addClass('active');
441             }
442             
443             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
444                 v.addClass('disabled');
445             }
446             
447         });
448         
449         
450         year = parseInt(year/10, 10) * 10;
451         
452         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
453         
454         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
455         
456         year -= 1;
457         for (var i = -1; i < 11; i++) {
458             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
459                 tag: 'span',
460                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
461                 html: year
462             });
463             
464             year += 1;
465         }
466     },
467     
468     showMode: function(dir) 
469     {
470         if (dir) {
471             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
472         }
473         
474         Roo.each(this.picker().select('>div',true).elements, function(v){
475             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
476             v.hide();
477         });
478         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
479     },
480     
481     place: function()
482     {
483         if(this.isInline) {
484             return;
485         }
486         
487         this.picker().removeClass(['bottom', 'top']);
488         
489         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
490             /*
491              * place to the top of element!
492              *
493              */
494             
495             this.picker().addClass('top');
496             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
497             
498             return;
499         }
500         
501         this.picker().addClass('bottom');
502         
503         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
504     },
505     
506     // return false when it fails
507     parseDate : function(value)
508     {
509         if(!value) {
510             return false;
511         }
512         if(value instanceof Date){
513             return value;
514         }
515         var v = Date.parseDate(value, 'Y-m-d');
516
517         return (typeof(v) == 'undefined') ? false : v;
518     },
519     
520     formatDate : function(date, fmt)
521     {   
522         return (!date || !(date instanceof Date)) ?
523         date : date.dateFormat(fmt || this.format);
524     },
525
526     translateDate : function(date)
527     {
528         switch(this.language) {
529             case 'zh_CN':
530                 return new Intl.DateTimeFormat('zh-CN', {
531                     year : 'numeric',
532                     month : 'long',
533                     day : 'numeric'
534                 }).format(date);
535             default :
536                 return this.formatDate(date);
537         }
538     },
539     
540     onFocus : function()
541     {
542         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
543         this.showPopup();
544     },
545     
546     onBlur : function()
547     {
548         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
549
550         if(!this.readOnly) {
551             var d = this.inputEl().getValue();
552             var date = this.parseDate(d);
553             if(date) {
554                 this.setValue(date);
555             }
556             else {
557                 this.setValue(this.getValue());
558             }
559         }
560                 
561         this.hidePopup();
562     },
563     
564     showPopup : function()
565     {
566         if(this.readOnly) {
567             return;
568         }
569         this.picker().show();
570         this.update();
571         this.place();
572         
573         this.fireEvent('showpopup', this, this.date);
574     },
575     
576     hidePopup : function()
577     {
578         if(this.isInline) {
579             return;
580         }
581         this.picker().hide();
582         this.viewMode = this.startViewMode;
583         this.showMode();
584
585         this.inputEl().blur();
586         
587         this.fireEvent('hidepopup', this, this.date);
588         
589     },
590     
591     onMousedown: function(e)
592     {
593         e.stopPropagation();
594         e.preventDefault();
595     },
596     
597     keyup: function(e)
598     {
599         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
600         this.update();
601     },
602
603     setValue: function(v)
604     {
605         if(this.fireEvent('beforeselect', this, v) !== false){
606             var d = this.parseDate(v);
607
608             if(!d) {
609                 this.date = this.viewDate = this.value = this.hiddenField.value =  '';
610                 if(this.rendered){
611                     this.inputEl().dom.value = '';
612                     this.validate();
613                 }
614                 return;
615             }
616
617             d = new Date(d).clearTime();
618
619             this.value = this.hiddenField.value = d.dateFormat('Y-m-d');
620
621             v = this.translateDate(d);
622             if(this.rendered){
623                 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
624                 this.validate();
625             }
626
627             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
628
629             this.update();
630
631             this.fireEvent('select', this, this.date);
632         }
633     },
634
635     // bypass validation
636     setRawValue : function(v){
637         if(this.fireEvent('beforeselect', this, v) !== false){
638             var d = this.parseDate(v);
639
640             if(!d) {
641                 this.date = this.viewDate = this.value = this.hiddenField.value =  '';
642                 if(this.rendered){
643                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
644                 }
645                 return;
646             }
647
648             d = new Date(d).clearTime();
649
650             this.value = this.hiddenField.value = d.dateFormat('Y-m-d');
651
652             v = this.translateDate(d);
653             if(this.rendered){
654                 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
655             }
656
657             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
658
659             this.update();
660
661             this.fireEvent('select', this, this.date);
662         }
663     },
664     
665     getValue: function()
666     {
667         return this.value;
668     },
669
670     getRawValue : function(){
671         return this.getValue();
672     },
673     
674     fireKey: function(e)
675     {
676         if (!this.picker().isVisible()){
677             if (e.keyCode == 27) { // allow escape to hide and re-show picker
678                 this.showPopup();
679             }
680             return;
681         }
682         
683         var dateChanged = false,
684         dir, day, month,
685         newDate, newViewDate;
686         
687         switch(e.keyCode){
688             case 27: // escape
689                 this.hidePopup();
690                 e.preventDefault();
691                 break;
692             case 37: // left
693             case 39: // right
694                 if (!this.keyboardNavigation) {
695                     break;
696                 }
697                 dir = e.keyCode == 37 ? -1 : 1;
698                 
699                 if (e.ctrlKey){
700                     newDate = this.moveYear(this.date, dir);
701                     newViewDate = this.moveYear(this.viewDate, dir);
702                 } else if (e.shiftKey){
703                     newDate = this.moveMonth(this.date, dir);
704                     newViewDate = this.moveMonth(this.viewDate, dir);
705                 } else {
706                     newDate = new Date(this.date);
707                     newDate.setUTCDate(this.date.getUTCDate() + dir);
708                     newViewDate = new Date(this.viewDate);
709                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
710                 }
711                 if (this.dateWithinRange(newDate)){
712                     this.date = newDate;
713                     this.viewDate = newViewDate;
714                     this.setValue(this.date);
715 //                    this.update();
716                     e.preventDefault();
717                     dateChanged = true;
718                 }
719                 break;
720             case 38: // up
721             case 40: // down
722                 if (!this.keyboardNavigation) {
723                     break;
724                 }
725                 dir = e.keyCode == 38 ? -1 : 1;
726                 if (e.ctrlKey){
727                     newDate = this.moveYear(this.date, dir);
728                     newViewDate = this.moveYear(this.viewDate, dir);
729                 } else if (e.shiftKey){
730                     newDate = this.moveMonth(this.date, dir);
731                     newViewDate = this.moveMonth(this.viewDate, dir);
732                 } else {
733                     newDate = new Date(this.date);
734                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
735                     newViewDate = new Date(this.viewDate);
736                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
737                 }
738                 if (this.dateWithinRange(newDate)){
739                     this.date = newDate;
740                     this.viewDate = newViewDate;
741                     this.setValue(this.date);
742 //                    this.update();
743                     e.preventDefault();
744                     dateChanged = true;
745                 }
746                 break;
747             case 13: // enter
748                 this.setValue(this.date);
749                 this.hidePopup();
750                 e.preventDefault();
751                 break;
752             case 9: // tab
753                 this.setValue(this.date);
754                 this.hidePopup();
755                 break;
756             case 16: // shift
757             case 17: // ctrl
758             case 18: // alt
759                 break;
760             default :
761                 this.hidePopup();
762                 
763         }
764     },
765     
766     
767     onClick: function(e) 
768     {
769         e.stopPropagation();
770         e.preventDefault();
771         
772         var target = e.getTarget();
773         
774         if(target.nodeName.toLowerCase() === 'i'){
775             target = Roo.get(target).dom.parentNode;
776         }
777         
778         var nodeName = target.nodeName;
779         var className = target.className;
780         var html = target.innerHTML;
781         //Roo.log(nodeName);
782         
783         switch(nodeName.toLowerCase()) {
784             case 'th':
785                 switch(className) {
786                     case 'switch':
787                         this.showMode(1);
788                         break;
789                     case 'prev':
790                     case 'next':
791                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
792                         switch(this.viewMode){
793                                 case 0:
794                                         this.viewDate = this.moveMonth(this.viewDate, dir);
795                                         break;
796                                 case 1:
797                                 case 2:
798                                         this.viewDate = this.moveYear(this.viewDate, dir);
799                                         break;
800                         }
801                         this.fill();
802                         break;
803                     case 'today':
804                         var date = new Date();
805                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
806 //                        this.fill()
807                         this.setValue(this.date);
808                         
809                         this.hidePopup();
810                         break;
811                 }
812                 break;
813             case 'span':
814                 if (className.indexOf('disabled') < 0) {
815                 if (!this.viewDate) {
816                     this.viewDate = new Date();
817                 }
818                 this.viewDate.setUTCDate(1);
819                     if (className.indexOf('month') > -1) {
820                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
821                     } else {
822                         var year = parseInt(html, 10) || 0;
823                         this.viewDate.setUTCFullYear(year);
824                         
825                     }
826                     
827                     if(this.singleMode){
828                         this.setValue(this.viewDate);
829                         this.hidePopup();
830                         return;
831                     }
832                     
833                     this.showMode(-1);
834                     this.fill();
835                 }
836                 break;
837                 
838             case 'td':
839                 //Roo.log(className);
840                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
841                     var day = parseInt(html, 10) || 1;
842                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
843                         month = (this.viewDate || new Date()).getUTCMonth();
844
845                     if (className.indexOf('old') > -1) {
846                         if(month === 0 ){
847                             month = 11;
848                             year -= 1;
849                         }else{
850                             month -= 1;
851                         }
852                     } else if (className.indexOf('new') > -1) {
853                         if (month == 11) {
854                             month = 0;
855                             year += 1;
856                         } else {
857                             month += 1;
858                         }
859                     }
860                     //Roo.log([year,month,day]);
861                     this.date = this.UTCDate(year, month, day,0,0,0,0);
862                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
863 //                    this.fill();
864                     this.setValue(this.date);
865                     this.hidePopup();
866                 }
867                 break;
868         }
869     },
870     
871     setStartDate: function(startDate)
872     {
873         this.startDate = startDate || -Infinity;
874         if (this.startDate !== -Infinity) {
875             var date = this.parseDate(this.startDate);
876             this.startDate = date ? date : -Infinity;
877         }
878         this.update();
879         this.updateNavArrows();
880     },
881
882     setEndDate: function(endDate)
883     {
884         this.endDate = endDate || Infinity;
885         if (this.endDate !== Infinity) {
886             var date = this.parseDate(this.endDate);
887             this.endDate = date ? date : Infinity;
888         }
889         this.update();
890         this.updateNavArrows();
891     },
892     
893     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
894     {
895         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
896         if (typeof(this.daysOfWeekDisabled) !== 'object') {
897             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
898         }
899         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
900             return parseInt(d, 10);
901         });
902         this.update();
903         this.updateNavArrows();
904     },
905     
906     updateNavArrows: function() 
907     {
908         if(this.singleMode){
909             return;
910         }
911         
912         var d = new Date(this.viewDate),
913         year = d.getUTCFullYear(),
914         month = d.getUTCMonth();
915         
916         Roo.each(this.picker().select('.prev', true).elements, function(v){
917             v.show();
918             switch (this.viewMode) {
919                 case 0:
920
921                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
922                         v.hide();
923                     }
924                     break;
925                 case 1:
926                 case 2:
927                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
928                         v.hide();
929                     }
930                     break;
931             }
932         });
933         
934         Roo.each(this.picker().select('.next', true).elements, function(v){
935             v.show();
936             switch (this.viewMode) {
937                 case 0:
938
939                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
940                         v.hide();
941                     }
942                     break;
943                 case 1:
944                 case 2:
945                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
946                         v.hide();
947                     }
948                     break;
949             }
950         })
951     },
952     
953     moveMonth: function(date, dir)
954     {
955         if (!dir) {
956             return date;
957         }
958         var new_date = new Date(date.valueOf()),
959         day = new_date.getUTCDate(),
960         month = new_date.getUTCMonth(),
961         mag = Math.abs(dir),
962         new_month, test;
963         dir = dir > 0 ? 1 : -1;
964         if (mag == 1){
965             test = dir == -1
966             // If going back one month, make sure month is not current month
967             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
968             ? function(){
969                 return new_date.getUTCMonth() == month;
970             }
971             // If going forward one month, make sure month is as expected
972             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
973             : function(){
974                 return new_date.getUTCMonth() != new_month;
975             };
976             new_month = month + dir;
977             new_date.setUTCMonth(new_month);
978             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
979             if (new_month < 0 || new_month > 11) {
980                 new_month = (new_month + 12) % 12;
981             }
982         } else {
983             // For magnitudes >1, move one month at a time...
984             for (var i=0; i<mag; i++) {
985                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
986                 new_date = this.moveMonth(new_date, dir);
987             }
988             // ...then reset the day, keeping it in the new month
989             new_month = new_date.getUTCMonth();
990             new_date.setUTCDate(day);
991             test = function(){
992                 return new_month != new_date.getUTCMonth();
993             };
994         }
995         // Common date-resetting loop -- if date is beyond end of month, make it
996         // end of month
997         while (test()){
998             new_date.setUTCDate(--day);
999             new_date.setUTCMonth(new_month);
1000         }
1001         return new_date;
1002     },
1003
1004     moveYear: function(date, dir)
1005     {
1006         return this.moveMonth(date, dir*12);
1007     },
1008
1009     dateWithinRange: function(date)
1010     {
1011         return date >= this.startDate && date <= this.endDate;
1012     },
1013
1014     
1015     remove: function() 
1016     {
1017         this.picker().remove();
1018     },
1019     
1020     validateValue : function(value)
1021     {
1022         if(this.getVisibilityEl().hasClass('hidden')){
1023             return true;
1024         }
1025         
1026         if(value.length < 1)  {
1027             if(this.allowBlank){
1028                 return true;
1029             }
1030             return false;
1031         }
1032         
1033         if(value.length < this.minLength){
1034             return false;
1035         }
1036         if(value.length > this.maxLength){
1037             return false;
1038         }
1039         if(this.vtype){
1040             var vt = Roo.form.VTypes;
1041             if(!vt[this.vtype](value, this)){
1042                 return false;
1043             }
1044         }
1045         if(typeof this.validator == "function"){
1046             var msg = this.validator(value);
1047             if(msg !== true){
1048                 return false;
1049             }
1050         }
1051         
1052         if(this.regex && !this.regex.test(value)){
1053             return false;
1054         }
1055         
1056         if(!this.parseDate(value)){
1057             return false;
1058         }
1059         
1060         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
1061             return false;
1062         }      
1063         
1064         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
1065             return false;
1066         } 
1067         
1068         
1069         return true;
1070     },
1071     
1072     reset : function()
1073     {
1074         this.date = this.viewDate = '';
1075         
1076         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
1077     }
1078    
1079 });
1080
1081 Roo.apply(Roo.bootstrap.form.DateField,  {
1082     
1083     head : {
1084         tag: 'thead',
1085         cn: [
1086         {
1087             tag: 'tr',
1088             cn: [
1089             {
1090                 tag: 'th',
1091                 cls: 'prev',
1092                 html: '<i class="fa fa-arrow-left"/>'
1093             },
1094             {
1095                 tag: 'th',
1096                 cls: 'switch',
1097                 colspan: '5'
1098             },
1099             {
1100                 tag: 'th',
1101                 cls: 'next',
1102                 html: '<i class="fa fa-arrow-right"/>'
1103             }
1104
1105             ]
1106         }
1107         ]
1108     },
1109     
1110     content : {
1111         tag: 'tbody',
1112         cn: [
1113         {
1114             tag: 'tr',
1115             cn: [
1116             {
1117                 tag: 'td',
1118                 colspan: '7'
1119             }
1120             ]
1121         }
1122         ]
1123     },
1124     
1125     footer : {
1126         tag: 'tfoot',
1127         cn: [
1128         {
1129             tag: 'tr',
1130             cn: [
1131             {
1132                 tag: 'th',
1133                 colspan: '7',
1134                 cls: 'today'
1135             }
1136                     
1137             ]
1138         }
1139         ]
1140     },
1141     
1142     dates : {},
1143
1144     todayText : "Today",
1145     
1146     modes: [
1147     {
1148         clsName: 'days',
1149         navFnc: 'Month',
1150         navStep: 1
1151     },
1152     {
1153         clsName: 'months',
1154         navFnc: 'FullYear',
1155         navStep: 1
1156     },
1157     {
1158         clsName: 'years',
1159         navFnc: 'FullYear',
1160         navStep: 10
1161     }]
1162 });
1163
1164 Roo.apply(Roo.bootstrap.form.DateField,  {
1165   
1166     template : {
1167         tag: 'div',
1168         cls: 'datepicker dropdown-menu roo-dynamic shadow',
1169         cn: [
1170         {
1171             tag: 'div',
1172             cls: 'datepicker-days',
1173             cn: [
1174             {
1175                 tag: 'table',
1176                 cls: 'table-condensed',
1177                 cn:[
1178                 Roo.bootstrap.form.DateField.head,
1179                 {
1180                     tag: 'tbody'
1181                 },
1182                 Roo.bootstrap.form.DateField.footer
1183                 ]
1184             }
1185             ]
1186         },
1187         {
1188             tag: 'div',
1189             cls: 'datepicker-months',
1190             cn: [
1191             {
1192                 tag: 'table',
1193                 cls: 'table-condensed',
1194                 cn:[
1195                 Roo.bootstrap.form.DateField.head,
1196                 Roo.bootstrap.form.DateField.content,
1197                 Roo.bootstrap.form.DateField.footer
1198                 ]
1199             }
1200             ]
1201         },
1202         {
1203             tag: 'div',
1204             cls: 'datepicker-years',
1205             cn: [
1206             {
1207                 tag: 'table',
1208                 cls: 'table-condensed',
1209                 cn:[
1210                 Roo.bootstrap.form.DateField.head,
1211                 Roo.bootstrap.form.DateField.content,
1212                 Roo.bootstrap.form.DateField.footer
1213                 ]
1214             }
1215             ]
1216         }
1217         ]
1218     }
1219 });
1220
1221  
1222
1223