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