mirror of
https://github.com/wassname/fullcalendar.git
synced 2026-08-04 12:53:58 +08:00
288 lines
7.6 KiB
JavaScript
288 lines
7.6 KiB
JavaScript
|
|
/* Event-rendering methods for the DayGrid class
|
|
----------------------------------------------------------------------------------------------------------------------*/
|
|
|
|
$.extend(DayGrid.prototype, {
|
|
|
|
segs: null,
|
|
rowStructs: null, // an array of objects, each holding information about a row's event-rendering
|
|
|
|
|
|
// Render the given events onto the Grid and return the rendered segments
|
|
renderEvents: function(events) {
|
|
var rowStructs = this.rowStructs = this.renderEventRows(events);
|
|
var segs = [];
|
|
|
|
// append to each row's content skeleton
|
|
this.rowEls.each(function(i, rowNode) {
|
|
$(rowNode).find('.fc-content-skeleton > table').append(
|
|
rowStructs[i].tbodyEl
|
|
);
|
|
segs.push.apply(segs, rowStructs[i].segs);
|
|
});
|
|
|
|
this.segs = segs;
|
|
},
|
|
|
|
|
|
// Retrieves all segment objects that have been rendered
|
|
getSegs: function() {
|
|
return (this.segs || []).concat(
|
|
this.popoverSegs || [] // segs rendered in the "more" events popover
|
|
);
|
|
},
|
|
|
|
|
|
// Removes all rendered event elements
|
|
destroyEvents: function() {
|
|
var rowStructs = this.rowStructs || [];
|
|
var rowStruct;
|
|
|
|
while ((rowStruct = rowStructs.pop())) {
|
|
rowStruct.tbodyEl.remove();
|
|
}
|
|
|
|
this.segs = null;
|
|
this.destroySegPopover(); // removes the "more.." events popover
|
|
},
|
|
|
|
|
|
// Uses the given events array to generate <tbody> elements that should be appended to each row's content skeleton.
|
|
// Returns an array of rowStruct objects (see the bottom of `renderEventRow`).
|
|
renderEventRows: function(events) {
|
|
var segs = this.eventsToSegs(events);
|
|
var rowStructs = [];
|
|
var segRows;
|
|
var row;
|
|
|
|
segs = this.renderSegs(segs); // returns a new array with only visible segments
|
|
segRows = this.groupSegRows(segs); // group into nested arrays
|
|
|
|
// iterate each row of segment groupings
|
|
for (row = 0; row < segRows.length; row++) {
|
|
rowStructs.push(
|
|
this.renderEventRow(row, segRows[row])
|
|
);
|
|
}
|
|
|
|
return rowStructs;
|
|
},
|
|
|
|
|
|
// Builds the HTML to be used for the default element for an individual segment
|
|
renderSegHtml: function(seg, disableResizing) {
|
|
var view = this.view;
|
|
var isRTL = view.opt('isRTL');
|
|
var event = seg.event;
|
|
var isDraggable = view.isEventDraggable(event);
|
|
var isResizable = !disableResizing && event.allDay && seg.isEnd && view.isEventResizable(event);
|
|
var classes = this.getSegClasses(seg, isDraggable, isResizable);
|
|
var skinCss = this.getEventSkinCss(event);
|
|
var timeHtml = '';
|
|
var titleHtml;
|
|
|
|
classes.unshift('fc-day-grid-event');
|
|
|
|
// Only display a timed events time if it is the starting segment
|
|
if (!event.allDay && seg.isStart) {
|
|
timeHtml = '<span class="fc-time">' + htmlEscape(view.getEventTimeText(event)) + '</span>';
|
|
}
|
|
|
|
titleHtml =
|
|
'<span class="fc-title">' +
|
|
(htmlEscape(event.title || '') || ' ') + // we always want one line of height
|
|
'</span>';
|
|
|
|
return '<a class="' + classes.join(' ') + '"' +
|
|
(event.url ?
|
|
' href="' + htmlEscape(event.url) + '"' :
|
|
''
|
|
) +
|
|
(skinCss ?
|
|
' style="' + skinCss + '"' :
|
|
''
|
|
) +
|
|
'>' +
|
|
'<div class="fc-content">' +
|
|
(isRTL ?
|
|
titleHtml + ' ' + timeHtml : // put a natural space in between
|
|
timeHtml + ' ' + titleHtml //
|
|
) +
|
|
'</div>' +
|
|
(isResizable ?
|
|
'<div class="fc-resizer"/>' :
|
|
''
|
|
) +
|
|
'</a>';
|
|
},
|
|
|
|
|
|
// Given a row # and an array of segments all in the same row, render a <tbody> element, a skeleton that contains
|
|
// the segments. Returns object with a bunch of internal data about how the render was calculated.
|
|
renderEventRow: function(row, rowSegs) {
|
|
var view = this.view;
|
|
var colCnt = view.colCnt;
|
|
var segLevels = this.buildSegLevels(rowSegs); // group into sub-arrays of levels
|
|
var levelCnt = Math.max(1, segLevels.length); // ensure at least one level
|
|
var tbody = $('<tbody/>');
|
|
var segMatrix = []; // lookup for which segments are rendered into which level+col cells
|
|
var cellMatrix = []; // lookup for all <td> elements of the level+col matrix
|
|
var loneCellMatrix = []; // lookup for <td> elements that only take up a single column
|
|
var i, levelSegs;
|
|
var col;
|
|
var tr;
|
|
var j, seg;
|
|
var td;
|
|
|
|
// populates empty cells from the current column (`col`) to `endCol`
|
|
function emptyCellsUntil(endCol) {
|
|
while (col < endCol) {
|
|
// try to grab a cell from the level above and extend its rowspan. otherwise, create a fresh cell
|
|
td = (loneCellMatrix[i - 1] || [])[col];
|
|
if (td) {
|
|
td.attr(
|
|
'rowspan',
|
|
parseInt(td.attr('rowspan') || 1, 10) + 1
|
|
);
|
|
}
|
|
else {
|
|
td = $('<td/>');
|
|
tr.append(td);
|
|
}
|
|
cellMatrix[i][col] = td;
|
|
loneCellMatrix[i][col] = td;
|
|
col++;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < levelCnt; i++) { // iterate through all levels
|
|
levelSegs = segLevels[i];
|
|
col = 0;
|
|
tr = $('<tr/>');
|
|
|
|
segMatrix.push([]);
|
|
cellMatrix.push([]);
|
|
loneCellMatrix.push([]);
|
|
|
|
// levelCnt might be 1 even though there are no actual levels. protect against this.
|
|
// this single empty row is useful for styling.
|
|
if (levelSegs) {
|
|
for (j = 0; j < levelSegs.length; j++) { // iterate through segments in level
|
|
seg = levelSegs[j];
|
|
|
|
emptyCellsUntil(seg.leftCol);
|
|
|
|
// create a container that occupies or more columns. append the event element.
|
|
td = $('<td class="fc-event-container"/>').append(seg.el);
|
|
if (seg.leftCol != seg.rightCol) {
|
|
td.attr('colspan', seg.rightCol - seg.leftCol + 1);
|
|
}
|
|
else { // a single-column segment
|
|
loneCellMatrix[i][col] = td;
|
|
}
|
|
|
|
while (col <= seg.rightCol) {
|
|
cellMatrix[i][col] = td;
|
|
segMatrix[i][col] = seg;
|
|
col++;
|
|
}
|
|
|
|
tr.append(td);
|
|
}
|
|
}
|
|
|
|
emptyCellsUntil(colCnt); // finish off the row
|
|
this.bookendCells(tr, 'eventSkeleton');
|
|
tbody.append(tr);
|
|
}
|
|
|
|
return { // a "rowStruct"
|
|
row: row, // the row number
|
|
tbodyEl: tbody,
|
|
cellMatrix: cellMatrix,
|
|
segMatrix: segMatrix,
|
|
segLevels: segLevels,
|
|
segs: rowSegs
|
|
};
|
|
},
|
|
|
|
|
|
// Stacks a flat array of segments, which are all assumed to be in the same row, into subarrays of vertical levels.
|
|
buildSegLevels: function(segs) {
|
|
var levels = [];
|
|
var i, seg;
|
|
var j;
|
|
|
|
// Give preference to elements with certain criteria, so they have
|
|
// a chance to be closer to the top.
|
|
segs.sort(compareSegs);
|
|
|
|
for (i = 0; i < segs.length; i++) {
|
|
seg = segs[i];
|
|
|
|
// loop through levels, starting with the topmost, until the segment doesn't collide with other segments
|
|
for (j = 0; j < levels.length; j++) {
|
|
if (!isDaySegCollision(seg, levels[j])) {
|
|
break;
|
|
}
|
|
}
|
|
// `j` now holds the desired subrow index
|
|
seg.level = j;
|
|
|
|
// create new level array if needed and append segment
|
|
(levels[j] || (levels[j] = [])).push(seg);
|
|
}
|
|
|
|
// order segments left-to-right. very important if calendar is RTL
|
|
for (j = 0; j < levels.length; j++) {
|
|
levels[j].sort(compareDaySegCols);
|
|
}
|
|
|
|
return levels;
|
|
},
|
|
|
|
|
|
// Given a flat array of segments, return an array of sub-arrays, grouped by each segment's row
|
|
groupSegRows: function(segs) {
|
|
var view = this.view;
|
|
var segRows = [];
|
|
var i;
|
|
|
|
for (i = 0; i < view.rowCnt; i++) {
|
|
segRows.push([]);
|
|
}
|
|
|
|
for (i = 0; i < segs.length; i++) {
|
|
segRows[segs[i].row].push(segs[i]);
|
|
}
|
|
|
|
return segRows;
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// Computes whether two segments' columns collide. They are assumed to be in the same row.
|
|
function isDaySegCollision(seg, otherSegs) {
|
|
var i, otherSeg;
|
|
|
|
for (i = 0; i < otherSegs.length; i++) {
|
|
otherSeg = otherSegs[i];
|
|
|
|
if (
|
|
otherSeg.leftCol <= seg.rightCol &&
|
|
otherSeg.rightCol >= seg.leftCol
|
|
) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// A cmp function for determining the leftmost event
|
|
function compareDaySegCols(a, b) {
|
|
return a.leftCol - b.leftCol;
|
|
}
|