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