roojs-ui.js
[roojs1] / roojs-calendar-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = (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 {Roo.data.Store} store
498      * The data source for the calendar
499      */
500         store : false,
501      /**
502      * @cfg {Number} startDay
503      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
504      */
505     startDay : 0,
506     
507     loadMask : false,
508     
509     header : false,
510       
511     getAutoCreate : function(){
512         
513         
514         var fc_button = function(name, corner, style, content ) {
515             return Roo.apply({},{
516                 tag : 'span',
517                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
518                          (corner.length ?
519                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
520                             ''
521                         ),
522                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
523                 unselectable: 'on'
524             });
525         };
526         
527         var header = {};
528         
529         if(!this.header){
530             header = {
531                 tag : 'table',
532                 cls : 'fc-header',
533                 style : 'width:100%',
534                 cn : [
535                     {
536                         tag: 'tr',
537                         cn : [
538                             {
539                                 tag : 'td',
540                                 cls : 'fc-header-left',
541                                 cn : [
542                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
543                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
544                                     { tag: 'span', cls: 'fc-header-space' },
545                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
546
547
548                                 ]
549                             },
550
551                             {
552                                 tag : 'td',
553                                 cls : 'fc-header-center',
554                                 cn : [
555                                     {
556                                         tag: 'span',
557                                         cls: 'fc-header-title',
558                                         cn : {
559                                             tag: 'H2',
560                                             html : 'month / year'
561                                         }
562                                     }
563
564                                 ]
565                             },
566                             {
567                                 tag : 'td',
568                                 cls : 'fc-header-right',
569                                 cn : [
570                               /*      fc_button('month', 'left', '', 'month' ),
571                                     fc_button('week', '', '', 'week' ),
572                                     fc_button('day', 'right', '', 'day' )
573                                 */    
574
575                                 ]
576                             }
577
578                         ]
579                     }
580                 ]
581             };
582         }
583         
584         header = this.header;
585         
586        
587         var cal_heads = function() {
588             var ret = [];
589             // fixme - handle this.
590             
591             for (var i =0; i < Date.dayNames.length; i++) {
592                 var d = Date.dayNames[i];
593                 ret.push({
594                     tag: 'th',
595                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
596                     html : d.substring(0,3)
597                 });
598                 
599             }
600             ret[0].cls += ' fc-first';
601             ret[6].cls += ' fc-last';
602             return ret;
603         };
604         var cal_cell = function(n) {
605             return  {
606                 tag: 'td',
607                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
608                 cn : [
609                     {
610                         cn : [
611                             {
612                                 cls: 'fc-day-number',
613                                 html: 'D'
614                             },
615                             {
616                                 cls: 'fc-day-content',
617                              
618                                 cn : [
619                                      {
620                                         style: 'position: relative;' // height: 17px;
621                                     }
622                                 ]
623                             }
624                             
625                             
626                         ]
627                     }
628                 ]
629                 
630             }
631         };
632         var cal_rows = function() {
633             
634             var ret = [];
635             for (var r = 0; r < 6; r++) {
636                 var row= {
637                     tag : 'tr',
638                     cls : 'fc-week',
639                     cn : []
640                 };
641                 
642                 for (var i =0; i < Date.dayNames.length; i++) {
643                     var d = Date.dayNames[i];
644                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
645
646                 }
647                 row.cn[0].cls+=' fc-first';
648                 row.cn[0].cn[0].style = 'min-height:90px';
649                 row.cn[6].cls+=' fc-last';
650                 ret.push(row);
651                 
652             }
653             ret[0].cls += ' fc-first';
654             ret[4].cls += ' fc-prev-last';
655             ret[5].cls += ' fc-last';
656             return ret;
657             
658         };
659         
660         var cal_table = {
661             tag: 'table',
662             cls: 'fc-border-separate',
663             style : 'width:100%',
664             cellspacing  : 0,
665             cn : [
666                 { 
667                     tag: 'thead',
668                     cn : [
669                         { 
670                             tag: 'tr',
671                             cls : 'fc-first fc-last',
672                             cn : cal_heads()
673                         }
674                     ]
675                 },
676                 { 
677                     tag: 'tbody',
678                     cn : cal_rows()
679                 }
680                   
681             ]
682         };
683          
684          var cfg = {
685             cls : 'fc fc-ltr',
686             cn : [
687                 header,
688                 {
689                     cls : 'fc-content',
690                     style : "position: relative;",
691                     cn : [
692                         {
693                             cls : 'fc-view fc-view-month fc-grid',
694                             style : 'position: relative',
695                             unselectable : 'on',
696                             cn : [
697                                 {
698                                     cls : 'fc-event-container',
699                                     style : 'position:absolute;z-index:8;top:0;left:0;'
700                                 },
701                                 cal_table
702                             ]
703                         }
704                     ]
705     
706                 }
707            ] 
708             
709         };
710         
711          
712         
713         return cfg;
714     },
715     
716     
717     initEvents : function()
718     {
719         if(!this.store){
720             throw "can not find store for calendar";
721         }
722         
723         var mark = {
724             tag: "div",
725             cls:"x-dlg-mask",
726             style: "text-align:center",
727             cn: [
728                 {
729                     tag: "div",
730                     style: "background-color:white;width:50%;margin:250 auto",
731                     cn: [
732                         {
733                             tag: "img",
734                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
735                         },
736                         {
737                             tag: "span",
738                             html: "Loading"
739                         }
740                         
741                     ]
742                 }
743             ]
744         };
745         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
746         
747         var size = this.el.select('.fc-content', true).first().getSize();
748         this.maskEl.setSize(size.width, size.height);
749         this.maskEl.enableDisplayMode("block");
750         if(!this.loadMask){
751             this.maskEl.hide();
752         }
753         
754         this.store = Roo.factory(this.store, Roo.data);
755         this.store.on('load', this.onLoad, this);
756         this.store.on('beforeload', this.onBeforeLoad, this);
757         
758         this.resize();
759         
760         this.cells = this.el.select('.fc-day',true);
761         //Roo.log(this.cells);
762         this.textNodes = this.el.query('.fc-day-number');
763         this.cells.addClassOnOver('fc-state-hover');
764         
765         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
766         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
767         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
768         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
769         
770         this.on('monthchange', this.onMonthChange, this);
771         
772         this.update(new Date().clearTime());
773     },
774     
775     resize : function() {
776         var sz  = this.el.getSize();
777         
778         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
779         this.el.select('.fc-day-content div',true).setHeight(34);
780     },
781     
782     
783     // private
784     showPrevMonth : function(e){
785         this.update(this.activeDate.add("mo", -1));
786     },
787     showToday : function(e){
788         this.update(new Date().clearTime());
789     },
790     // private
791     showNextMonth : function(e){
792         this.update(this.activeDate.add("mo", 1));
793     },
794
795     // private
796     showPrevYear : function(){
797         this.update(this.activeDate.add("y", -1));
798     },
799
800     // private
801     showNextYear : function(){
802         this.update(this.activeDate.add("y", 1));
803     },
804
805     
806    // private
807     update : function(date)
808     {
809         var vd = this.activeDate;
810         this.activeDate = date;
811 //        if(vd && this.el){
812 //            var t = date.getTime();
813 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
814 //                Roo.log('using add remove');
815 //                
816 //                this.fireEvent('monthchange', this, date);
817 //                
818 //                this.cells.removeClass("fc-state-highlight");
819 //                this.cells.each(function(c){
820 //                   if(c.dateValue == t){
821 //                       c.addClass("fc-state-highlight");
822 //                       setTimeout(function(){
823 //                            try{c.dom.firstChild.focus();}catch(e){}
824 //                       }, 50);
825 //                       return false;
826 //                   }
827 //                   return true;
828 //                });
829 //                return;
830 //            }
831 //        }
832         
833         var days = date.getDaysInMonth();
834         
835         var firstOfMonth = date.getFirstDateOfMonth();
836         var startingPos = firstOfMonth.getDay()-this.startDay;
837         
838         if(startingPos < this.startDay){
839             startingPos += 7;
840         }
841         
842         var pm = date.add(Date.MONTH, -1);
843         var prevStart = pm.getDaysInMonth()-startingPos;
844 //        
845         this.cells = this.el.select('.fc-day',true);
846         this.textNodes = this.el.query('.fc-day-number');
847         this.cells.addClassOnOver('fc-state-hover');
848         
849         var cells = this.cells.elements;
850         var textEls = this.textNodes;
851         
852         Roo.each(cells, function(cell){
853             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
854         });
855         
856         days += startingPos;
857
858         // convert everything to numbers so it's fast
859         var day = 86400000;
860         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
861         //Roo.log(d);
862         //Roo.log(pm);
863         //Roo.log(prevStart);
864         
865         var today = new Date().clearTime().getTime();
866         var sel = date.clearTime().getTime();
867         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
868         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
869         var ddMatch = this.disabledDatesRE;
870         var ddText = this.disabledDatesText;
871         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
872         var ddaysText = this.disabledDaysText;
873         var format = this.format;
874         
875         var setCellClass = function(cal, cell){
876             cell.row = 0;
877             cell.events = [];
878             cell.more = [];
879             //Roo.log('set Cell Class');
880             cell.title = "";
881             var t = d.getTime();
882             
883             //Roo.log(d);
884             
885             cell.dateValue = t;
886             if(t == today){
887                 cell.className += " fc-today";
888                 cell.className += " fc-state-highlight";
889                 cell.title = cal.todayText;
890             }
891             if(t == sel){
892                 // disable highlight in other month..
893                 //cell.className += " fc-state-highlight";
894                 
895             }
896             // disabling
897             if(t < min) {
898                 cell.className = " fc-state-disabled";
899                 cell.title = cal.minText;
900                 return;
901             }
902             if(t > max) {
903                 cell.className = " fc-state-disabled";
904                 cell.title = cal.maxText;
905                 return;
906             }
907             if(ddays){
908                 if(ddays.indexOf(d.getDay()) != -1){
909                     cell.title = ddaysText;
910                     cell.className = " fc-state-disabled";
911                 }
912             }
913             if(ddMatch && format){
914                 var fvalue = d.dateFormat(format);
915                 if(ddMatch.test(fvalue)){
916                     cell.title = ddText.replace("%0", fvalue);
917                     cell.className = " fc-state-disabled";
918                 }
919             }
920             
921             if (!cell.initialClassName) {
922                 cell.initialClassName = cell.dom.className;
923             }
924             
925             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
926         };
927
928         var i = 0;
929         
930         for(; i < startingPos; i++) {
931             textEls[i].innerHTML = (++prevStart);
932             d.setDate(d.getDate()+1);
933             
934             cells[i].className = "fc-past fc-other-month";
935             setCellClass(this, cells[i]);
936         }
937         
938         var intDay = 0;
939         
940         for(; i < days; i++){
941             intDay = i - startingPos + 1;
942             textEls[i].innerHTML = (intDay);
943             d.setDate(d.getDate()+1);
944             
945             cells[i].className = ''; // "x-date-active";
946             setCellClass(this, cells[i]);
947         }
948         var extraDays = 0;
949         
950         for(; i < 42; i++) {
951             textEls[i].innerHTML = (++extraDays);
952             d.setDate(d.getDate()+1);
953             
954             cells[i].className = "fc-future fc-other-month";
955             setCellClass(this, cells[i]);
956         }
957         
958         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
959         
960         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
961         
962         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
963         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
964         
965         if(totalRows != 6){
966             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
967             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
968         }
969         
970         this.fireEvent('monthchange', this, date);
971         
972         
973         /*
974         if(!this.internalRender){
975             var main = this.el.dom.firstChild;
976             var w = main.offsetWidth;
977             this.el.setWidth(w + this.el.getBorderWidth("lr"));
978             Roo.fly(main).setWidth(w);
979             this.internalRender = true;
980             // opera does not respect the auto grow header center column
981             // then, after it gets a width opera refuses to recalculate
982             // without a second pass
983             if(Roo.isOpera && !this.secondPass){
984                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
985                 this.secondPass = true;
986                 this.update.defer(10, this, [date]);
987             }
988         }
989         */
990         
991     },
992     
993     findCell : function(dt) {
994         dt = dt.clearTime().getTime();
995         var ret = false;
996         this.cells.each(function(c){
997             //Roo.log("check " +c.dateValue + '?=' + dt);
998             if(c.dateValue == dt){
999                 ret = c;
1000                 return false;
1001             }
1002             return true;
1003         });
1004         
1005         return ret;
1006     },
1007     
1008     findCells : function(ev) {
1009         var s = ev.start.clone().clearTime().getTime();
1010        // Roo.log(s);
1011         var e= ev.end.clone().clearTime().getTime();
1012        // Roo.log(e);
1013         var ret = [];
1014         this.cells.each(function(c){
1015              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
1016             
1017             if(c.dateValue > e){
1018                 return ;
1019             }
1020             if(c.dateValue < s){
1021                 return ;
1022             }
1023             ret.push(c);
1024         });
1025         
1026         return ret;    
1027     },
1028     
1029 //    findBestRow: function(cells)
1030 //    {
1031 //        var ret = 0;
1032 //        
1033 //        for (var i =0 ; i < cells.length;i++) {
1034 //            ret  = Math.max(cells[i].rows || 0,ret);
1035 //        }
1036 //        return ret;
1037 //        
1038 //    },
1039     
1040     
1041     addItem : function(ev)
1042     {
1043         // look for vertical location slot in
1044         var cells = this.findCells(ev);
1045         
1046 //        ev.row = this.findBestRow(cells);
1047         
1048         // work out the location.
1049         
1050         var crow = false;
1051         var rows = [];
1052         for(var i =0; i < cells.length; i++) {
1053             
1054             cells[i].row = cells[0].row;
1055             
1056             if(i == 0){
1057                 cells[i].row = cells[i].row + 1;
1058             }
1059             
1060             if (!crow) {
1061                 crow = {
1062                     start : cells[i],
1063                     end :  cells[i]
1064                 };
1065                 continue;
1066             }
1067             if (crow.start.getY() == cells[i].getY()) {
1068                 // on same row.
1069                 crow.end = cells[i];
1070                 continue;
1071             }
1072             // different row.
1073             rows.push(crow);
1074             crow = {
1075                 start: cells[i],
1076                 end : cells[i]
1077             };
1078             
1079         }
1080         
1081         rows.push(crow);
1082         ev.els = [];
1083         ev.rows = rows;
1084         ev.cells = cells;
1085         
1086         cells[0].events.push(ev);
1087         
1088         this.calevents.push(ev);
1089     },
1090     
1091     clearEvents: function() {
1092         
1093         if(!this.calevents){
1094             return;
1095         }
1096         
1097         Roo.each(this.cells.elements, function(c){
1098             c.row = 0;
1099             c.events = [];
1100             c.more = [];
1101         });
1102         
1103         Roo.each(this.calevents, function(e) {
1104             Roo.each(e.els, function(el) {
1105                 el.un('mouseenter' ,this.onEventEnter, this);
1106                 el.un('mouseleave' ,this.onEventLeave, this);
1107                 el.remove();
1108             },this);
1109         },this);
1110         
1111         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
1112             e.remove();
1113         });
1114         
1115     },
1116     
1117     renderEvents: function()
1118     {   
1119         var _this = this;
1120         
1121         this.cells.each(function(c) {
1122             
1123             if(c.row < 5){
1124                 return;
1125             }
1126             
1127             var ev = c.events;
1128             
1129             var r = 4;
1130             if(c.row != c.events.length){
1131                 r = 4 - (4 - (c.row - c.events.length));
1132             }
1133             
1134             c.events = ev.slice(0, r);
1135             c.more = ev.slice(r);
1136             
1137             if(c.more.length && c.more.length == 1){
1138                 c.events.push(c.more.pop());
1139             }
1140             
1141             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
1142             
1143         });
1144             
1145         this.cells.each(function(c) {
1146             
1147             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
1148             
1149             
1150             for (var e = 0; e < c.events.length; e++){
1151                 var ev = c.events[e];
1152                 var rows = ev.rows;
1153                 
1154                 for(var i = 0; i < rows.length; i++) {
1155                 
1156                     // how many rows should it span..
1157
1158                     var  cfg = {
1159                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
1160                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
1161
1162                         unselectable : "on",
1163                         cn : [
1164                             {
1165                                 cls: 'fc-event-inner',
1166                                 cn : [
1167     //                                {
1168     //                                  tag:'span',
1169     //                                  cls: 'fc-event-time',
1170     //                                  html : cells.length > 1 ? '' : ev.time
1171     //                                },
1172                                     {
1173                                       tag:'span',
1174                                       cls: 'fc-event-title',
1175                                       html : String.format('{0}', ev.title)
1176                                     }
1177
1178
1179                                 ]
1180                             },
1181                             {
1182                                 cls: 'ui-resizable-handle ui-resizable-e',
1183                                 html : '&nbsp;&nbsp;&nbsp'
1184                             }
1185
1186                         ]
1187                     };
1188
1189                     if (i == 0) {
1190                         cfg.cls += ' fc-event-start';
1191                     }
1192                     if ((i+1) == rows.length) {
1193                         cfg.cls += ' fc-event-end';
1194                     }
1195
1196                     var ctr = _this.el.select('.fc-event-container',true).first();
1197                     var cg = ctr.createChild(cfg);
1198
1199                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
1200                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
1201
1202                     var r = (c.more.length) ? 1 : 0;
1203                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
1204                     cg.setWidth(ebox.right - sbox.x -2);
1205
1206                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
1207                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
1208                     cg.on('click', _this.onEventClick, _this, ev);
1209
1210                     ev.els.push(cg);
1211                     
1212                 }
1213                 
1214             }
1215             
1216             
1217             if(c.more.length){
1218                 var  cfg = {
1219                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
1220                     style : 'position: absolute',
1221                     unselectable : "on",
1222                     cn : [
1223                         {
1224                             cls: 'fc-event-inner',
1225                             cn : [
1226                                 {
1227                                   tag:'span',
1228                                   cls: 'fc-event-title',
1229                                   html : 'More'
1230                                 }
1231
1232
1233                             ]
1234                         },
1235                         {
1236                             cls: 'ui-resizable-handle ui-resizable-e',
1237                             html : '&nbsp;&nbsp;&nbsp'
1238                         }
1239
1240                     ]
1241                 };
1242
1243                 var ctr = _this.el.select('.fc-event-container',true).first();
1244                 var cg = ctr.createChild(cfg);
1245
1246                 var sbox = c.select('.fc-day-content',true).first().getBox();
1247                 var ebox = c.select('.fc-day-content',true).first().getBox();
1248                 //Roo.log(cg);
1249                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
1250                 cg.setWidth(ebox.right - sbox.x -2);
1251
1252                 cg.on('click', _this.onMoreEventClick, _this, c.more);
1253                 
1254             }
1255             
1256         });
1257         
1258         
1259         
1260     },
1261     
1262     onEventEnter: function (e, el,event,d) {
1263         this.fireEvent('evententer', this, el, event);
1264     },
1265     
1266     onEventLeave: function (e, el,event,d) {
1267         this.fireEvent('eventleave', this, el, event);
1268     },
1269     
1270     onEventClick: function (e, el,event,d) {
1271         this.fireEvent('eventclick', this, el, event);
1272     },
1273     
1274     onMonthChange: function () {
1275         this.store.load();
1276     },
1277     
1278     onMoreEventClick: function(e, el, more)
1279     {
1280         var _this = this;
1281         
1282         this.calpopover.placement = 'right';
1283         this.calpopover.setTitle('More');
1284         
1285         this.calpopover.setContent('');
1286         
1287         var ctr = this.calpopover.el.select('.popover-content', true).first();
1288         
1289         Roo.each(more, function(m){
1290             var cfg = {
1291                 cls : 'fc-event-hori fc-event-draggable',
1292                 html : m.title
1293             };
1294             var cg = ctr.createChild(cfg);
1295             
1296             cg.on('click', _this.onEventClick, _this, m);
1297         });
1298         
1299         this.calpopover.show(el);
1300         
1301         
1302     },
1303     
1304     onLoad: function () 
1305     {   
1306         this.calevents = [];
1307         var cal = this;
1308         
1309         if(this.store.getCount() > 0){
1310             this.store.data.each(function(d){
1311                cal.addItem({
1312                     id : d.data.id,
1313                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
1314                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
1315                     time : d.data.start_time,
1316                     title : d.data.title,
1317                     description : d.data.description,
1318                     venue : d.data.venue
1319                 });
1320             });
1321         }
1322         
1323         this.renderEvents();
1324         
1325         if(this.calevents.length && this.loadMask){
1326             this.maskEl.hide();
1327         }
1328     },
1329     
1330     onBeforeLoad: function()
1331     {
1332         this.clearEvents();
1333         if(this.loadMask){
1334             this.maskEl.show();
1335         }
1336     }
1337 });
1338
1339  
1340  /*
1341  * - LGPL
1342  *
1343  * based on jquery fullcalendar
1344  * 
1345  */
1346
1347
1348 /**
1349  * @class Roo.Calendar
1350  * @extends Roo.Component
1351  * Bootstrap Calendar class
1352     
1353  * @constructor
1354  * Create a new Container
1355  * @param {Object} config The config object
1356  */
1357
1358 Roo.CalendarPanel = function(config){
1359     
1360     Roo.log("cal panel ctr");
1361   
1362     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
1363         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
1364         
1365     //this.wrapper.dom.appendChild(grid.getGridEl().dom);
1366     
1367     Roo.CalendarPanel.superclass.constructor.call(this, this.wrapper, config);
1368     
1369     Roo.log(this.el);
1370     
1371     if(this.toolbar){
1372         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
1373     }
1374     // xtype created footer. - not sure if will work as we normally have to render first..
1375     if (this.footer && !this.footer.el && this.footer.xtype) {
1376         
1377         //this.footer.container = this.grid.getView().getFooterPanel(true);
1378         //this.footer.dataSource = this.grid.dataSource;
1379         //this.footer = Roo.factory(this.footer, Roo);
1380         
1381     }
1382     this.view = new Roo.calendar.View(Roo.apply({
1383         skipNavHeader : true,
1384         skipMonthHeader : false
1385         
1386     },config));
1387     
1388      
1389     this.on('activate', function()
1390     {
1391         Roo.log('activate');
1392          
1393         //console.log('render tree');
1394         this.render();
1395     },this);
1396     
1397     this.addEvents({
1398         /**
1399              * @event select
1400              * Fires when a date is selected
1401              * @param {DatePicker} this
1402              * @param {Date} date The selected date
1403              */
1404         'select': true,
1405         /**
1406              * @event monthchange
1407              * Fires when the displayed month changes 
1408              * @param {DatePicker} this
1409              * @param {Date} date The selected month
1410              */
1411         'monthchange': true,
1412         /**
1413              * @event evententer
1414              * Fires when mouse over an event
1415              * @param {Calendar} this
1416              * @param {event} Event
1417              */
1418         'evententer': true,
1419         /**
1420              * @event eventleave
1421              * Fires when the mouse leaves an
1422              * @param {Calendar} this
1423              * @param {event}
1424              */
1425         'eventleave': true,
1426         /**
1427              * @event eventclick
1428              * Fires when the mouse click an
1429              * @param {Calendar} this
1430              * @param {event}
1431              */
1432         'eventclick': true,
1433         /**
1434              * @event rendered
1435              * Fires when the grid is rendered
1436              * @param {Calendar} this
1437             
1438              */
1439         'rendered': true
1440         
1441         
1442     });
1443     this.relayEvents(this.view, ["select","monthchange","evententer","eventleave","rendered"]);
1444
1445     //this.grid = grid;
1446     //this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
1447 };
1448
1449
1450 Roo.extend(Roo.CalendarPanel, Roo.ContentPanel, {
1451     
1452       
1453     render : function()
1454     {
1455         var ct = this.el.appendChild(document.createElement("div"));
1456         this.onRender(this.el, false)
1457     },
1458     
1459     onRender : function(ct, position)
1460     {
1461         if (this.rendered) {
1462             return;
1463         }
1464         this.rendered = true;
1465         
1466         Roo.log("render calendar");
1467         
1468         
1469         //Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1470         
1471         
1472         var cfg = Roo.apply({},  this.view.getAutoCreate());
1473         cfg.id = Roo.id();
1474         
1475         // fill in the extra attributes 
1476         if (this.xattr && typeof(this.xattr) =='object') {
1477             for (var i in this.xattr) {
1478                 cfg[i] = this.xattr[i];
1479             }
1480         }
1481         
1482         if(this.dataId){
1483             cfg.dataId = this.dataId;
1484         }
1485         
1486         if (this.cls) {
1487             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
1488         }
1489         
1490         if (this.style) { // fixme needs to support more complex style data.
1491             cfg.style = this.style;
1492         }
1493         
1494         if(this.name){
1495             cfg.name = this.name;
1496         }
1497         
1498         this.view.el =  ct.createChild(cfg, position);
1499         
1500         //if(this.tabIndex !== undefined){
1501         //    this.el.dom.setAttribute('tabIndex', this.tabIndex);
1502         //}
1503         
1504         
1505         this.view.initEvents();
1506         this.fireEvent('rendered');
1507     }
1508     
1509     
1510     
1511     
1512 });
1513
1514