fix #7760 - translation allowed on date and time fields
[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         this.picker().show();
567         this.update();
568         this.place();
569         
570         this.fireEvent('showpopup', this, this.date);
571     },
572     
573     hidePopup : function()
574     {
575         if(this.isInline) {
576             return;
577         }
578         this.picker().hide();
579         this.viewMode = this.startViewMode;
580         this.showMode();
581         
582         this.fireEvent('hidepopup', this, this.date);
583         
584     },
585     
586     onMousedown: function(e)
587     {
588         e.stopPropagation();
589         e.preventDefault();
590     },
591     
592     keyup: function(e)
593     {
594         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
595         this.update();
596     },
597
598     setValue: function(v)
599     {
600         if(this.fireEvent('beforeselect', this, v) !== false){
601             var d = this.parseDate(v);
602
603             if(!d) {
604                 this.date = this.viewDate = this.value = this.hiddenField.value =  '';
605                 if(this.rendered){
606                     this.inputEl().dom.value = '';
607                     this.validate();
608                 }
609                 return;
610             }
611
612             d = new Date(d).clearTime();
613
614             this.value = this.hiddenField.value = d.dateFormat('Y-m-d');
615
616             v = this.translateDate(d);
617             if(this.rendered){
618                 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
619                 this.validate();
620             }
621
622             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
623
624             this.update();
625
626             this.fireEvent('select', this, this.date);
627         }
628     },
629
630     // bypass validation
631     setRawValue : function(v){
632         if(this.fireEvent('beforeselect', this, v) !== false){
633             var d = this.parseDate(v);
634
635             if(!d) {
636                 this.date = this.viewDate = this.value = this.hiddenField.value =  '';
637                 if(this.rendered){
638                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
639                 }
640                 return;
641             }
642
643             d = new Date(d).clearTime();
644
645             this.value = this.hiddenField.value = d.dateFormat('Y-m-d');
646
647             v = this.translateDate(d);
648             if(this.rendered){
649                 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
650             }
651
652             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
653
654             this.update();
655
656             this.fireEvent('select', this, this.date);
657         }
658     },
659     
660     getValue: function()
661     {
662         return this.value;
663     },
664
665     getRawValue : function(){
666         return this.getValue();
667     },
668     
669     fireKey: function(e)
670     {
671         if (!this.picker().isVisible()){
672             if (e.keyCode == 27) { // allow escape to hide and re-show picker
673                 this.showPopup();
674             }
675             return;
676         }
677         
678         var dateChanged = false,
679         dir, day, month,
680         newDate, newViewDate;
681         
682         switch(e.keyCode){
683             case 27: // escape
684                 this.hidePopup();
685                 e.preventDefault();
686                 break;
687             case 37: // left
688             case 39: // right
689                 if (!this.keyboardNavigation) {
690                     break;
691                 }
692                 dir = e.keyCode == 37 ? -1 : 1;
693                 
694                 if (e.ctrlKey){
695                     newDate = this.moveYear(this.date, dir);
696                     newViewDate = this.moveYear(this.viewDate, dir);
697                 } else if (e.shiftKey){
698                     newDate = this.moveMonth(this.date, dir);
699                     newViewDate = this.moveMonth(this.viewDate, dir);
700                 } else {
701                     newDate = new Date(this.date);
702                     newDate.setUTCDate(this.date.getUTCDate() + dir);
703                     newViewDate = new Date(this.viewDate);
704                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
705                 }
706                 if (this.dateWithinRange(newDate)){
707                     this.date = newDate;
708                     this.viewDate = newViewDate;
709                     this.setValue(this.date);
710 //                    this.update();
711                     e.preventDefault();
712                     dateChanged = true;
713                 }
714                 break;
715             case 38: // up
716             case 40: // down
717                 if (!this.keyboardNavigation) {
718                     break;
719                 }
720                 dir = e.keyCode == 38 ? -1 : 1;
721                 if (e.ctrlKey){
722                     newDate = this.moveYear(this.date, dir);
723                     newViewDate = this.moveYear(this.viewDate, dir);
724                 } else if (e.shiftKey){
725                     newDate = this.moveMonth(this.date, dir);
726                     newViewDate = this.moveMonth(this.viewDate, dir);
727                 } else {
728                     newDate = new Date(this.date);
729                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
730                     newViewDate = new Date(this.viewDate);
731                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
732                 }
733                 if (this.dateWithinRange(newDate)){
734                     this.date = newDate;
735                     this.viewDate = newViewDate;
736                     this.setValue(this.date);
737 //                    this.update();
738                     e.preventDefault();
739                     dateChanged = true;
740                 }
741                 break;
742             case 13: // enter
743                 this.setValue(this.date);
744                 this.hidePopup();
745                 e.preventDefault();
746                 break;
747             case 9: // tab
748                 this.setValue(this.date);
749                 this.hidePopup();
750                 break;
751             case 16: // shift
752             case 17: // ctrl
753             case 18: // alt
754                 break;
755             default :
756                 this.hidePopup();
757                 
758         }
759     },
760     
761     
762     onClick: function(e) 
763     {
764         e.stopPropagation();
765         e.preventDefault();
766         
767         var target = e.getTarget();
768         
769         if(target.nodeName.toLowerCase() === 'i'){
770             target = Roo.get(target).dom.parentNode;
771         }
772         
773         var nodeName = target.nodeName;
774         var className = target.className;
775         var html = target.innerHTML;
776         //Roo.log(nodeName);
777         
778         switch(nodeName.toLowerCase()) {
779             case 'th':
780                 switch(className) {
781                     case 'switch':
782                         this.showMode(1);
783                         break;
784                     case 'prev':
785                     case 'next':
786                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
787                         switch(this.viewMode){
788                                 case 0:
789                                         this.viewDate = this.moveMonth(this.viewDate, dir);
790                                         break;
791                                 case 1:
792                                 case 2:
793                                         this.viewDate = this.moveYear(this.viewDate, dir);
794                                         break;
795                         }
796                         this.fill();
797                         break;
798                     case 'today':
799                         var date = new Date();
800                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
801 //                        this.fill()
802                         this.setValue(this.date);
803                         
804                         this.hidePopup();
805                         break;
806                 }
807                 break;
808             case 'span':
809                 if (className.indexOf('disabled') < 0) {
810                 if (!this.viewDate) {
811                     this.viewDate = new Date();
812                 }
813                 this.viewDate.setUTCDate(1);
814                     if (className.indexOf('month') > -1) {
815                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
816                     } else {
817                         var year = parseInt(html, 10) || 0;
818                         this.viewDate.setUTCFullYear(year);
819                         
820                     }
821                     
822                     if(this.singleMode){
823                         this.setValue(this.viewDate);
824                         this.hidePopup();
825                         return;
826                     }
827                     
828                     this.showMode(-1);
829                     this.fill();
830                 }
831                 break;
832                 
833             case 'td':
834                 //Roo.log(className);
835                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
836                     var day = parseInt(html, 10) || 1;
837                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
838                         month = (this.viewDate || new Date()).getUTCMonth();
839
840                     if (className.indexOf('old') > -1) {
841                         if(month === 0 ){
842                             month = 11;
843                             year -= 1;
844                         }else{
845                             month -= 1;
846                         }
847                     } else if (className.indexOf('new') > -1) {
848                         if (month == 11) {
849                             month = 0;
850                             year += 1;
851                         } else {
852                             month += 1;
853                         }
854                     }
855                     //Roo.log([year,month,day]);
856                     this.date = this.UTCDate(year, month, day,0,0,0,0);
857                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
858 //                    this.fill();
859                     this.setValue(this.date);
860                     this.hidePopup();
861                 }
862                 break;
863         }
864     },
865     
866     setStartDate: function(startDate)
867     {
868         this.startDate = startDate || -Infinity;
869         if (this.startDate !== -Infinity) {
870             var date = this.parseDate(this.startDate);
871             this.startDate = date ? date : -Infinity;
872         }
873         this.update();
874         this.updateNavArrows();
875     },
876
877     setEndDate: function(endDate)
878     {
879         this.endDate = endDate || Infinity;
880         if (this.endDate !== Infinity) {
881             var date = this.parseDate(this.endDate);
882             this.endDate = date ? date : Infinity;
883         }
884         this.update();
885         this.updateNavArrows();
886     },
887     
888     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
889     {
890         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
891         if (typeof(this.daysOfWeekDisabled) !== 'object') {
892             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
893         }
894         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
895             return parseInt(d, 10);
896         });
897         this.update();
898         this.updateNavArrows();
899     },
900     
901     updateNavArrows: function() 
902     {
903         if(this.singleMode){
904             return;
905         }
906         
907         var d = new Date(this.viewDate),
908         year = d.getUTCFullYear(),
909         month = d.getUTCMonth();
910         
911         Roo.each(this.picker().select('.prev', true).elements, function(v){
912             v.show();
913             switch (this.viewMode) {
914                 case 0:
915
916                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
917                         v.hide();
918                     }
919                     break;
920                 case 1:
921                 case 2:
922                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
923                         v.hide();
924                     }
925                     break;
926             }
927         });
928         
929         Roo.each(this.picker().select('.next', true).elements, function(v){
930             v.show();
931             switch (this.viewMode) {
932                 case 0:
933
934                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
935                         v.hide();
936                     }
937                     break;
938                 case 1:
939                 case 2:
940                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
941                         v.hide();
942                     }
943                     break;
944             }
945         })
946     },
947     
948     moveMonth: function(date, dir)
949     {
950         if (!dir) {
951             return date;
952         }
953         var new_date = new Date(date.valueOf()),
954         day = new_date.getUTCDate(),
955         month = new_date.getUTCMonth(),
956         mag = Math.abs(dir),
957         new_month, test;
958         dir = dir > 0 ? 1 : -1;
959         if (mag == 1){
960             test = dir == -1
961             // If going back one month, make sure month is not current month
962             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
963             ? function(){
964                 return new_date.getUTCMonth() == month;
965             }
966             // If going forward one month, make sure month is as expected
967             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
968             : function(){
969                 return new_date.getUTCMonth() != new_month;
970             };
971             new_month = month + dir;
972             new_date.setUTCMonth(new_month);
973             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
974             if (new_month < 0 || new_month > 11) {
975                 new_month = (new_month + 12) % 12;
976             }
977         } else {
978             // For magnitudes >1, move one month at a time...
979             for (var i=0; i<mag; i++) {
980                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
981                 new_date = this.moveMonth(new_date, dir);
982             }
983             // ...then reset the day, keeping it in the new month
984             new_month = new_date.getUTCMonth();
985             new_date.setUTCDate(day);
986             test = function(){
987                 return new_month != new_date.getUTCMonth();
988             };
989         }
990         // Common date-resetting loop -- if date is beyond end of month, make it
991         // end of month
992         while (test()){
993             new_date.setUTCDate(--day);
994             new_date.setUTCMonth(new_month);
995         }
996         return new_date;
997     },
998
999     moveYear: function(date, dir)
1000     {
1001         return this.moveMonth(date, dir*12);
1002     },
1003
1004     dateWithinRange: function(date)
1005     {
1006         return date >= this.startDate && date <= this.endDate;
1007     },
1008
1009     
1010     remove: function() 
1011     {
1012         this.picker().remove();
1013     },
1014     
1015     validateValue : function(value)
1016     {
1017         if(this.getVisibilityEl().hasClass('hidden')){
1018             return true;
1019         }
1020         
1021         if(value.length < 1)  {
1022             if(this.allowBlank){
1023                 return true;
1024             }
1025             return false;
1026         }
1027         
1028         if(value.length < this.minLength){
1029             return false;
1030         }
1031         if(value.length > this.maxLength){
1032             return false;
1033         }
1034         if(this.vtype){
1035             var vt = Roo.form.VTypes;
1036             if(!vt[this.vtype](value, this)){
1037                 return false;
1038             }
1039         }
1040         if(typeof this.validator == "function"){
1041             var msg = this.validator(value);
1042             if(msg !== true){
1043                 return false;
1044             }
1045         }
1046         
1047         if(this.regex && !this.regex.test(value)){
1048             return false;
1049         }
1050         
1051         if(!this.parseDate(value)){
1052             return false;
1053         }
1054         
1055         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
1056             return false;
1057         }      
1058         
1059         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
1060             return false;
1061         } 
1062         
1063         
1064         return true;
1065     },
1066     
1067     reset : function()
1068     {
1069         this.date = this.viewDate = '';
1070         
1071         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
1072     }
1073    
1074 });
1075
1076 Roo.apply(Roo.bootstrap.form.DateField,  {
1077     
1078     head : {
1079         tag: 'thead',
1080         cn: [
1081         {
1082             tag: 'tr',
1083             cn: [
1084             {
1085                 tag: 'th',
1086                 cls: 'prev',
1087                 html: '<i class="fa fa-arrow-left"/>'
1088             },
1089             {
1090                 tag: 'th',
1091                 cls: 'switch',
1092                 colspan: '5'
1093             },
1094             {
1095                 tag: 'th',
1096                 cls: 'next',
1097                 html: '<i class="fa fa-arrow-right"/>'
1098             }
1099
1100             ]
1101         }
1102         ]
1103     },
1104     
1105     content : {
1106         tag: 'tbody',
1107         cn: [
1108         {
1109             tag: 'tr',
1110             cn: [
1111             {
1112                 tag: 'td',
1113                 colspan: '7'
1114             }
1115             ]
1116         }
1117         ]
1118     },
1119     
1120     footer : {
1121         tag: 'tfoot',
1122         cn: [
1123         {
1124             tag: 'tr',
1125             cn: [
1126             {
1127                 tag: 'th',
1128                 colspan: '7',
1129                 cls: 'today'
1130             }
1131                     
1132             ]
1133         }
1134         ]
1135     },
1136     
1137     dates : {},
1138
1139     todayText : "Today",
1140     
1141     modes: [
1142     {
1143         clsName: 'days',
1144         navFnc: 'Month',
1145         navStep: 1
1146     },
1147     {
1148         clsName: 'months',
1149         navFnc: 'FullYear',
1150         navStep: 1
1151     },
1152     {
1153         clsName: 'years',
1154         navFnc: 'FullYear',
1155         navStep: 10
1156     }]
1157 });
1158
1159 Roo.apply(Roo.bootstrap.form.DateField,  {
1160   
1161     template : {
1162         tag: 'div',
1163         cls: 'datepicker dropdown-menu roo-dynamic shadow',
1164         cn: [
1165         {
1166             tag: 'div',
1167             cls: 'datepicker-days',
1168             cn: [
1169             {
1170                 tag: 'table',
1171                 cls: 'table-condensed',
1172                 cn:[
1173                 Roo.bootstrap.form.DateField.head,
1174                 {
1175                     tag: 'tbody'
1176                 },
1177                 Roo.bootstrap.form.DateField.footer
1178                 ]
1179             }
1180             ]
1181         },
1182         {
1183             tag: 'div',
1184             cls: 'datepicker-months',
1185             cn: [
1186             {
1187                 tag: 'table',
1188                 cls: 'table-condensed',
1189                 cn:[
1190                 Roo.bootstrap.form.DateField.head,
1191                 Roo.bootstrap.form.DateField.content,
1192                 Roo.bootstrap.form.DateField.footer
1193                 ]
1194             }
1195             ]
1196         },
1197         {
1198             tag: 'div',
1199             cls: 'datepicker-years',
1200             cn: [
1201             {
1202                 tag: 'table',
1203                 cls: 'table-condensed',
1204                 cn:[
1205                 Roo.bootstrap.form.DateField.head,
1206                 Roo.bootstrap.form.DateField.content,
1207                 Roo.bootstrap.form.DateField.footer
1208                 ]
1209             }
1210             ]
1211         }
1212         ]
1213     }
1214 });
1215
1216  
1217
1218