remove debugging code
[roojs1] / Roo / calendar / View.js
1 /*
2  * - LGPL
3  *
4  * based on jquery fullcalendar
5  * 
6  */
7
8  Roo.calendar = Roo.calendar || {};
9
10
11 /**
12  * @class Roo.calendar.View
13  * @extends Roo.bootstrap.Component
14  * Bootstrap Calendar class
15     
16  * @constructor
17  * Create a new Container
18  * @param {Object} config The config object
19  */
20
21
22
23
24 Roo.calendar.View = function(config){
25    Roo.calendar.View.superclass.constructor.call(this, config);
26      this.addEvents({
27         /**
28              * @event select
29              * Fires when a date is selected
30              * @param {DatePicker} this
31              * @param {Date} date The selected date
32              */
33         'select': true,
34         /**
35              * @event monthchange
36              * Fires when the displayed month changes 
37              * @param {DatePicker} this
38              * @param {Date} date The selected month
39              */
40         'monthchange': true,
41         /**
42              * @event evententer
43              * Fires when mouse over an event
44              * @param {Calendar} this
45              * @param {event} Event
46              */
47         'evententer': true,
48         /**
49              * @event eventleave
50              * Fires when the mouse leaves an
51              * @param {Calendar} this
52              * @param {event}
53              */
54         'eventleave': true,
55         /**
56              * @event eventclick
57              * Fires when the mouse click an
58              * @param {Calendar} this
59              * @param {event}
60              */
61         'eventclick': true
62         
63     });
64
65 };
66
67 Roo.extend(Roo.calendar.View, Roo.BoxComponent,  {
68     
69      /**
70      * @cfg {Number} startDay
71      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
72      */
73     startDay : 0,
74     
75     
76     skipNavHeader : false,
77     skipMonthHeader : false,
78     
79     
80     
81     
82     navHeader: function() {
83         // header is a bit specific to bootstrap implementation - eg. roo would not use this..
84     
85         var fc_button = function(name, corner, style, content ) {
86             return Roo.apply({},{
87                 tag : 'span',
88                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
89                          (corner.length ?
90                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
91                             ''
92                         ),
93                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
94                 unselectable: 'on'
95             });
96         };
97      
98         return  {
99             tag : 'table',
100             cls : 'fc-header',
101             style : 'width:100%',
102             cn : [
103                 {
104                     tag: 'tr',
105                     cn : [
106                         {
107                             tag : 'td',
108                             cls : 'fc-header-left',
109                             cn : [
110                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
111                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
112                                 { tag: 'span', cls: 'fc-header-space' },
113                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
114                                 
115                                 
116                             ]
117                         },
118                         
119                         {
120                             tag : 'td',
121                             cls : 'fc-header-center',
122                             cn : [
123                                 {
124                                     tag: 'span',
125                                     cls: 'fc-header-title',
126                                     cn : {
127                                         tag: 'H2',
128                                         html : 'month / year'
129                                     }
130                                 }
131                                 
132                             ]
133                         },
134                         {
135                             tag : 'td',
136                             cls : 'fc-header-right',
137                             cn : [
138                           //     fc_button('month', 'left', '', 'month' ),
139                             //    fc_button('week', '', '', 'week' ),
140                               //  fc_button('day', 'right', '', 'day' )
141                             //    
142                                 
143                             ]
144                         }
145                         
146                     ]
147                 }
148             ]
149         };
150     },
151     
152     monthHeader : function ()
153     {
154         var ret = [];
155             // fixme - handle this.
156             
157         for (var i =0; i < Date.dayNames.length; i++) {
158             var d = Date.dayNames[i];
159             ret.push({
160                 tag: 'th',
161                 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
162                 html : d.substring(0,3)
163             });
164             
165         }
166         ret[0].cls += ' fc-first';
167         ret[6].cls += ' fc-last';
168         return ret;
169     },
170     
171     monthBody : function()
172     {
173          var cal_cell = function(n) {
174             return  {
175                 tag: 'td',
176                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
177                 cn : [
178                     {
179                         cn : [
180                             {
181                                 cls: 'fc-day-number',
182                                 html: 'D'
183                             },
184                             {
185                                 cls: 'fc-day-content',
186                              
187                                 cn : [
188                                      {
189                                         style: 'position: relative;' // height: 17px;
190                                     }
191                                 ]
192                             }
193                             
194                             
195                         ]
196                     }
197                 ]
198                 
199             }
200         };
201              
202         var ret = []
203         for (var r = 0; r < 6; r++) {
204             var row= {
205                 tag : 'tr',
206                 cls : 'fc-week',
207                 cn : []
208             };
209             
210             for (var i =0; i < Date.dayNames.length; i++) {
211                 var d = Date.dayNames[i];
212                 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
213
214             }
215             row.cn[0].cls+=' fc-first';
216             row.cn[0].cn[0].style = 'min-height:90px';
217             row.cn[6].cls+=' fc-last';
218             ret.push(row);
219             
220         }
221         ret[0].cls += ' fc-first';
222         ret[4].cls += ' fc-prev-last';
223         ret[5].cls += ' fc-last';
224         return ret;
225     
226     },
227     
228     monthTable : function()
229     {
230         var ret = {
231             tag: 'table',
232             cls: 'fc-border-separate',
233             style : 'width:100%',
234             cellspacing  : 0,
235             cn : []
236         }
237         if (!this.skipMonthHeader) {
238            ret.cn.push( { 
239                 tag: 'thead',
240                 cn : [
241                     { 
242                         tag: 'tr',
243                         cls : 'fc-first fc-last',
244                         cn : this.monthHeader()
245                     }
246                 ]
247             });
248         }
249         ret.cn.push( { 
250                     tag: 'tbody',
251                     cn : this.monthBody()
252                 });
253         return ret;
254     },
255     
256     
257     
258       
259     getAutoCreate : function(){
260          
261          
262         var mbody = [];
263         if (!this.skipNavHeader) {
264             mbody.push(this.navHeader());
265         }
266         mbody.push({
267             cls : 'fc-content',
268             style : "position: relative;",
269             cn : [
270                 {
271                     cls : 'fc-view fc-view-month fc-grid',
272                     style : 'position: relative',
273                     unselectable : 'on',
274                     cn : [
275                         {
276                             cls : 'fc-event-container',
277                             style : 'position:absolute;z-index:8;top:0;left:0;'
278                         },
279                         this.monthTable()
280                     ]
281                 }
282             ]
283
284         });
285         return  {
286             cls : 'fc fc-ltr',
287             cn : mbody
288            
289         };
290         
291           
292     },
293     
294     
295     initEvents : function()
296     {
297         if(!this.store){
298             throw "can not find store for calendar";
299         }
300         
301         this.store = Roo.factory(this.store, Roo.data);
302         this.store.on('load', this.onLoad, this);
303         
304         this.resize();
305         
306         this.cells = this.el.select('.fc-day',true);
307         //Roo.log(this.cells);
308         this.textNodes = this.el.query('.fc-day-number');
309         this.cells.addClassOnOver('fc-state-hover');
310         
311         
312         if (!this.skipNavHeader) {
313                 
314             this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
315             this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
316             this.el.select('.fc-button-today',true).on('click', this.showToday, this);
317             this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
318         }
319         this.on('monthchange', this.onMonthChange, this);
320         
321         this.update(new Date().clearTime());
322     },
323     
324     resize : function() {
325         var sz  = this.el.getSize();
326         
327         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
328         this.el.select('.fc-day-content div',true).setHeight(34);
329     },
330     
331     
332     // private
333     showPrevMonth : function(e){
334         this.update(this.activeDate.add("mo", -1));
335     },
336     showToday : function(e){
337         this.update(new Date().clearTime());
338     },
339     // private
340     showNextMonth : function(e){
341         this.update(this.activeDate.add("mo", 1));
342     },
343
344     // private
345     showPrevYear : function(){
346         this.update(this.activeDate.add("y", -1));
347     },
348
349     // private
350     showNextYear : function(){
351         this.update(this.activeDate.add("y", 1));
352     },
353
354     
355    // private
356     update : function(date)
357     {
358         var vd = this.activeDate;
359         this.activeDate = date;
360 //        if(vd && this.el){
361 //            var t = date.getTime();
362 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
363 //                Roo.log('using add remove');
364 //                
365 //                this.fireEvent('monthchange', this, date);
366 //                
367 //                this.cells.removeClass("fc-state-highlight");
368 //                this.cells.each(function(c){
369 //                   if(c.dateValue == t){
370 //                       c.addClass("fc-state-highlight");
371 //                       setTimeout(function(){
372 //                            try{c.dom.firstChild.focus();}catch(e){}
373 //                       }, 50);
374 //                       return false;
375 //                   }
376 //                   return true;
377 //                });
378 //                return;
379 //            }
380 //        }
381         
382         var days = date.getDaysInMonth();
383         
384         var firstOfMonth = date.getFirstDateOfMonth();
385         var startingPos = firstOfMonth.getDay()-this.startDay;
386         
387         if(startingPos < this.startDay){
388             startingPos += 7;
389         }
390         
391         var pm = date.add(Date.MONTH, -1);
392         var prevStart = pm.getDaysInMonth()-startingPos;
393 //        
394         this.cells = this.el.select('.fc-day',true);
395         this.textNodes = this.el.query('.fc-day-number');
396         this.cells.addClassOnOver('fc-state-hover');
397         
398         var cells = this.cells.elements;
399         var textEls = this.textNodes;
400         
401         Roo.each(cells, function(cell){
402             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
403         });
404         
405         days += startingPos;
406
407         // convert everything to numbers so it's fast
408         var day = 86400000;
409         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
410         //Roo.log(d);
411         //Roo.log(pm);
412         //Roo.log(prevStart);
413         
414         var today = new Date().clearTime().getTime();
415         var sel = date.clearTime().getTime();
416         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
417         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
418         var ddMatch = this.disabledDatesRE;
419         var ddText = this.disabledDatesText;
420         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
421         var ddaysText = this.disabledDaysText;
422         var format = this.format;
423         
424         var setCellClass = function(cal, cell){
425             
426             //Roo.log('set Cell Class');
427             cell.title = "";
428             var t = d.getTime();
429             
430             //Roo.log(d);
431             
432             cell.dateValue = t;
433             
434             cell.className += " fc-cell-" + t;
435             
436             if(t == today){
437                 cell.className += " fc-today";
438                 cell.className += " fc-state-highlight";
439                 cell.title = cal.todayText;
440             }
441             if(t == sel){
442                 // disable highlight in other month..
443                 //cell.className += " fc-state-highlight";
444                 
445             }
446             // disabling
447             if(t < min) {
448                 cell.className = " fc-state-disabled";
449                 cell.title = cal.minText;
450                 return;
451             }
452             if(t > max) {
453                 cell.className = " fc-state-disabled";
454                 cell.title = cal.maxText;
455                 return;
456             }
457             if(ddays){
458                 if(ddays.indexOf(d.getDay()) != -1){
459                     cell.title = ddaysText;
460                     cell.className = " fc-state-disabled";
461                 }
462             }
463             if(ddMatch && format){
464                 var fvalue = d.dateFormat(format);
465                 if(ddMatch.test(fvalue)){
466                     cell.title = ddText.replace("%0", fvalue);
467                     cell.className = " fc-state-disabled";
468                 }
469             }
470             
471             if (!cell.initialClassName) {
472                 cell.initialClassName = cell.dom.className;
473             }
474             
475             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
476         };
477
478         var i = 0;
479         
480         for(; i < startingPos; i++) {
481             textEls[i].innerHTML = (++prevStart);
482             d.setDate(d.getDate()+1);
483             
484             cells[i].className = "fc-past fc-other-month";
485             setCellClass(this, cells[i]);
486         }
487         
488         var intDay = 0;
489         
490         for(; i < days; i++){
491             intDay = i - startingPos + 1;
492             textEls[i].innerHTML = (intDay);
493             d.setDate(d.getDate()+1);
494             
495             cells[i].className = ''; // "x-date-active";
496             setCellClass(this, cells[i]);
497         }
498         var extraDays = 0;
499         
500         for(; i < 42; i++) {
501             textEls[i].innerHTML = (++extraDays);
502             d.setDate(d.getDate()+1);
503             
504             cells[i].className = "fc-future fc-other-month";
505             setCellClass(this, cells[i]);
506         }
507         
508         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
509         
510         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
511         
512         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
513         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
514         
515         if(totalRows != 6){
516             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
517             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
518         }
519         
520         this.fireEvent('monthchange', this, date);
521         
522         
523         /*
524         if(!this.internalRender){
525             var main = this.el.dom.firstChild;
526             var w = main.offsetWidth;
527             this.el.setWidth(w + this.el.getBorderWidth("lr"));
528             Roo.fly(main).setWidth(w);
529             this.internalRender = true;
530             // opera does not respect the auto grow header center column
531             // then, after it gets a width opera refuses to recalculate
532             // without a second pass
533             if(Roo.isOpera && !this.secondPass){
534                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
535                 this.secondPass = true;
536                 this.update.defer(10, this, [date]);
537             }
538         }
539         */
540         
541     },
542     
543     findCell : function(dt) {
544         dt = dt.clearTime().getTime();
545         var ret = false;
546         this.cells.each(function(c){
547             //Roo.log("check " +c.dateValue + '?=' + dt);
548             if(c.dateValue == dt){
549                 ret = c;
550                 return false;
551             }
552             return true;
553         });
554         
555         return ret;
556     },
557     
558     findCells : function(ev) {
559         var s = ev.start_dt.clone().clearTime().getTime();
560        // Roo.log(s);
561         var e= ev.end_dt.clone().clearTime().getTime();
562        // Roo.log(e);
563         var ret = [];
564         this.cells.each(function(c){
565              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
566             
567             if(c.dateValue > e){
568                 return ;
569             }
570             if(c.dateValue < s){
571                 return ;
572             }
573             ret.push(c);
574         });
575         
576         return ret;    
577     },
578     
579     findBestRow: function(cells)
580     {
581         var ret = 0;
582         
583         for (var i =0 ; i < cells.length;i++) {
584             ret  = Math.max(cells[i].rows || 0,ret);
585         }
586         return ret;
587         
588     },
589     
590     
591     addItem : function(ev)
592     {
593         // look for vertical location slot in
594         var cells = this.findCells(ev);
595         
596         ev.row = this.findBestRow(cells);
597         
598         // work out the location.
599         
600         var crow = false;
601         var rows = [];
602         for(var i =0; i < cells.length; i++) {
603             if (!crow) {
604                 crow = {
605                     start : cells[i],
606                     end :  cells[i]
607                 };
608                 continue;
609             }
610             if (crow.start.getY() == cells[i].getY()) {
611                 // on same row.
612                 crow.end = cells[i];
613                 continue;
614             }
615             // different row.
616             rows.push(crow);
617             crow = {
618                 start: cells[i],
619                 end : cells[i]
620             };
621             
622         }
623         
624         rows.push(crow);
625         ev.els = [];
626         ev.rows = rows;
627         ev.cells = cells;
628         for (var i = 0; i < cells.length;i++) {
629             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
630             
631         }
632         
633         this.calevents.push(ev);
634     },
635     
636     clearEvents: function() {
637         
638         if(!this.calevents){
639             return;
640         }
641         
642         Roo.each(this.cells.elements, function(c){
643             c.rows = 0;
644         });
645         
646         Roo.each(this.calevents, function(e) {
647             Roo.each(e.els, function(el) {
648                 el.un('mouseenter' ,this.onEventEnter, this);
649                 el.un('mouseleave' ,this.onEventLeave, this);
650                 el.remove();
651             },this);
652         },this);
653         
654     },
655     
656     renderEvents: function()
657     {   
658         // first make sure there is enough space..
659         
660         this.cells.each(function(c) {
661 //            c.select('.fc-day-content div',true).first().setHeight(20);
662             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
663         });
664         
665         for (var e = 0; e < this.calevents.length; e++) {
666             var ev = this.calevents[e];
667             var cells = ev.cells;
668             var rows = ev.rows;
669             
670             for(var i =0; i < rows.length; i++) {
671                 
672                 var startCell = Roo.select('.fc-cell-' + rows[i].start.dateValue + ' .fc-day-content div', true).first();
673                 
674                 // how many rows should it span..
675                 
676                 var  cfg = {
677                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
678                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
679                     
680                     unselectable : "on",
681                     cn : [
682                         {
683                             cls: 'fc-event-inner',
684                             cn : [
685                                 {
686                                   tag:'span',
687                                   cls: 'fc-event-time',
688                                   html : cells.length > 1 ? '' : ev.start_dt.format('h:ia')
689                                 },
690                                 {
691                                   tag:'span',
692                                   cls: 'fc-event-title',
693                                   html : String.format('{0}', ev.title)
694                                 }
695                                 
696                                 
697                             ]
698                         },
699                         {
700                             cls: 'ui-resizable-handle ui-resizable-e',
701                             html : '&nbsp;&nbsp;&nbsp'
702                         }
703                         
704                     ]
705                 };
706                 if (i == 0) {
707                     cfg.cls += ' fc-event-start';
708                 }
709                 
710                 if ((i+1) == rows.length) {
711                     cfg.cls += ' fc-event-end';
712                 }
713                 
714 //                var ctr = this.el.select('.fc-event-container',true).first();
715                 var cg = startCell.createChild(cfg);
716                 
717                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
718                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
719                 cg.on('click', this.onEventClick, this, ev);
720                 
721                 ev.els.push(cg);
722 //                
723                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
724                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
725 //                //Roo.log(cg);
726                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);   
727 //                Roo.log(rows[i]);
728                 var boxes = Math.ceil((rows[i].end.dateValue - rows[i].start.dateValue) / 86400000) + 1;
729 //                cg.setWidth(ebox.right - sbox.x -2);
730 //                Roo.log(boxes);
731                 cg.setWidth(134 * boxes);
732 //                cg.setWidth(134);
733                 
734             }
735             
736             
737         }
738         
739     },
740     
741     onEventEnter: function (e, el,event,d) {
742         this.fireEvent('evententer', this, el, event);
743     },
744     
745     onEventLeave: function (e, el,event,d) {
746         this.fireEvent('eventleave', this, el, event);
747     },
748     
749     onEventClick: function (e, el,event,d) {
750         this.fireEvent('eventclick', this, el, event);
751     },
752     
753     onMonthChange: function () {
754         this.store.load();
755     },
756     
757     onLoad: function () {
758         
759         this.clearEvents();
760         //Roo.log('calendar onload');
761 //        
762         this.calevents = [];
763          
764         if(this.store.getCount() > 0){
765             this.store.data.each(function(d){
766                 
767                 
768                 // FIXME..
769                 var add = Roo.apply({}, d.data);
770                 if (typeof(add.end_dt) == 'undefined')  {
771                     Roo.log("Missing End time in calendar data: ");
772                     Roo.log(d);
773                     return;
774                 }
775                 if (typeof(add.start_dt) == 'undefined')  {
776                     Roo.log("Missing Start time in calendar data: ");
777                     Roo.log(d);
778                     return;
779                 }
780                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
781                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
782                 add.id = add.id || d.id;
783                 add.title = add.title || '??';
784                 this.addItem(add);
785                 
786                 // other than the 'required' coluns, we should just pass the data from the store.
787                 
788                 
789                /*cal.addItem({
790                     id : d.data.id,
791                     start: new Date(d.data.start_dt),
792                     end : new Date(d.data.end_dt),
793                     time : d.data.start_time,
794                     title : d.data.title,
795                     description : d.data.description,
796                     venue : d.data.venue
797                 });*/
798             },this);
799         }
800         
801         this.renderEvents();
802     }
803 });
804
805  
806