4 * based on jquery fullcalendar
10 * @class Roo.bootstrap.Calendar
11 * @extends Roo.bootstrap.Component
12 * Bootstrap Calendar class
15 * Create a new Container
16 * @param {Object} config The config object
19 Roo.bootstrap.Calendar = function(config){
20 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
24 * Fires when a date is selected
25 * @param {DatePicker} this
26 * @param {Date} date The selected date
31 * Fires when the displayed month changes
32 * @param {DatePicker} this
33 * @param {Date} date The selected month
38 * Fires when mouse over an event
39 * @param {Calendar} this
40 * @param {event} Event
45 * Fires when the mouse leaves an
46 * @param {Calendar} this
55 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
58 * @cfg {Number} startDay
59 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
63 getAutoCreate : function(){
66 fc_button = function(name, corner, style, content ) {
69 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
71 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
74 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
89 cls : 'fc-header-left',
91 fc_button('prev', 'left', 'arrow', '‹' ),
92 fc_button('next', 'right', 'arrow', '›' ),
93 { tag: 'span', cls: 'fc-header-space' },
94 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
102 cls : 'fc-header-center',
106 cls: 'fc-header-title',
109 html : 'month / year'
117 cls : 'fc-header-right',
119 /* fc_button('month', 'left', '', 'month' ),
120 fc_button('week', '', '', 'week' ),
121 fc_button('day', 'right', '', 'day' )
133 var cal_heads = function() {
135 // fixme - handle this.
137 for (var i =0; i < Date.dayNames.length; i++) {
138 var d = Date.dayNames[i];
141 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
142 html : d.substring(0,3)
146 ret[0].cls += ' fc-first';
147 ret[6].cls += ' fc-last';
150 var cal_cell = function(n) {
153 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
158 cls: 'fc-day-number',
162 cls: 'fc-day-content',
166 style: 'position: relative;' // height: 17px;
178 var cal_rows = function() {
181 for (var r = 0; r < 6; r++) {
188 for (var i =0; i < Date.dayNames.length; i++) {
189 var d = Date.dayNames[i];
190 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
193 row.cn[0].cls+=' fc-first';
194 row.cn[0].cn[0].style = 'min-height:90px';
195 row.cn[6].cls+=' fc-last';
199 ret[0].cls += ' fc-first';
200 ret[4].cls += ' fc-prev-last';
201 ret[5].cls += ' fc-last';
208 cls: 'fc-border-separate',
209 style : 'width:100%',
217 cls : 'fc-first fc-last',
236 style : "position: relative;",
239 cls : 'fc-view fc-view-month fc-grid',
240 style : 'position: relative',
244 cls : 'fc-event-container',
245 style : 'position:absolute;z-index:8;top:0;left:0;'
263 initEvents : function()
266 throw "can not find store for combo";
269 this.store = Roo.factory(this.store, Roo.data);
270 this.store.on('load', this.onLoad, this);
273 this.cells = this.el.select('.fc-day',true);
274 this.textNodes = this.el.query('.fc-day-number');
275 this.cells.addClassOnOver('fc-state-hover');
277 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
278 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
279 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
280 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
282 this.on('monthchange', this.onMonthChange, this);
284 this.update(new Date().clearTime());
287 resize : function() {
288 var sz = this.el.getSize();
290 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
291 this.el.select('.fc-day-content div',true).setHeight(34);
296 showPrevMonth : function(e){
297 this.update(this.activeDate.add("mo", -1));
299 showToday : function(e){
300 this.update(new Date().clearTime());
303 showNextMonth : function(e){
304 this.update(this.activeDate.add("mo", 1));
308 showPrevYear : function(){
309 this.update(this.activeDate.add("y", -1));
313 showNextYear : function(){
314 this.update(this.activeDate.add("y", 1));
319 update : function(date)
321 var vd = this.activeDate;
322 this.activeDate = date;
324 var t = date.getTime();
325 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
326 Roo.log('using add remove');
327 this.cells.removeClass("fc-state-highlight");
328 this.cells.each(function(c){
329 if(c.dateValue == t){
330 c.addClass("fc-state-highlight");
331 setTimeout(function(){
332 try{c.dom.firstChild.focus();}catch(e){}
342 var days = date.getDaysInMonth();
344 var firstOfMonth = date.getFirstDateOfMonth();
345 var startingPos = firstOfMonth.getDay()-this.startDay;
347 if(startingPos < this.startDay){
351 var pm = date.add("mo", -1);
352 var prevStart = pm.getDaysInMonth()-startingPos;
354 var cells = this.cells.elements;
355 var textEls = this.textNodes;
358 // convert everything to numbers so it's fast
360 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
361 var today = new Date().clearTime().getTime();
362 var sel = date.clearTime().getTime();
363 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
364 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
365 var ddMatch = this.disabledDatesRE;
366 var ddText = this.disabledDatesText;
367 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
368 var ddaysText = this.disabledDaysText;
369 var format = this.format;
371 var setCellClass = function(cal, cell){
377 cell.className += " fc-today";
378 cell.title = cal.todayText;
381 cell.className += " fc-state-highlight";
382 //setTimeout(function(){
383 // try{cell.firstChild.focus();}catch(e){}
388 cell.className = " fc-state-disabled";
389 cell.title = cal.minText;
393 cell.className = " fc-state-disabled";
394 cell.title = cal.maxText;
398 if(ddays.indexOf(d.getDay()) != -1){
399 cell.title = ddaysText;
400 cell.className = " fc-state-disabled";
403 if(ddMatch && format){
404 var fvalue = d.dateFormat(format);
405 if(ddMatch.test(fvalue)){
406 cell.title = ddText.replace("%0", fvalue);
407 cell.className = " fc-state-disabled";
410 if (!cell.initialClassName) {
411 cell.initialClassName = cell.dom.className;
413 cell.dom.className = cell.initialClassName + ' ' + cell.className;
417 for(; i < startingPos; i++) {
418 textEls[i].innerHTML = (++prevStart);
419 d.setDate(d.getDate()+1);
420 cells[i].className = "fc-past fc-other-month";
421 setCellClass(this, cells[i]);
423 for(; i < days; i++){
424 intDay = i - startingPos + 1;
425 textEls[i].innerHTML = (intDay);
426 d.setDate(d.getDate()+1);
427 cells[i].className = ''; // "x-date-active";
428 setCellClass(this, cells[i]);
433 textEls[i].innerHTML = (++extraDays);
434 d.setDate(d.getDate()+1);
435 cells[i].className = "fc-future fc-other-month";
436 setCellClass(this, cells[i]);
439 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
441 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
443 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
444 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
447 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
448 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
451 this.fireEvent('monthchange', this, date);
455 if(!this.internalRender){
456 var main = this.el.dom.firstChild;
457 var w = main.offsetWidth;
458 this.el.setWidth(w + this.el.getBorderWidth("lr"));
459 Roo.fly(main).setWidth(w);
460 this.internalRender = true;
461 // opera does not respect the auto grow header center column
462 // then, after it gets a width opera refuses to recalculate
463 // without a second pass
464 if(Roo.isOpera && !this.secondPass){
465 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
466 this.secondPass = true;
467 this.update.defer(10, this, [date]);
474 findCell : function(dt) {
475 dt = dt.clearTime().getTime();
477 this.cells.each(function(c){
478 //Roo.log("check " +c.dateValue + '?=' + dt);
479 if(c.dateValue == dt){
489 findCells : function(ev) {
490 var s = ev.start.clone().clearTime().getTime();
491 var e= ev.end.clone().clearTime().getTime();
493 this.cells.each(function(c){
494 //Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
508 findBestRow: function(cells)
512 for (var i =0 ; i < cells.length;i++) {
513 ret = Math.max(cells[i].rows || 0,ret);
516 //d.setDate(d.ev()+1);
521 addItem : function(ev)
523 // look for vertical location slot in
524 var cells = this.findCells(ev);
526 ev.row = this.findBestRow(cells);
528 // work out the location.
532 for(var i =0; i < cells.length; i++) {
540 if (crow.start.getY() == cells[i].getY()) {
558 for (var i = 0; i < cells.length;i++) {
559 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
563 this.calevents.push(ev);
566 clearEvents: function() {
572 Roo.each(this.cells.elements, function(c){
576 Roo.each(this.calevents, function(e) {
577 Roo.each(e.els, function(el) {
578 el.un('mouseenter' ,this.onEventEnter, this);
579 el.un('mouseleave' ,this.onEventLeave, this);
586 renderEvents: function()
588 // first make sure there is enough space..
590 this.cells.each(function(c) {
591 // Roo.log(c.select('.fc-day-content div',true).first(), Math.max(34, c.rows * 20));
592 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
596 for (var e = 0; e < this.calevents.length; e++) {
597 var ev = this.calevents[e];
598 var cells = ev.cells;
605 for(var i =0; i < rows.length; i++) {
608 // how many rows should it span..
611 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
612 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
617 cls: 'fc-event-inner',
621 cls: 'fc-event-time',
622 html : cells.length > 1 ? '' : '7pm'
626 cls: 'fc-event-title',
627 html : String.format('{0}', ev.title)
634 cls: 'ui-resizable-handle ui-resizable-e',
635 html : '  '
641 cfg.cls += ' fc-event-start';
643 if ((i+1) == rows.length) {
644 cfg.cls += ' fc-event-end';
647 var ctr = this.el.select('.fc-event-container',true).first();
648 var cg = ctr.createChild(cfg);
650 cg.on('mouseenter' ,this.onEventEnter, this);
651 cg.on('mouseleave' ,this.onEventLeave, this);
654 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
655 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
657 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
658 cg.setWidth(ebox.right - sbox.x -2);
660 boxHeight += Roo.log(cg.getHeight());
663 cells.each(function(c) {
664 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, boxHeight));
671 onEventEnter: function (e, el,c,d) {
672 this.fireEvent('evententer', this, el);
675 onEventLeave: function (e, el,c,d) {
676 this.fireEvent('eventleave', this, el);
679 onMonthChange: function () {
683 onLoad: function () {
689 if(this.store.getCount() > 0){
690 this.store.data.each(function(d){
692 start: new Date(d.data.start_dt),
693 end : new Date(d.data.end_dt),
698 Roo.log(this.calevents);