/* Grid-based Views: month, basicWeek, basicDay -----------------------------------------------------------------------------*/ setDefaults({ weekMode: 'fixed' }); views.month = function(element, options, viewName) { return new Grid(element, options, { render: function(date, delta) { 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 ); } }, viewName); }; views.basicWeek = function(element, options, viewName) { return new Grid(element, options, { render: function(date, delta) { 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 ); } }, viewName); }; views.basicDay = function(element, options, viewName) { return new Grid(element, options, { render: function(date, delta) { 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 ); } }, viewName); }; // rendering bugs var tdHeightBug; function Grid(element, options, methods, viewName) { var tm, firstDay, nwe, // no weekends (int) rtl, dis, dit, // day index sign / translate viewWidth, viewHeight, rowCnt, colCnt, colWidth, thead, tbody, cachedEvents=[], segmentContainer, dayContentPositions = new HorizontalPositionCache(function(dayOfWeek) { return tbody.find('td:eq(' + ((dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt) + ') div div'); }), // ... // initialize superclass view = $.extend(this, viewMethods, methods, { renderGrid: renderGrid, renderEvents: renderEvents, rerenderEvents: rerenderEvents, clearEvents: clearEvents, setHeight: setHeight, setWidth: setWidth, defaultEventEnd: function(event) { // calculates an end if event doesnt have one, mostly for resizing return cloneDate(event.start); } }); view.name = viewName; view.init(element, options); /* Grid Rendering -----------------------------------------------------------------------------*/ disableTextSelection(element.addClass('fc-grid')); function renderGrid(r, c, colFormat, showNumbers) { 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); dayBind(tbody.find('td')); segmentContainer = $("
").appendTo(element); }else{ // NOT first time, reuse as many cells as possible 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); } dayBind(tbody.find('td.fc-new').removeClass('fc-new')); // 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); } }); } } } function setHeight(height) { viewHeight = height; var leftTDs = tbody.find('tr td:first-child'), tbodyHeight = viewHeight - 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); } 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); } } function setWidth(width) { viewWidth = width; dayContentPositions.clear(); setOuterWidth( thead.find('th').slice(0, -1), colWidth = Math.floor(viewWidth / colCnt) ); } /* Event Rendering -----------------------------------------------------------------------------*/ function renderEvents(events) { view.reportEvents(cachedEvents = events); renderSegs(compileSegs(events)); } function rerenderEvents(modifiedEventId) { clearEvents(); renderSegs(compileSegs(cachedEvents), modifiedEventId); } function clearEvents() { view._clearEvents(); // only clears the hashes segmentContainer.empty(); } function compileSegs(events) { var d1 = cloneDate(view.visStart), d2 = addDays(cloneDate(d1), colCnt), visEventsEnds = $.map(events, exclEndDay), i, row, j, level, k, seg, segs=[]; for (i=0; i" + "" + (!event.allDay && seg.isStart ? "" + htmlEscape(formatDates(event.start, event.end, view.option('timeFormat'), options)) + "" :'') + "" + htmlEscape(event.title) + "" + "" + ((event.editable || event.editable === undefined && options.editable) && !options.disableResizing && $.fn.resizable ? "
" : '') + "
"; seg.left = left; seg.outerWidth = right - left; } segmentContainer[0].innerHTML = html; // faster than html() eventElements = segmentContainer.children(); // retrieve elements, run through eventRender callback, bind handlers for (i=0; i div') // optimal selector? .height(top + levelHeight); } // calculate row tops for (rowI=0; rowI