Roo/bootstrap/MonthField.js
[roojs1] / Roo / bootstrap / MonthField.js
1 /*
2  * - LGPL
3  *
4  * MonthField
5  * 
6  */
7
8 /**
9  * @class Roo.bootstrap.MonthField
10  * @extends Roo.bootstrap.Input
11  * Bootstrap MonthField class
12  * 
13  * @cfg {String} language default en
14  * 
15  * @constructor
16  * Create a new MonthField
17  * @param {Object} config The config object
18  */
19
20 Roo.bootstrap.MonthField = function(config){
21     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22     
23     this.addEvents({
24         /**
25          * @event show
26          * Fires when this field show.
27          * @param {Roo.bootstrap.MonthField} this
28          * @param {Mixed} date The date value
29          */
30         show : true,
31         /**
32          * @event show
33          * Fires when this field hide.
34          * @param {Roo.bootstrap.MonthField} this
35          * @param {Mixed} date The date value
36          */
37         hide : true,
38         /**
39          * @event select
40          * Fires when select a date.
41          * @param {Roo.bootstrap.MonthField} this
42          * @param {Mixed} date The date value
43          */
44         select : true
45     });
46 };
47
48 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
49     
50     format : "F",
51     
52     onRender: function(ct, position)
53     {
54         
55         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
56         
57         this.language = this.language || 'en';
58         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
59         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
60         
61         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
62         this.format = this.format || 'F';
63         this.isInline = false;
64         this.isInput = true;
65         this.component = this.el.select('.add-on', true).first() || false;
66         this.component = (this.component && this.component.length === 0) ? false : this.component;
67         this.hasInput = this.component && this.inputEL().length;
68         
69         this.minViewMode = 1;
70         this.viewMode = 1;
71                 
72         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
73         
74         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
75         
76         this.picker().on('mousedown', this.onMousedown, this);
77         this.picker().on('click', this.onClick, this);
78         
79         this.picker().addClass('datepicker-dropdown');
80         
81         this.startViewMode = this.viewMode;
82
83         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
84             v.setStyle('width', '189px');
85         });
86         
87         this.fillMonths();
88         this.update();
89         
90         if(this.isInline) {
91             this.show();
92         }
93     },
94     
95     setValue: function(v)
96     {
97         
98         var d = new Date(this.parseDate(v) ).clearTime();
99         
100         if(isNaN(d.getTime())){
101             this.date = this.viewDate = '';
102             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
103             return;
104         }
105         
106         v = this.formatDate(d);
107         
108         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
109         
110         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
111      
112         this.update();
113
114         this.fireEvent('select', this, this.date);
115         
116     },
117     
118     onClick: function(e) 
119     {
120         e.stopPropagation();
121         e.preventDefault();
122         
123         var target = e.getTarget();
124         
125         if(target.nodeName.toLowerCase() === 'i'){
126             target = Roo.get(target).dom.parentNode;
127         }
128         
129         var nodeName = target.nodeName;
130         var className = target.className;
131         var html = target.innerHTML;
132         
133         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
134             return;
135         }
136         this.viewDate.setUTCDate(1);
137         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
138         
139         this.setValue(this.formatDate(this.viewDate));
140         this.hide();
141                         
142     },
143     
144     picker : function()
145     {
146         return this.pickerEl;
147 //        return this.el.select('.datepicker', true).first();
148     },
149     
150     fillMonths: function()
151     {    
152         var i = 0
153         var months = this.picker().select('>.datepicker-months td', true).first();
154         
155         months.dom.innerHTML = '';
156         
157         while (i < 12) {
158             var month = {
159                 tag: 'span',
160                 cls: 'month',
161                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
162             }
163             
164             months.createChild(month);
165         }
166         
167     },
168     
169     update: function()
170     {
171         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;
172         
173         if (this.date < this.startDate) {
174             this.viewDate = new Date(this.startDate);
175         } else if (this.date > this.endDate) {
176             this.viewDate = new Date(this.endDate);
177         } else {
178             this.viewDate = new Date(this.date);
179         }
180         
181         this.fill();
182     },
183     
184     fill: function() 
185     {
186         this.fillMonths();
187     },
188     
189     place: function()
190     {
191         if(this.isInline) return;
192         
193         this.picker().removeClass(['bottom', 'top']);
194         
195         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
196             /*
197              * place to the top of element!
198              *
199              */
200             
201             this.picker().addClass('top');
202             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
203             
204             return;
205         }
206         
207         this.picker().addClass('bottom');
208         
209         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
210     },
211     
212     onFocus : function()
213     {
214         Roo.bootstrap.DateField.superclass.onFocus.call(this);
215         this.show();
216     },
217     
218     onBlur : function()
219     {
220         Roo.bootstrap.DateField.superclass.onBlur.call(this);
221         
222         var d = this.inputEl().getValue();
223         
224         this.setValue(d);
225                 
226         this.hide();
227     },
228     
229     show : function()
230     {
231         this.picker().show();
232         this.update();
233         this.place();
234         
235         this.fireEvent('show', this, this.date);
236     },
237     
238     hide : function()
239     {
240         if(this.isInline) return;
241         this.picker().hide();
242         this.fireEvent('hide', this, this.date);
243         
244     },
245     
246     onMousedown: function(e)
247     {
248         e.stopPropagation();
249         e.preventDefault();
250     },
251     
252     keyup: function(e)
253     {
254         Roo.bootstrap.DateField.superclass.keyup.call(this);
255         this.update();
256     },
257
258     getValue: function()
259     {
260         return this.formatDate(this.date);
261     },
262     
263     fireKey: function(e)
264     {
265         if (!this.picker().isVisible()){
266             if (e.keyCode == 27) // allow escape to hide and re-show picker
267                 this.show();
268             return;
269         }
270         
271         var dateChanged = false,
272         dir, day, month,
273         newDate, newViewDate;
274         
275         switch(e.keyCode){
276             case 27: // escape
277                 this.hide();
278                 e.preventDefault();
279                 break;
280             case 37: // left
281             case 39: // right
282                 if (!this.keyboardNavigation) break;
283                 dir = e.keyCode == 37 ? -1 : 1;
284                 
285                 if (e.ctrlKey){
286                     newDate = this.moveYear(this.date, dir);
287                     newViewDate = this.moveYear(this.viewDate, dir);
288                 } else if (e.shiftKey){
289                     newDate = this.moveMonth(this.date, dir);
290                     newViewDate = this.moveMonth(this.viewDate, dir);
291                 } else {
292                     newDate = new Date(this.date);
293                     newDate.setUTCDate(this.date.getUTCDate() + dir);
294                     newViewDate = new Date(this.viewDate);
295                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
296                 }
297                 if (this.dateWithinRange(newDate)){
298                     this.date = newDate;
299                     this.viewDate = newViewDate;
300                     this.setValue(this.formatDate(this.date));
301 //                    this.update();
302                     e.preventDefault();
303                     dateChanged = true;
304                 }
305                 break;
306             case 38: // up
307             case 40: // down
308                 if (!this.keyboardNavigation) break;
309                 dir = e.keyCode == 38 ? -1 : 1;
310                 if (e.ctrlKey){
311                     newDate = this.moveYear(this.date, dir);
312                     newViewDate = this.moveYear(this.viewDate, dir);
313                 } else if (e.shiftKey){
314                     newDate = this.moveMonth(this.date, dir);
315                     newViewDate = this.moveMonth(this.viewDate, dir);
316                 } else {
317                     newDate = new Date(this.date);
318                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
319                     newViewDate = new Date(this.viewDate);
320                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
321                 }
322                 if (this.dateWithinRange(newDate)){
323                     this.date = newDate;
324                     this.viewDate = newViewDate;
325                     this.setValue(this.formatDate(this.date));
326 //                    this.update();
327                     e.preventDefault();
328                     dateChanged = true;
329                 }
330                 break;
331             case 13: // enter
332                 this.setValue(this.formatDate(this.date));
333                 this.hide();
334                 e.preventDefault();
335                 break;
336             case 9: // tab
337                 this.setValue(this.formatDate(this.date));
338                 this.hide();
339                 break;
340             case 16: // shift
341             case 17: // ctrl
342             case 18: // alt
343                 break;
344             default :
345                 this.hide();
346                 
347         }
348     },
349     
350     setStartDate: function(startDate)
351     {
352         this.startDate = startDate || -Infinity;
353         if (this.startDate !== -Infinity) {
354             this.startDate = this.parseDate(this.startDate);
355         }
356         this.update();
357         this.updateNavArrows();
358     },
359
360     setEndDate: function(endDate)
361     {
362         this.endDate = endDate || Infinity;
363         if (this.endDate !== Infinity) {
364             this.endDate = this.parseDate(this.endDate);
365         }
366         this.update();
367         this.updateNavArrows();
368     },
369     
370     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
371     {
372         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
373         if (typeof(this.daysOfWeekDisabled) !== 'object') {
374             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
375         }
376         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
377             return parseInt(d, 10);
378         });
379         this.update();
380         this.updateNavArrows();
381     },
382     
383     updateNavArrows: function() 
384     {
385         if(this.singleMode){
386             return;
387         }
388         
389         var d = new Date(this.viewDate),
390         year = d.getUTCFullYear(),
391         month = d.getUTCMonth();
392         
393         Roo.each(this.picker().select('.prev', true).elements, function(v){
394             v.show();
395             switch (this.viewMode) {
396                 case 0:
397
398                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
399                         v.hide();
400                     }
401                     break;
402                 case 1:
403                 case 2:
404                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
405                         v.hide();
406                     }
407                     break;
408             }
409         });
410         
411         Roo.each(this.picker().select('.next', true).elements, function(v){
412             v.show();
413             switch (this.viewMode) {
414                 case 0:
415
416                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
417                         v.hide();
418                     }
419                     break;
420                 case 1:
421                 case 2:
422                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
423                         v.hide();
424                     }
425                     break;
426             }
427         })
428     },
429     
430     moveMonth: function(date, dir)
431     {
432         if (!dir) return date;
433         var new_date = new Date(date.valueOf()),
434         day = new_date.getUTCDate(),
435         month = new_date.getUTCMonth(),
436         mag = Math.abs(dir),
437         new_month, test;
438         dir = dir > 0 ? 1 : -1;
439         if (mag == 1){
440             test = dir == -1
441             // If going back one month, make sure month is not current month
442             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
443             ? function(){
444                 return new_date.getUTCMonth() == month;
445             }
446             // If going forward one month, make sure month is as expected
447             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
448             : function(){
449                 return new_date.getUTCMonth() != new_month;
450             };
451             new_month = month + dir;
452             new_date.setUTCMonth(new_month);
453             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
454             if (new_month < 0 || new_month > 11)
455                 new_month = (new_month + 12) % 12;
456         } else {
457             // For magnitudes >1, move one month at a time...
458             for (var i=0; i<mag; i++)
459                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
460                 new_date = this.moveMonth(new_date, dir);
461             // ...then reset the day, keeping it in the new month
462             new_month = new_date.getUTCMonth();
463             new_date.setUTCDate(day);
464             test = function(){
465                 return new_month != new_date.getUTCMonth();
466             };
467         }
468         // Common date-resetting loop -- if date is beyond end of month, make it
469         // end of month
470         while (test()){
471             new_date.setUTCDate(--day);
472             new_date.setUTCMonth(new_month);
473         }
474         return new_date;
475     },
476
477     moveYear: function(date, dir)
478     {
479         return this.moveMonth(date, dir*12);
480     },
481
482     dateWithinRange: function(date)
483     {
484         return date >= this.startDate && date <= this.endDate;
485     },
486
487     
488     remove: function() 
489     {
490         this.picker().remove();
491     }
492    
493 });
494
495 Roo.apply(Roo.bootstrap.DateField,  {
496     
497     head : {
498         tag: 'thead',
499         cn: [
500         {
501             tag: 'tr',
502             cn: [
503             {
504                 tag: 'th',
505                 cls: 'prev',
506                 html: '<i class="fa fa-arrow-left"/>'
507             },
508             {
509                 tag: 'th',
510                 cls: 'switch',
511                 colspan: '5'
512             },
513             {
514                 tag: 'th',
515                 cls: 'next',
516                 html: '<i class="fa fa-arrow-right"/>'
517             }
518
519             ]
520         }
521         ]
522     },
523     
524     content : {
525         tag: 'tbody',
526         cn: [
527         {
528             tag: 'tr',
529             cn: [
530             {
531                 tag: 'td',
532                 colspan: '7'
533             }
534             ]
535         }
536         ]
537     },
538     
539     footer : {
540         tag: 'tfoot',
541         cn: [
542         {
543             tag: 'tr',
544             cn: [
545             {
546                 tag: 'th',
547                 colspan: '7',
548                 cls: 'today'
549             }
550                     
551             ]
552         }
553         ]
554     },
555     
556     dates:{
557         en: {
558             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
559             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
560             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
561             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
562             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
563             today: "Today"
564         }
565     },
566     
567     modes: [
568     {
569         clsName: 'days',
570         navFnc: 'Month',
571         navStep: 1
572     },
573     {
574         clsName: 'months',
575         navFnc: 'FullYear',
576         navStep: 1
577     },
578     {
579         clsName: 'years',
580         navFnc: 'FullYear',
581         navStep: 10
582     }]
583 });
584
585 Roo.apply(Roo.bootstrap.DateField,  {
586   
587     template : {
588         tag: 'div',
589         cls: 'datepicker dropdown-menu roo-dynamic',
590         cn: [
591         {
592             tag: 'div',
593             cls: 'datepicker-days',
594             cn: [
595             {
596                 tag: 'table',
597                 cls: 'table-condensed',
598                 cn:[
599                 Roo.bootstrap.DateField.head,
600                 {
601                     tag: 'tbody'
602                 },
603                 Roo.bootstrap.DateField.footer
604                 ]
605             }
606             ]
607         },
608         {
609             tag: 'div',
610             cls: 'datepicker-months',
611             cn: [
612             {
613                 tag: 'table',
614                 cls: 'table-condensed',
615                 cn:[
616                 Roo.bootstrap.DateField.head,
617                 Roo.bootstrap.DateField.content,
618                 Roo.bootstrap.DateField.footer
619                 ]
620             }
621             ]
622         },
623         {
624             tag: 'div',
625             cls: 'datepicker-years',
626             cn: [
627             {
628                 tag: 'table',
629                 cls: 'table-condensed',
630                 cn:[
631                 Roo.bootstrap.DateField.head,
632                 Roo.bootstrap.DateField.content,
633                 Roo.bootstrap.DateField.footer
634                 ]
635             }
636             ]
637         }
638         ]
639     }
640 });
641
642  
643
644  
645