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