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