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     parseDate : function(value)
213     {
214         if(!value || value instanceof Date){
215             return value;
216         }
217         var v = Date.parseDate(value, this.format);
218         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
219             v = Date.parseDate(value, 'Y-m-d');
220         }
221         if(!v && this.altFormats){
222             if(!this.altFormatsArray){
223                 this.altFormatsArray = this.altFormats.split("|");
224             }
225             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
226                 v = Date.parseDate(value, this.altFormatsArray[i]);
227             }
228         }
229         return v;
230     },
231     
232     formatDate : function(date, fmt)
233     {   
234         return (!date || !(date instanceof Date)) ?
235         date : date.dateFormat(fmt || this.format);
236     },
237     
238     onFocus : function()
239     {
240         Roo.bootstrap.DateField.superclass.onFocus.call(this);
241         this.show();
242     },
243     
244     onBlur : function()
245     {
246         Roo.bootstrap.DateField.superclass.onBlur.call(this);
247         
248         var d = this.inputEl().getValue();
249         
250         this.setValue(d);
251                 
252         this.hide();
253     },
254     
255     show : function()
256     {
257         this.picker().show();
258         this.update();
259         this.place();
260         
261         this.fireEvent('show', this, this.date);
262     },
263     
264     hide : function()
265     {
266         if(this.isInline) return;
267         this.picker().hide();
268         this.viewMode = this.startViewMode;
269         this.showMode();
270         
271         this.fireEvent('hide', this, this.date);
272         
273     },
274     
275     onMousedown: function(e)
276     {
277         e.stopPropagation();
278         e.preventDefault();
279     },
280     
281     keyup: function(e)
282     {
283         Roo.bootstrap.DateField.superclass.keyup.call(this);
284         this.update();
285     },
286
287     getValue: function()
288     {
289         return this.formatDate(this.date);
290     },
291     
292     fireKey: function(e)
293     {
294         if (!this.picker().isVisible()){
295             if (e.keyCode == 27) // allow escape to hide and re-show picker
296                 this.show();
297             return;
298         }
299         
300         var dateChanged = false,
301         dir, day, month,
302         newDate, newViewDate;
303         
304         switch(e.keyCode){
305             case 27: // escape
306                 this.hide();
307                 e.preventDefault();
308                 break;
309             case 37: // left
310             case 39: // right
311                 if (!this.keyboardNavigation) break;
312                 dir = e.keyCode == 37 ? -1 : 1;
313                 
314                 if (e.ctrlKey){
315                     newDate = this.moveYear(this.date, dir);
316                     newViewDate = this.moveYear(this.viewDate, dir);
317                 } else if (e.shiftKey){
318                     newDate = this.moveMonth(this.date, dir);
319                     newViewDate = this.moveMonth(this.viewDate, dir);
320                 } else {
321                     newDate = new Date(this.date);
322                     newDate.setUTCDate(this.date.getUTCDate() + dir);
323                     newViewDate = new Date(this.viewDate);
324                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
325                 }
326                 if (this.dateWithinRange(newDate)){
327                     this.date = newDate;
328                     this.viewDate = newViewDate;
329                     this.setValue(this.formatDate(this.date));
330 //                    this.update();
331                     e.preventDefault();
332                     dateChanged = true;
333                 }
334                 break;
335             case 38: // up
336             case 40: // down
337                 if (!this.keyboardNavigation) break;
338                 dir = e.keyCode == 38 ? -1 : 1;
339                 if (e.ctrlKey){
340                     newDate = this.moveYear(this.date, dir);
341                     newViewDate = this.moveYear(this.viewDate, dir);
342                 } else if (e.shiftKey){
343                     newDate = this.moveMonth(this.date, dir);
344                     newViewDate = this.moveMonth(this.viewDate, dir);
345                 } else {
346                     newDate = new Date(this.date);
347                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
348                     newViewDate = new Date(this.viewDate);
349                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
350                 }
351                 if (this.dateWithinRange(newDate)){
352                     this.date = newDate;
353                     this.viewDate = newViewDate;
354                     this.setValue(this.formatDate(this.date));
355 //                    this.update();
356                     e.preventDefault();
357                     dateChanged = true;
358                 }
359                 break;
360             case 13: // enter
361                 this.setValue(this.formatDate(this.date));
362                 this.hide();
363                 e.preventDefault();
364                 break;
365             case 9: // tab
366                 this.setValue(this.formatDate(this.date));
367                 this.hide();
368                 break;
369             case 16: // shift
370             case 17: // ctrl
371             case 18: // alt
372                 break;
373             default :
374                 this.hide();
375                 
376         }
377     },
378     
379     setStartDate: function(startDate)
380     {
381         this.startDate = startDate || -Infinity;
382         if (this.startDate !== -Infinity) {
383             this.startDate = this.parseDate(this.startDate);
384         }
385         this.update();
386         this.updateNavArrows();
387     },
388
389     setEndDate: function(endDate)
390     {
391         this.endDate = endDate || Infinity;
392         if (this.endDate !== Infinity) {
393             this.endDate = this.parseDate(this.endDate);
394         }
395         this.update();
396         this.updateNavArrows();
397     },
398     
399     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
400     {
401         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
402         if (typeof(this.daysOfWeekDisabled) !== 'object') {
403             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
404         }
405         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
406             return parseInt(d, 10);
407         });
408         this.update();
409         this.updateNavArrows();
410     },
411     
412     updateNavArrows: function() 
413     {
414         if(this.singleMode){
415             return;
416         }
417         
418         var d = new Date(this.viewDate),
419         year = d.getUTCFullYear(),
420         month = d.getUTCMonth();
421         
422         Roo.each(this.picker().select('.prev', true).elements, function(v){
423             v.show();
424             switch (this.viewMode) {
425                 case 0:
426
427                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
428                         v.hide();
429                     }
430                     break;
431                 case 1:
432                 case 2:
433                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
434                         v.hide();
435                     }
436                     break;
437             }
438         });
439         
440         Roo.each(this.picker().select('.next', true).elements, function(v){
441             v.show();
442             switch (this.viewMode) {
443                 case 0:
444
445                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
446                         v.hide();
447                     }
448                     break;
449                 case 1:
450                 case 2:
451                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
452                         v.hide();
453                     }
454                     break;
455             }
456         })
457     },
458     
459     moveMonth: function(date, dir)
460     {
461         if (!dir) return date;
462         var new_date = new Date(date.valueOf()),
463         day = new_date.getUTCDate(),
464         month = new_date.getUTCMonth(),
465         mag = Math.abs(dir),
466         new_month, test;
467         dir = dir > 0 ? 1 : -1;
468         if (mag == 1){
469             test = dir == -1
470             // If going back one month, make sure month is not current month
471             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
472             ? function(){
473                 return new_date.getUTCMonth() == month;
474             }
475             // If going forward one month, make sure month is as expected
476             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
477             : function(){
478                 return new_date.getUTCMonth() != new_month;
479             };
480             new_month = month + dir;
481             new_date.setUTCMonth(new_month);
482             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
483             if (new_month < 0 || new_month > 11)
484                 new_month = (new_month + 12) % 12;
485         } else {
486             // For magnitudes >1, move one month at a time...
487             for (var i=0; i<mag; i++)
488                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
489                 new_date = this.moveMonth(new_date, dir);
490             // ...then reset the day, keeping it in the new month
491             new_month = new_date.getUTCMonth();
492             new_date.setUTCDate(day);
493             test = function(){
494                 return new_month != new_date.getUTCMonth();
495             };
496         }
497         // Common date-resetting loop -- if date is beyond end of month, make it
498         // end of month
499         while (test()){
500             new_date.setUTCDate(--day);
501             new_date.setUTCMonth(new_month);
502         }
503         return new_date;
504     },
505
506     moveYear: function(date, dir)
507     {
508         return this.moveMonth(date, dir*12);
509     },
510
511     dateWithinRange: function(date)
512     {
513         return date >= this.startDate && date <= this.endDate;
514     },
515
516     
517     remove: function() 
518     {
519         this.picker().remove();
520     }
521    
522 });
523
524 Roo.apply(Roo.bootstrap.DateField,  {
525     
526     head : {
527         tag: 'thead',
528         cn: [
529         {
530             tag: 'tr',
531             cn: [
532             {
533                 tag: 'th',
534                 cls: 'prev',
535                 html: '<i class="fa fa-arrow-left"/>'
536             },
537             {
538                 tag: 'th',
539                 cls: 'switch',
540                 colspan: '5'
541             },
542             {
543                 tag: 'th',
544                 cls: 'next',
545                 html: '<i class="fa fa-arrow-right"/>'
546             }
547
548             ]
549         }
550         ]
551     },
552     
553     content : {
554         tag: 'tbody',
555         cn: [
556         {
557             tag: 'tr',
558             cn: [
559             {
560                 tag: 'td',
561                 colspan: '7'
562             }
563             ]
564         }
565         ]
566     },
567     
568     footer : {
569         tag: 'tfoot',
570         cn: [
571         {
572             tag: 'tr',
573             cn: [
574             {
575                 tag: 'th',
576                 colspan: '7',
577                 cls: 'today'
578             }
579                     
580             ]
581         }
582         ]
583     },
584     
585     dates:{
586         en: {
587             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
588             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
589             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
590             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
591             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
592             today: "Today"
593         }
594     },
595     
596     modes: [
597     {
598         clsName: 'days',
599         navFnc: 'Month',
600         navStep: 1
601     },
602     {
603         clsName: 'months',
604         navFnc: 'FullYear',
605         navStep: 1
606     },
607     {
608         clsName: 'years',
609         navFnc: 'FullYear',
610         navStep: 10
611     }]
612 });
613
614 Roo.apply(Roo.bootstrap.DateField,  {
615   
616     template : {
617         tag: 'div',
618         cls: 'datepicker dropdown-menu roo-dynamic',
619         cn: [
620         {
621             tag: 'div',
622             cls: 'datepicker-days',
623             cn: [
624             {
625                 tag: 'table',
626                 cls: 'table-condensed',
627                 cn:[
628                 Roo.bootstrap.DateField.head,
629                 {
630                     tag: 'tbody'
631                 },
632                 Roo.bootstrap.DateField.footer
633                 ]
634             }
635             ]
636         },
637         {
638             tag: 'div',
639             cls: 'datepicker-months',
640             cn: [
641             {
642                 tag: 'table',
643                 cls: 'table-condensed',
644                 cn:[
645                 Roo.bootstrap.DateField.head,
646                 Roo.bootstrap.DateField.content,
647                 Roo.bootstrap.DateField.footer
648                 ]
649             }
650             ]
651         },
652         {
653             tag: 'div',
654             cls: 'datepicker-years',
655             cn: [
656             {
657                 tag: 'table',
658                 cls: 'table-condensed',
659                 cn:[
660                 Roo.bootstrap.DateField.head,
661                 Roo.bootstrap.DateField.content,
662                 Roo.bootstrap.DateField.footer
663                 ]
664             }
665             ]
666         }
667         ]
668     }
669 });
670
671  
672
673  
674