/* Grid-based Views: month, basicWeek, basicDay -----------------------------------------------------------------------------*/ setDefaults({ weekMode: 'fixed' }); views.month = function(element, options) { return new Grid(element, options, { render: function(date, delta, fetchEvents) { if (delta) { addMonths(date, delta); date.setDate(1); } // start/end var start = this.start = cloneDate(date, true); start.setDate(1); this.end = addMonths(cloneDate(start), 1); // visStart/visEnd var visStart = this.visStart = cloneDate(start), visEnd = this.visEnd = cloneDate(this.end), nwe = options.weekends ? 0 : 1; if (nwe) { skipWeekend(visStart); skipWeekend(visEnd, -1, true); } addDays(visStart, -((visStart.getDay() - Math.max(options.firstDay, nwe) + 7) % 7)); addDays(visEnd, (7 - visEnd.getDay() + Math.max(options.firstDay, nwe)) % 7); // row count var rowCnt = Math.round((visEnd - visStart) / (DAY_MS * 7)); if (options.weekMode == 'fixed') { addDays(visEnd, (6 - rowCnt) * 7); rowCnt = 6; } // title this.title = formatDate( start, this.option('titleFormat'), options ); // render this.renderGrid( rowCnt, options.weekends ? 7 : 5, this.option('columnFormat'), true, fetchEvents ); } }); } views.basicWeek = function(element, options) { return new Grid(element, options, { render: function(date, delta, fetchEvents) { if (delta) { addDays(date, delta * 7); } var visStart = this.visStart = cloneDate( this.start = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7)) ), visEnd = this.visEnd = cloneDate( this.end = addDays(cloneDate(visStart), 7) ); if (!options.weekends) { skipWeekend(visStart); skipWeekend(visEnd, -1, true); } this.title = formatDates( visStart, addDays(cloneDate(visEnd), -1), this.option('titleFormat'), options ); this.renderGrid( 1, options.weekends ? 7 : 5, this.option('columnFormat'), false, fetchEvents ); } }); }; views.basicDay = function(element, options) { return new Grid(element, options, { render: function(date, delta, fetchEvents) { if (delta) { addDays(date, delta); if (!options.weekends) { skipWeekend(date, delta < 0 ? -1 : 1); } } this.title = formatDate(date, this.option('titleFormat'), options); this.start = this.visStart = cloneDate(date, true); this.end = this.visEnd = addDays(cloneDate(this.start), 1); this.renderGrid(1, 1, this.option('columnFormat'), false, fetchEvents); } }); } // rendering bugs var tdHeightBug, rtlLeftDiff; function Grid(element, options, methods) { var tm, firstDay, nwe, // no weekends (int) rtl, dis, dit, // day index sign / translate rowCnt, colCnt, colWidth, thead, tbody, cachedSegs, //... // initialize superclass view = $.extend(this, viewMethods, methods, { renderGrid: renderGrid, renderEvents: renderEvents, rerenderEvents: rerenderEvents, updateSize: updateSize, defaultEventEnd: function(event) { // calculates an end if event doesnt have one, mostly for resizing return cloneDate(event.start); }, visEventEnd: function(event) { // returns exclusive 'visible' end, for rendering if (event.end) { var end = cloneDate(event.end); return (event.allDay || end.getHours() || end.getMinutes()) ? addDays(end, 1) : end; }else{ return addDays(cloneDate(event.start), 1); } } }); view.init(element, options); /* Grid Rendering -----------------------------------------------------------------------------*/ element.addClass('fc-grid').css('position', 'relative'); if (element.disableSelection) { element.disableSelection(); } function renderGrid(r, c, colFormat, showNumbers, fetchEvents) { rowCnt = r; colCnt = c; // update option-derived variables tm = options.theme ? 'ui' : 'fc'; nwe = options.weekends ? 0 : 1; firstDay = options.firstDay; if (rtl = options.isRTL) { dis = -1; dit = colCnt - 1; }else{ dis = 1; dit = 0; } var month = view.start.getMonth(), today = clearTime(new Date()), s, i, j, d = cloneDate(view.visStart); if (!tbody) { // first time, build all cells from scratch var table = $("").appendTo(element); s = ""; for (i=0; i" + formatDate(d, colFormat, options) + ""; addDays(d, 1); if (nwe) { skipWeekend(d); } } thead = $(s + "").appendTo(table); s = ""; d = cloneDate(view.visStart); for (i=0; i"; for (j=0; j1 && d.getMonth() != month ? ' fc-other-month' : '') + (+d == +today ? ' fc-today '+tm+'-state-highlight' : ' fc-not-today') + "'>" + (showNumbers ? "
" + d.getDate() + "
" : '') + "
 
"; addDays(d, 1); if (nwe) { skipWeekend(d); } } s += ""; } tbody = $(s + "
").appendTo(table); tbody.find('td').click(dayClick); }else{ // NOT first time, reuse as many cells as possible view.clearEvents(); var prevRowCnt = tbody.find('tr').length; if (rowCnt < prevRowCnt) { tbody.find('tr:gt(' + (rowCnt-1) + ')').remove(); // remove extra rows } else if (rowCnt > prevRowCnt) { // needs to create new rows... s = ''; for (i=prevRowCnt; i"; for (j=0; j" + (showNumbers ? "
" : '') + "
 
" + ""; addDays(d, 1); if (nwe) { skipWeekend(d); } } s += ""; } tbody.append(s); } tbody.find('td.fc-new').removeClass('fc-new').click(dayClick); // re-label and re-class existing cells d = cloneDate(view.visStart); tbody.find('td').each(function() { var td = $(this); if (rowCnt > 1) { if (d.getMonth() == month) { td.removeClass('fc-other-month'); }else{ td.addClass('fc-other-month'); } } if (+d == +today) { td.removeClass('fc-not-today') .addClass('fc-today') .addClass(tm + '-state-highlight'); }else{ td.addClass('fc-not-today') .removeClass('fc-today') .removeClass(tm + '-state-highlight'); } td.find('div.fc-day-number').text(d.getDate()); addDays(d, 1); if (nwe) { skipWeekend(d); } }); if (rowCnt == 1) { // more changes likely (week or day view) // redo column header text and class d = cloneDate(view.visStart); thead.find('th').each(function() { $(this).text(formatDate(d, colFormat, options)); this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); addDays(d, 1); if (nwe) { skipWeekend(d); } }); // redo cell day-of-weeks d = cloneDate(view.visStart); tbody.find('td').each(function() { this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); addDays(d, 1); if (nwe) { skipWeekend(d); } }); } } updateSize(); fetchEvents(renderEvents); }; function dayClick(ev) { var n = parseInt(this.className.match(/fc\-day(\d+)/)[1]), date = addDays( cloneDate(view.visStart), Math.floor(n/colCnt) * 7 + n % colCnt ); view.trigger('dayClick', this, date, true, ev); } function updateSize() { var height = Math.round(element.width() / options.aspectRatio), leftTDs = tbody.find('tr td:first-child'), tbodyHeight = height - thead.height(), rowHeight1, rowHeight2; if (options.weekMode == 'variable') { rowHeight1 = rowHeight2 = Math.floor(tbodyHeight / (rowCnt==1 ? 2 : 6)); }else{ rowHeight1 = Math.floor(tbodyHeight / rowCnt); rowHeight2 = tbodyHeight - rowHeight1*(rowCnt-1); } reportTBody(tbody); if (tdHeightBug == undefined) { // bug in firefox where cell height includes padding var tr = tbody.find('tr:first'), td = tr.find('td:first'); td.height(rowHeight1); tdHeightBug = rowHeight1 != td.height(); } if (tdHeightBug) { leftTDs.slice(0, -1).height(rowHeight1); leftTDs.slice(-1).height(rowHeight2); }else{ setOuterHeight(leftTDs.slice(0, -1), rowHeight1); setOuterHeight(leftTDs.slice(-1), rowHeight2); } setOuterWidth( thead.find('th').slice(0, -1), colWidth = Math.floor(element.width() / colCnt) ); } /* Event Rendering -----------------------------------------------------------------------------*/ function renderEvents(events) { view.reportEvents(events); renderSegs(cachedSegs = compileSegs(events)); } function rerenderEvents(skipCompile) { view.clearEvents(); if (skipCompile) { renderSegs(cachedSegs); }else{ renderEvents(view.cachedEvents); } } function compileSegs(events) { var d1 = cloneDate(view.visStart), d2 = addDays(cloneDate(d1), colCnt), rows = [], i=0; for (; i") .append(eventAnchor = $("") .append(event.allDay || !seg.isStart ? null : $("") .html(formatDates(event.start, event.end, view.option('timeFormat'), options))) .append($("") .text(event.title))); if (event.url) { eventAnchor.attr('href', event.url); } triggerRes = view.trigger('eventRender', event, event, eventElement); if (triggerRes !== false) { if (triggerRes && typeof triggerRes != 'boolean') { eventElement = $(triggerRes); } eventElement .css({ position: 'absolute', top: top, left: left + (rtlLeftDiff||0), zIndex: 8 }) .appendTo(element); setOuterWidth(eventElement, right-left, true); if (rtl && rtlLeftDiff == undefined) { // bug in IE6 where offsets are miscalculated with direction:rtl rtlLeftDiff = left - eventElement.position().left; if (rtlLeftDiff) { eventElement.css('left', left + rtlLeftDiff); } } view.eventElementHandlers(event, eventElement); if (event.editable || event.editable == undefined && options.editable) { draggableEvent(event, eventElement); if (seg.isEnd) { view.resizableDayEvent(event, eventElement, colWidth); } } view.reportEventElement(event, eventElement); view.trigger('eventAfterRender', event, event, eventElement); levelHeight = Math.max(levelHeight, eventElement.outerHeight(true)); } } rowContentHeight += levelHeight; top += levelHeight; } innerDiv.height(rowContentHeight); } } /* Event Dragging -----------------------------------------------------------------------------*/ function draggableEvent(event, eventElement) { if (!options.disableDragging && eventElement.draggable) { var matrix; eventElement.draggable({ zIndex: 9, delay: 50, opacity: view.option('dragOpacity'), revertDuration: options.dragRevertDuration, start: function(ev, ui) { view.hideEvents(event, eventElement); view.trigger('eventDragStart', eventElement, event, ev, ui); matrix = new HoverMatrix(function(cell) { eventElement.draggable('option', 'revert', !cell || !cell.rowDelta && !cell.colDelta); if (cell) { view.showOverlay(cell); }else{ view.hideOverlay(); } }); tbody.find('tr').each(function() { matrix.row(this); }); var tds = tbody.find('tr:first td'); if (rtl) { tds = $(tds.get().reverse()); } tds.each(function() { matrix.col(this); }); matrix.mouse(ev.pageX, ev.pageY); }, drag: function(ev) { matrix.mouse(ev.pageX, ev.pageY); }, stop: function(ev, ui) { view.hideOverlay(); view.trigger('eventDragStop', eventElement, event, ev, ui); var cell = matrix.cell; if (!cell || !cell.rowDelta && !cell.colDelta) { if ($.browser.msie) { eventElement.css('filter', ''); // clear IE opacity side-effects } view.showEvents(event, eventElement); }else{ eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link view.eventDrop(this, event, cell.rowDelta*7+cell.colDelta*dis, 0, event.allDay, ev, ui); } } }); } } // event resizing w/ 'view' methods... };