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