Roo/bootstrap/Component.js
[roojs1] / roojs-calendar-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160     /**
161      * This is really a wrapper for addxtypeChild
162      * it handles stuff relating to flexy:foreach / flexy:if
163      * = some of our projects use a flat rendering of the output, and try and overlay it with dynamic data.
164      *  -- this is a bit of a nightmare... and is even more confusing to debug..
165      *
166      *  
167      *
168      */
169     addxtype  : function(tree,cntr)
170     {
171         var cn = this;
172         
173         cn = Roo.factory(tree);
174         //Roo.log(['addxtype', cn]);
175            
176         cn.parentType = this.xtype; //??
177         cn.parentId = this.id;
178         
179         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
180         if (typeof(cn.container_method) == 'string') {
181             cntr = cn.container_method;
182         }
183         
184         
185         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
186         
187         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
188         
189         var build_from_html =  Roo.XComponent.build_from_html;
190           
191         var is_body  = (tree.xtype == 'Body') ;
192           
193         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
194           
195         var self_cntr_el = Roo.get(this[cntr](false));
196         
197         // do not try and build conditional elements 
198         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
199             return false;
200         }
201         
202         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
203             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
204                 return this.addxtypeChild(tree,cntr, is_body);
205             }
206             
207             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
208                 
209             if(echild){
210                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
211             }
212             
213             Roo.log('skipping render');
214             return cn;
215             
216         }
217         
218         var ret = false;
219         if (!build_from_html) {
220             return false;
221         }
222         
223         // this i think handles overlaying multiple children of the same type
224         // with the sam eelement.. - which might be buggy..
225         while (true) {
226             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
227             
228             if (!echild) {
229                 break;
230             }
231             
232             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
233                 break;
234             }
235             
236             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
237         }
238        
239         return ret;
240     },
241     /**
242      * add a child to this element
243      *   - turn the child.cfg into a child_instance
244      *   - call child_instance.render( this { getContainerMethod()} )
245      *   - loop through the children, and call addxtype.. (reall this) on newly created child.
246      *  
247      */
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * based on jquery fullcalendar
442  * 
443  */
444
445 Roo.bootstrap = Roo.bootstrap || {};
446 /**
447  * @class Roo.bootstrap.Calendar
448  * @extends Roo.bootstrap.Component
449  * Bootstrap Calendar class
450  * @cfg {Boolean} loadMask (true|false) default false
451  * @cfg {Object} header generate the user specific header of the calendar, default false
452
453  * @constructor
454  * Create a new Container
455  * @param {Object} config The config object
456  */
457
458
459
460 Roo.bootstrap.Calendar = function(config){
461     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
462      this.addEvents({
463         /**
464              * @event select
465              * Fires when a date is selected
466              * @param {DatePicker} this
467              * @param {Date} date The selected date
468              */
469         'select': true,
470         /**
471              * @event monthchange
472              * Fires when the displayed month changes 
473              * @param {DatePicker} this
474              * @param {Date} date The selected month
475              */
476         'monthchange': true,
477         /**
478              * @event evententer
479              * Fires when mouse over an event
480              * @param {Calendar} this
481              * @param {event} Event
482              */
483         'evententer': true,
484         /**
485              * @event eventleave
486              * Fires when the mouse leaves an
487              * @param {Calendar} this
488              * @param {event}
489              */
490         'eventleave': true,
491         /**
492              * @event eventclick
493              * Fires when the mouse click an
494              * @param {Calendar} this
495              * @param {event}
496              */
497         'eventclick': true
498         
499     });
500
501 };
502
503 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
504     
505      /**
506      * @cfg {Number} startDay
507      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
508      */
509     startDay : 0,
510     
511     loadMask : false,
512     
513     header : false,
514       
515     getAutoCreate : function(){
516         
517         
518         var fc_button = function(name, corner, style, content ) {
519             return Roo.apply({},{
520                 tag : 'span',
521                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
522                          (corner.length ?
523                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
524                             ''
525                         ),
526                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
527                 unselectable: 'on'
528             });
529         };
530         
531         var header = {};
532         
533         if(!this.header){
534             header = {
535                 tag : 'table',
536                 cls : 'fc-header',
537                 style : 'width:100%',
538                 cn : [
539                     {
540                         tag: 'tr',
541                         cn : [
542                             {
543                                 tag : 'td',
544                                 cls : 'fc-header-left',
545                                 cn : [
546                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
547                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
548                                     { tag: 'span', cls: 'fc-header-space' },
549                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
550
551
552                                 ]
553                             },
554
555                             {
556                                 tag : 'td',
557                                 cls : 'fc-header-center',
558                                 cn : [
559                                     {
560                                         tag: 'span',
561                                         cls: 'fc-header-title',
562                                         cn : {
563                                             tag: 'H2',
564                                             html : 'month / year'
565                                         }
566                                     }
567
568                                 ]
569                             },
570                             {
571                                 tag : 'td',
572                                 cls : 'fc-header-right',
573                                 cn : [
574                               /*      fc_button('month', 'left', '', 'month' ),
575                                     fc_button('week', '', '', 'week' ),
576                                     fc_button('day', 'right', '', 'day' )
577                                 */    
578
579                                 ]
580                             }
581
582                         ]
583                     }
584                 ]
585             };
586         }
587         
588         header = this.header;
589         
590        
591         var cal_heads = function() {
592             var ret = [];
593             // fixme - handle this.
594             
595             for (var i =0; i < Date.dayNames.length; i++) {
596                 var d = Date.dayNames[i];
597                 ret.push({
598                     tag: 'th',
599                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
600                     html : d.substring(0,3)
601                 });
602                 
603             }
604             ret[0].cls += ' fc-first';
605             ret[6].cls += ' fc-last';
606             return ret;
607         };
608         var cal_cell = function(n) {
609             return  {
610                 tag: 'td',
611                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
612                 cn : [
613                     {
614                         cn : [
615                             {
616                                 cls: 'fc-day-number',
617                                 html: 'D'
618                             },
619                             {
620                                 cls: 'fc-day-content',
621                              
622                                 cn : [
623                                      {
624                                         style: 'position: relative;' // height: 17px;
625                                     }
626                                 ]
627                             }
628                             
629                             
630                         ]
631                     }
632                 ]
633                 
634             }
635         };
636         var cal_rows = function() {
637             
638             var ret = [];
639             for (var r = 0; r < 6; r++) {
640                 var row= {
641                     tag : 'tr',
642                     cls : 'fc-week',
643                     cn : []
644                 };
645                 
646                 for (var i =0; i < Date.dayNames.length; i++) {
647                     var d = Date.dayNames[i];
648                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
649
650                 }
651                 row.cn[0].cls+=' fc-first';
652                 row.cn[0].cn[0].style = 'min-height:90px';
653                 row.cn[6].cls+=' fc-last';
654                 ret.push(row);
655                 
656             }
657             ret[0].cls += ' fc-first';
658             ret[4].cls += ' fc-prev-last';
659             ret[5].cls += ' fc-last';
660             return ret;
661             
662         };
663         
664         var cal_table = {
665             tag: 'table',
666             cls: 'fc-border-separate',
667             style : 'width:100%',
668             cellspacing  : 0,
669             cn : [
670                 { 
671                     tag: 'thead',
672                     cn : [
673                         { 
674                             tag: 'tr',
675                             cls : 'fc-first fc-last',
676                             cn : cal_heads()
677                         }
678                     ]
679                 },
680                 { 
681                     tag: 'tbody',
682                     cn : cal_rows()
683                 }
684                   
685             ]
686         };
687          
688          var cfg = {
689             cls : 'fc fc-ltr',
690             cn : [
691                 header,
692                 {
693                     cls : 'fc-content',
694                     style : "position: relative;",
695                     cn : [
696                         {
697                             cls : 'fc-view fc-view-month fc-grid',
698                             style : 'position: relative',
699                             unselectable : 'on',
700                             cn : [
701                                 {
702                                     cls : 'fc-event-container',
703                                     style : 'position:absolute;z-index:8;top:0;left:0;'
704                                 },
705                                 cal_table
706                             ]
707                         }
708                     ]
709     
710                 }
711            ] 
712             
713         };
714         
715          
716         
717         return cfg;
718     },
719     
720     
721     initEvents : function()
722     {
723         if(!this.store){
724             throw "can not find store for calendar";
725         }
726         
727         var mark = {
728             tag: "div",
729             cls:"x-dlg-mask",
730             style: "text-align:center",
731             cn: [
732                 {
733                     tag: "div",
734                     style: "background-color:white;width:50%;margin:250 auto",
735                     cn: [
736                         {
737                             tag: "img",
738                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
739                         },
740                         {
741                             tag: "span",
742                             html: "Loading"
743                         }
744                         
745                     ]
746                 }
747             ]
748         };
749         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
750         
751         var size = this.el.select('.fc-content', true).first().getSize();
752         this.maskEl.setSize(size.width, size.height);
753         this.maskEl.enableDisplayMode("block");
754         if(!this.loadMask){
755             this.maskEl.hide();
756         }
757         
758         this.store = Roo.factory(this.store, Roo.data);
759         this.store.on('load', this.onLoad, this);
760         this.store.on('beforeload', this.onBeforeLoad, this);
761         
762         this.resize();
763         
764         this.cells = this.el.select('.fc-day',true);
765         //Roo.log(this.cells);
766         this.textNodes = this.el.query('.fc-day-number');
767         this.cells.addClassOnOver('fc-state-hover');
768         
769         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
770         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
771         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
772         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
773         
774         this.on('monthchange', this.onMonthChange, this);
775         
776         this.update(new Date().clearTime());
777     },
778     
779     resize : function() {
780         var sz  = this.el.getSize();
781         
782         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
783         this.el.select('.fc-day-content div',true).setHeight(34);
784     },
785     
786     
787     // private
788     showPrevMonth : function(e){
789         this.update(this.activeDate.add("mo", -1));
790     },
791     showToday : function(e){
792         this.update(new Date().clearTime());
793     },
794     // private
795     showNextMonth : function(e){
796         this.update(this.activeDate.add("mo", 1));
797     },
798
799     // private
800     showPrevYear : function(){
801         this.update(this.activeDate.add("y", -1));
802     },
803
804     // private
805     showNextYear : function(){
806         this.update(this.activeDate.add("y", 1));
807     },
808
809     
810    // private
811     update : function(date)
812     {
813         var vd = this.activeDate;
814         this.activeDate = date;
815 //        if(vd && this.el){
816 //            var t = date.getTime();
817 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
818 //                Roo.log('using add remove');
819 //                
820 //                this.fireEvent('monthchange', this, date);
821 //                
822 //                this.cells.removeClass("fc-state-highlight");
823 //                this.cells.each(function(c){
824 //                   if(c.dateValue == t){
825 //                       c.addClass("fc-state-highlight");
826 //                       setTimeout(function(){
827 //                            try{c.dom.firstChild.focus();}catch(e){}
828 //                       }, 50);
829 //                       return false;
830 //                   }
831 //                   return true;
832 //                });
833 //                return;
834 //            }
835 //        }
836         
837         var days = date.getDaysInMonth();
838         
839         var firstOfMonth = date.getFirstDateOfMonth();
840         var startingPos = firstOfMonth.getDay()-this.startDay;
841         
842         if(startingPos < this.startDay){
843             startingPos += 7;
844         }
845         
846         var pm = date.add(Date.MONTH, -1);
847         var prevStart = pm.getDaysInMonth()-startingPos;
848 //        
849         this.cells = this.el.select('.fc-day',true);
850         this.textNodes = this.el.query('.fc-day-number');
851         this.cells.addClassOnOver('fc-state-hover');
852         
853         var cells = this.cells.elements;
854         var textEls = this.textNodes;
855         
856         Roo.each(cells, function(cell){
857             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
858         });
859         
860         days += startingPos;
861
862         // convert everything to numbers so it's fast
863         var day = 86400000;
864         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
865         //Roo.log(d);
866         //Roo.log(pm);
867         //Roo.log(prevStart);
868         
869         var today = new Date().clearTime().getTime();
870         var sel = date.clearTime().getTime();
871         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
872         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
873         var ddMatch = this.disabledDatesRE;
874         var ddText = this.disabledDatesText;
875         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
876         var ddaysText = this.disabledDaysText;
877         var format = this.format;
878         
879         var setCellClass = function(cal, cell){
880             cell.row = 0;
881             cell.events = [];
882             cell.more = [];
883             //Roo.log('set Cell Class');
884             cell.title = "";
885             var t = d.getTime();
886             
887             //Roo.log(d);
888             
889             cell.dateValue = t;
890             if(t == today){
891                 cell.className += " fc-today";
892                 cell.className += " fc-state-highlight";
893                 cell.title = cal.todayText;
894             }
895             if(t == sel){
896                 // disable highlight in other month..
897                 //cell.className += " fc-state-highlight";
898                 
899             }
900             // disabling
901             if(t < min) {
902                 cell.className = " fc-state-disabled";
903                 cell.title = cal.minText;
904                 return;
905             }
906             if(t > max) {
907                 cell.className = " fc-state-disabled";
908                 cell.title = cal.maxText;
909                 return;
910             }
911             if(ddays){
912                 if(ddays.indexOf(d.getDay()) != -1){
913                     cell.title = ddaysText;
914                     cell.className = " fc-state-disabled";
915                 }
916             }
917             if(ddMatch && format){
918                 var fvalue = d.dateFormat(format);
919                 if(ddMatch.test(fvalue)){
920                     cell.title = ddText.replace("%0", fvalue);
921                     cell.className = " fc-state-disabled";
922                 }
923             }
924             
925             if (!cell.initialClassName) {
926                 cell.initialClassName = cell.dom.className;
927             }
928             
929             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
930         };
931
932         var i = 0;
933         
934         for(; i < startingPos; i++) {
935             textEls[i].innerHTML = (++prevStart);
936             d.setDate(d.getDate()+1);
937             
938             cells[i].className = "fc-past fc-other-month";
939             setCellClass(this, cells[i]);
940         }
941         
942         var intDay = 0;
943         
944         for(; i < days; i++){
945             intDay = i - startingPos + 1;
946             textEls[i].innerHTML = (intDay);
947             d.setDate(d.getDate()+1);
948             
949             cells[i].className = ''; // "x-date-active";
950             setCellClass(this, cells[i]);
951         }
952         var extraDays = 0;
953         
954         for(; i < 42; i++) {
955             textEls[i].innerHTML = (++extraDays);
956             d.setDate(d.getDate()+1);
957             
958             cells[i].className = "fc-future fc-other-month";
959             setCellClass(this, cells[i]);
960         }
961         
962         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
963         
964         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
965         
966         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
967         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
968         
969         if(totalRows != 6){
970             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
971             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
972         }
973         
974         this.fireEvent('monthchange', this, date);
975         
976         
977         /*
978         if(!this.internalRender){
979             var main = this.el.dom.firstChild;
980             var w = main.offsetWidth;
981             this.el.setWidth(w + this.el.getBorderWidth("lr"));
982             Roo.fly(main).setWidth(w);
983             this.internalRender = true;
984             // opera does not respect the auto grow header center column
985             // then, after it gets a width opera refuses to recalculate
986             // without a second pass
987             if(Roo.isOpera && !this.secondPass){
988                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
989                 this.secondPass = true;
990                 this.update.defer(10, this, [date]);
991             }
992         }
993         */
994         
995     },
996     
997     findCell : function(dt) {
998         dt = dt.clearTime().getTime();
999         var ret = false;
1000         this.cells.each(function(c){
1001             //Roo.log("check " +c.dateValue + '?=' + dt);
1002             if(c.dateValue == dt){
1003                 ret = c;
1004                 return false;
1005             }
1006             return true;
1007         });
1008         
1009         return ret;
1010     },
1011     
1012     findCells : function(ev) {
1013         var s = ev.start.clone().clearTime().getTime();
1014        // Roo.log(s);
1015         var e= ev.end.clone().clearTime().getTime();
1016        // Roo.log(e);
1017         var ret = [];
1018         this.cells.each(function(c){
1019              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
1020             
1021             if(c.dateValue > e){
1022                 return ;
1023             }
1024             if(c.dateValue < s){
1025                 return ;
1026             }
1027             ret.push(c);
1028         });
1029         
1030         return ret;    
1031     },
1032     
1033 //    findBestRow: function(cells)
1034 //    {
1035 //        var ret = 0;
1036 //        
1037 //        for (var i =0 ; i < cells.length;i++) {
1038 //            ret  = Math.max(cells[i].rows || 0,ret);
1039 //        }
1040 //        return ret;
1041 //        
1042 //    },
1043     
1044     
1045     addItem : function(ev)
1046     {
1047         // look for vertical location slot in
1048         var cells = this.findCells(ev);
1049         
1050 //        ev.row = this.findBestRow(cells);
1051         
1052         // work out the location.
1053         
1054         var crow = false;
1055         var rows = [];
1056         for(var i =0; i < cells.length; i++) {
1057             
1058             cells[i].row = cells[0].row;
1059             
1060             if(i == 0){
1061                 cells[i].row = cells[i].row + 1;
1062             }
1063             
1064             if (!crow) {
1065                 crow = {
1066                     start : cells[i],
1067                     end :  cells[i]
1068                 };
1069                 continue;
1070             }
1071             if (crow.start.getY() == cells[i].getY()) {
1072                 // on same row.
1073                 crow.end = cells[i];
1074                 continue;
1075             }
1076             // different row.
1077             rows.push(crow);
1078             crow = {
1079                 start: cells[i],
1080                 end : cells[i]
1081             };
1082             
1083         }
1084         
1085         rows.push(crow);
1086         ev.els = [];
1087         ev.rows = rows;
1088         ev.cells = cells;
1089         
1090         cells[0].events.push(ev);
1091         
1092         this.calevents.push(ev);
1093     },
1094     
1095     clearEvents: function() {
1096         
1097         if(!this.calevents){
1098             return;
1099         }
1100         
1101         Roo.each(this.cells.elements, function(c){
1102             c.row = 0;
1103             c.events = [];
1104             c.more = [];
1105         });
1106         
1107         Roo.each(this.calevents, function(e) {
1108             Roo.each(e.els, function(el) {
1109                 el.un('mouseenter' ,this.onEventEnter, this);
1110                 el.un('mouseleave' ,this.onEventLeave, this);
1111                 el.remove();
1112             },this);
1113         },this);
1114         
1115         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
1116             e.remove();
1117         });
1118         
1119     },
1120     
1121     renderEvents: function()
1122     {   
1123         var _this = this;
1124         
1125         this.cells.each(function(c) {
1126             
1127             if(c.row < 5){
1128                 return;
1129             }
1130             
1131             var ev = c.events;
1132             
1133             var r = 4;
1134             if(c.row != c.events.length){
1135                 r = 4 - (4 - (c.row - c.events.length));
1136             }
1137             
1138             c.events = ev.slice(0, r);
1139             c.more = ev.slice(r);
1140             
1141             if(c.more.length && c.more.length == 1){
1142                 c.events.push(c.more.pop());
1143             }
1144             
1145             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
1146             
1147         });
1148             
1149         this.cells.each(function(c) {
1150             
1151             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
1152             
1153             
1154             for (var e = 0; e < c.events.length; e++){
1155                 var ev = c.events[e];
1156                 var rows = ev.rows;
1157                 
1158                 for(var i = 0; i < rows.length; i++) {
1159                 
1160                     // how many rows should it span..
1161
1162                     var  cfg = {
1163                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
1164                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
1165
1166                         unselectable : "on",
1167                         cn : [
1168                             {
1169                                 cls: 'fc-event-inner',
1170                                 cn : [
1171     //                                {
1172     //                                  tag:'span',
1173     //                                  cls: 'fc-event-time',
1174     //                                  html : cells.length > 1 ? '' : ev.time
1175     //                                },
1176                                     {
1177                                       tag:'span',
1178                                       cls: 'fc-event-title',
1179                                       html : String.format('{0}', ev.title)
1180                                     }
1181
1182
1183                                 ]
1184                             },
1185                             {
1186                                 cls: 'ui-resizable-handle ui-resizable-e',
1187                                 html : '&nbsp;&nbsp;&nbsp'
1188                             }
1189
1190                         ]
1191                     };
1192
1193                     if (i == 0) {
1194                         cfg.cls += ' fc-event-start';
1195                     }
1196                     if ((i+1) == rows.length) {
1197                         cfg.cls += ' fc-event-end';
1198                     }
1199
1200                     var ctr = _this.el.select('.fc-event-container',true).first();
1201                     var cg = ctr.createChild(cfg);
1202
1203                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
1204                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
1205
1206                     var r = (c.more.length) ? 1 : 0;
1207                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
1208                     cg.setWidth(ebox.right - sbox.x -2);
1209
1210                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
1211                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
1212                     cg.on('click', _this.onEventClick, _this, ev);
1213
1214                     ev.els.push(cg);
1215                     
1216                 }
1217                 
1218             }
1219             
1220             
1221             if(c.more.length){
1222                 var  cfg = {
1223                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
1224                     style : 'position: absolute',
1225                     unselectable : "on",
1226                     cn : [
1227                         {
1228                             cls: 'fc-event-inner',
1229                             cn : [
1230                                 {
1231                                   tag:'span',
1232                                   cls: 'fc-event-title',
1233                                   html : 'More'
1234                                 }
1235
1236
1237                             ]
1238                         },
1239                         {
1240                             cls: 'ui-resizable-handle ui-resizable-e',
1241                             html : '&nbsp;&nbsp;&nbsp'
1242                         }
1243
1244                     ]
1245                 };
1246
1247                 var ctr = _this.el.select('.fc-event-container',true).first();
1248                 var cg = ctr.createChild(cfg);
1249
1250                 var sbox = c.select('.fc-day-content',true).first().getBox();
1251                 var ebox = c.select('.fc-day-content',true).first().getBox();
1252                 //Roo.log(cg);
1253                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
1254                 cg.setWidth(ebox.right - sbox.x -2);
1255
1256                 cg.on('click', _this.onMoreEventClick, _this, c.more);
1257                 
1258             }
1259             
1260         });
1261         
1262         
1263         
1264     },
1265     
1266     onEventEnter: function (e, el,event,d) {
1267         this.fireEvent('evententer', this, el, event);
1268     },
1269     
1270     onEventLeave: function (e, el,event,d) {
1271         this.fireEvent('eventleave', this, el, event);
1272     },
1273     
1274     onEventClick: function (e, el,event,d) {
1275         this.fireEvent('eventclick', this, el, event);
1276     },
1277     
1278     onMonthChange: function () {
1279         this.store.load();
1280     },
1281     
1282     onMoreEventClick: function(e, el, more)
1283     {
1284         var _this = this;
1285         
1286         this.calpopover.placement = 'right';
1287         this.calpopover.setTitle('More');
1288         
1289         this.calpopover.setContent('');
1290         
1291         var ctr = this.calpopover.el.select('.popover-content', true).first();
1292         
1293         Roo.each(more, function(m){
1294             var cfg = {
1295                 cls : 'fc-event-hori fc-event-draggable',
1296                 html : m.title
1297             };
1298             var cg = ctr.createChild(cfg);
1299             
1300             cg.on('click', _this.onEventClick, _this, m);
1301         });
1302         
1303         this.calpopover.show(el);
1304         
1305         
1306     },
1307     
1308     onLoad: function () 
1309     {   
1310         this.calevents = [];
1311         var cal = this;
1312         
1313         if(this.store.getCount() > 0){
1314             this.store.data.each(function(d){
1315                cal.addItem({
1316                     id : d.data.id,
1317                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
1318                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
1319                     time : d.data.start_time,
1320                     title : d.data.title,
1321                     description : d.data.description,
1322                     venue : d.data.venue
1323                 });
1324             });
1325         }
1326         
1327         this.renderEvents();
1328         
1329         if(this.calevents.length && this.loadMask){
1330             this.maskEl.hide();
1331         }
1332     },
1333     
1334     onBeforeLoad: function()
1335     {
1336         this.clearEvents();
1337         if(this.loadMask){
1338             this.maskEl.show();
1339         }
1340     }
1341 });
1342
1343  
1344  /*
1345  * - LGPL
1346  *
1347  * based on jquery fullcalendar
1348  * 
1349  */
1350
1351
1352 /**
1353  * @class Roo.Calendar
1354  * @extends Roo.Component
1355  * Bootstrap Calendar class
1356     
1357  * @constructor
1358  * Create a new Container
1359  * @param {Object} config The config object
1360  */
1361
1362 Roo.CalendarPanel = function(config){
1363     
1364     Roo.log("cal panel ctr");
1365   
1366     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
1367         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
1368         
1369     //this.wrapper.dom.appendChild(grid.getGridEl().dom);
1370     
1371     Roo.CalendarPanel.superclass.constructor.call(this, this.wrapper, config);
1372     
1373     Roo.log(this.el);
1374     
1375     if(this.toolbar){
1376         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
1377     }
1378     // xtype created footer. - not sure if will work as we normally have to render first..
1379     if (this.footer && !this.footer.el && this.footer.xtype) {
1380         
1381         //this.footer.container = this.grid.getView().getFooterPanel(true);
1382         //this.footer.dataSource = this.grid.dataSource;
1383         //this.footer = Roo.factory(this.footer, Roo);
1384         
1385     }
1386     this.view = new Roo.calendar.View(Roo.apply({
1387         skipNavHeader : true,
1388         skipMonthHeader : false
1389         
1390     },config));
1391     
1392      
1393     this.on('activate', function()
1394     {
1395         Roo.log('activate');
1396          
1397         //console.log('render tree');
1398         this.render();
1399     },this);
1400     
1401     this.addEvents({
1402         /**
1403              * @event select
1404              * Fires when a date is selected
1405              * @param {DatePicker} this
1406              * @param {Date} date The selected date
1407              */
1408         'select': true,
1409         /**
1410              * @event monthchange
1411              * Fires when the displayed month changes 
1412              * @param {DatePicker} this
1413              * @param {Date} date The selected month
1414              */
1415         'monthchange': true,
1416         /**
1417              * @event evententer
1418              * Fires when mouse over an event
1419              * @param {Calendar} this
1420              * @param {event} Event
1421              */
1422         'evententer': true,
1423         /**
1424              * @event eventleave
1425              * Fires when the mouse leaves an
1426              * @param {Calendar} this
1427              * @param {event}
1428              */
1429         'eventleave': true,
1430         /**
1431              * @event eventclick
1432              * Fires when the mouse click an
1433              * @param {Calendar} this
1434              * @param {event}
1435              */
1436         'eventclick': true,
1437         /**
1438              * @event rendered
1439              * Fires when the grid is rendered
1440              * @param {Calendar} this
1441             
1442              */
1443         'rendered': true
1444         
1445         
1446     });
1447     this.relayEvents(this.view, ["select","monthchange","evententer","eventleave","rendered"]);
1448
1449     //this.grid = grid;
1450     //this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
1451 };
1452
1453
1454 Roo.extend(Roo.CalendarPanel, Roo.ContentPanel, {
1455     
1456       
1457     render : function()
1458     {
1459         var ct = this.el.appendChild(document.createElement("div"));
1460         this.onRender(this.el, false)
1461     },
1462     
1463     onRender : function(ct, position)
1464     {
1465         if (this.rendered) {
1466             return;
1467         }
1468         this.rendered = true;
1469         
1470         Roo.log("render calendar");
1471         
1472         
1473         //Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1474         
1475         
1476         var cfg = Roo.apply({},  this.view.getAutoCreate());
1477         cfg.id = Roo.id();
1478         
1479         // fill in the extra attributes 
1480         if (this.xattr && typeof(this.xattr) =='object') {
1481             for (var i in this.xattr) {
1482                 cfg[i] = this.xattr[i];
1483             }
1484         }
1485         
1486         if(this.dataId){
1487             cfg.dataId = this.dataId;
1488         }
1489         
1490         if (this.cls) {
1491             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
1492         }
1493         
1494         if (this.style) { // fixme needs to support more complex style data.
1495             cfg.style = this.style;
1496         }
1497         
1498         if(this.name){
1499             cfg.name = this.name;
1500         }
1501         
1502         this.view.el =  ct.createChild(cfg, position);
1503         
1504         //if(this.tabIndex !== undefined){
1505         //    this.el.dom.setAttribute('tabIndex', this.tabIndex);
1506         //}
1507         
1508         
1509         this.view.initEvents();
1510         this.fireEvent('rendered');
1511     }
1512     
1513     
1514     
1515     
1516 });
1517
1518