mirror of
https://github.com/wassname/fullcalendar.git
synced 2026-08-03 12:50:21 +08:00
new algorithm for laying out agenda events. slotEventOverlap option
This commit is contained in:
@@ -28,7 +28,6 @@ function AgendaEventRenderer() {
|
||||
var colContentLeft = t.colContentLeft;
|
||||
var colContentRight = t.colContentRight;
|
||||
var cellToDate = t.cellToDate;
|
||||
var segmentCompare = t.segmentCompare;
|
||||
var getColCnt = t.getColCnt;
|
||||
var getColWidth = t.getColWidth;
|
||||
var getSnapHeight = t.getSnapHeight;
|
||||
@@ -89,35 +88,32 @@ function AgendaEventRenderer() {
|
||||
maxMinute = getMaxMinute(),
|
||||
d,
|
||||
visEventEnds = $.map(events, slotEventEnd),
|
||||
i, col,
|
||||
j, level,
|
||||
k, seg,
|
||||
i,
|
||||
j, seg,
|
||||
colSegs,
|
||||
segs = [];
|
||||
|
||||
for (i=0; i<colCnt; i++) {
|
||||
|
||||
d = cellToDate(0, i);
|
||||
addMinutes(d, minMinute);
|
||||
|
||||
col = stackAgendaSegs(
|
||||
sliceSegs(
|
||||
events,
|
||||
visEventEnds,
|
||||
d,
|
||||
addMinutes(cloneDate(d), maxMinute-minMinute)
|
||||
)
|
||||
colSegs = sliceSegs(
|
||||
events,
|
||||
visEventEnds,
|
||||
d,
|
||||
addMinutes(cloneDate(d), maxMinute-minMinute)
|
||||
);
|
||||
countForwardSegs(col);
|
||||
|
||||
for (j=0; j<col.length; j++) {
|
||||
level = col[j];
|
||||
for (k=0; k<level.length; k++) {
|
||||
seg = level[k];
|
||||
seg.col = i;
|
||||
seg.level = j;
|
||||
segs.push(seg);
|
||||
}
|
||||
colSegs = placeSlotSegs(colSegs); // returns a new order
|
||||
|
||||
for (j=0; j<colSegs.length; j++) {
|
||||
seg = colSegs[j];
|
||||
seg.col = i;
|
||||
segs.push(seg);
|
||||
}
|
||||
}
|
||||
|
||||
return segs;
|
||||
}
|
||||
|
||||
@@ -152,12 +148,11 @@ function AgendaEventRenderer() {
|
||||
start: segStart,
|
||||
end: segEnd,
|
||||
isStart: isStart,
|
||||
isEnd: isEnd,
|
||||
msLength: segEnd - segStart
|
||||
isEnd: isEnd
|
||||
});
|
||||
}
|
||||
}
|
||||
return segs.sort(segmentCompare);
|
||||
return segs.sort(compareSlotSegs);
|
||||
}
|
||||
|
||||
|
||||
@@ -178,27 +173,22 @@ function AgendaEventRenderer() {
|
||||
|
||||
var i, segCnt=segs.length, seg,
|
||||
event,
|
||||
classes,
|
||||
top, bottom,
|
||||
colI, levelI, forward,
|
||||
leftmost,
|
||||
availWidth,
|
||||
outerWidth,
|
||||
top,
|
||||
bottom,
|
||||
columnLeft,
|
||||
columnRight,
|
||||
columnWidth,
|
||||
width,
|
||||
left,
|
||||
html='',
|
||||
right,
|
||||
html = '',
|
||||
eventElements,
|
||||
eventElement,
|
||||
triggerRes,
|
||||
titleElement,
|
||||
height,
|
||||
slotSegmentContainer = getSlotSegmentContainer(),
|
||||
rtl, dis;
|
||||
|
||||
if (rtl = opt('isRTL')) {
|
||||
dis = -1;
|
||||
}else{
|
||||
dis = 1;
|
||||
}
|
||||
isRTL = opt('isRTL');
|
||||
|
||||
// calculate position/dimensions, create html
|
||||
for (i=0; i<segCnt; i++) {
|
||||
@@ -206,33 +196,48 @@ function AgendaEventRenderer() {
|
||||
event = seg.event;
|
||||
top = timePosition(seg.start, seg.start);
|
||||
bottom = timePosition(seg.start, seg.end);
|
||||
colI = seg.col;
|
||||
levelI = seg.level;
|
||||
forward = seg.forward || 0;
|
||||
leftmost = colContentLeft(colI);
|
||||
availWidth = colContentRight(colI) - leftmost;
|
||||
availWidth = Math.min(availWidth-6, availWidth*.95); // TODO: move this to CSS
|
||||
if (levelI) {
|
||||
// indented and thin
|
||||
outerWidth = availWidth / (levelI + forward + 1);
|
||||
}else{
|
||||
if (forward) {
|
||||
// moderately wide, aligned left still
|
||||
outerWidth = ((availWidth / (forward + 1)) - (12/2)) * 2; // 12 is the predicted width of resizer =
|
||||
}else{
|
||||
// can be entire width, aligned left
|
||||
outerWidth = availWidth;
|
||||
}
|
||||
columnLeft = colContentLeft(seg.col);
|
||||
columnRight = colContentRight(seg.col);
|
||||
columnWidth = columnRight - columnLeft;
|
||||
|
||||
// shave off space on right near scrollbars (2.5%)
|
||||
// TODO: move this to CSS somehow
|
||||
columnRight -= columnWidth * .025;
|
||||
columnWidth = columnRight - columnLeft;
|
||||
|
||||
width = columnWidth * (seg.forwardCoord - seg.backwardCoord);
|
||||
|
||||
if (opt('slotEventOverlap')) {
|
||||
// double the width while making sure resize handle is visible
|
||||
// (assumed to be 20px wide)
|
||||
width = Math.max(
|
||||
(width - (20/2)) * 2,
|
||||
width // narrow columns will want to make the segment smaller than
|
||||
// the natural width. don't allow it
|
||||
);
|
||||
}
|
||||
left = leftmost + // leftmost possible
|
||||
(availWidth / (levelI + forward + 1) * levelI) // indentation
|
||||
* dis + (rtl ? availWidth - outerWidth : 0); // rtl
|
||||
|
||||
if (isRTL) {
|
||||
right = columnRight - seg.backwardCoord * columnWidth;
|
||||
left = right - width;
|
||||
}
|
||||
else {
|
||||
left = columnLeft + seg.backwardCoord * columnWidth;
|
||||
right = left + width;
|
||||
}
|
||||
|
||||
// make sure horizontal coordinates are in bounds
|
||||
left = Math.max(left, columnLeft);
|
||||
right = Math.min(right, columnRight);
|
||||
width = right - left;
|
||||
|
||||
seg.top = top;
|
||||
seg.left = left;
|
||||
seg.outerWidth = outerWidth;
|
||||
seg.outerWidth = width;
|
||||
seg.outerHeight = bottom - top;
|
||||
html += slotSegHtml(event, seg);
|
||||
}
|
||||
|
||||
slotSegmentContainer[0].innerHTML = html; // faster than html()
|
||||
eventElements = slotSegmentContainer.children();
|
||||
|
||||
@@ -678,60 +683,205 @@ function AgendaEventRenderer() {
|
||||
|
||||
/* Agenda Event Segment Utilities
|
||||
-----------------------------------------------------------------------------*/
|
||||
// TODO: maybe somehow consolidate this with DayEventRenderer's segment system
|
||||
|
||||
|
||||
function stackAgendaSegs(segs) {
|
||||
var levels = [],
|
||||
i, len = segs.length, seg,
|
||||
j, collide, k;
|
||||
for (i=0; i<len; i++) {
|
||||
// Sets the seg.backwardCoord and seg.forwardCoord on each segment and returns a new
|
||||
// list in the order they should be placed into the DOM (an implicit z-index).
|
||||
function placeSlotSegs(segs) {
|
||||
var levels = buildSlotSegLevels(segs);
|
||||
var level0 = levels[0];
|
||||
var i;
|
||||
|
||||
computeForwardSlotSegs(levels);
|
||||
|
||||
if (level0) {
|
||||
|
||||
for (i=0; i<level0.length; i++) {
|
||||
computeSlotSegPressures(level0[i]);
|
||||
}
|
||||
|
||||
for (i=0; i<level0.length; i++) {
|
||||
computeSlotSegCoords(level0[i], 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return flattenSlotSegLevels(levels);
|
||||
}
|
||||
|
||||
|
||||
// Builds an array of segments "levels". The first level will be the leftmost tier of segments
|
||||
// if the calendar is left-to-right, or the rightmost if the calendar is right-to-left.
|
||||
function buildSlotSegLevels(segs) {
|
||||
var levels = [];
|
||||
var i, seg;
|
||||
var j;
|
||||
|
||||
for (i=0; i<segs.length; i++) {
|
||||
seg = segs[i];
|
||||
j = 0; // the level index where seg should belong
|
||||
while (true) {
|
||||
collide = false;
|
||||
if (levels[j]) {
|
||||
for (k=0; k<levels[j].length; k++) {
|
||||
if (agendaSegsCollide(levels[j][k], seg)) {
|
||||
collide = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (collide) {
|
||||
j++;
|
||||
}else{
|
||||
|
||||
// go through all the levels and stop on the first level where there are no collisions
|
||||
for (j=0; j<levels.length; j++) {
|
||||
if (!computeSlotSegCollisions(seg, levels[j]).length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (levels[j]) {
|
||||
levels[j].push(seg);
|
||||
}else{
|
||||
levels[j] = [seg];
|
||||
}
|
||||
|
||||
(levels[j] || (levels[j] = [])).push(seg);
|
||||
}
|
||||
|
||||
return levels;
|
||||
}
|
||||
|
||||
|
||||
function countForwardSegs(levels) {
|
||||
var i, j, k, level, segForward, segBack;
|
||||
for (i=levels.length-1; i>0; i--) {
|
||||
// For every segment, figure out the other segments that are in subsequent
|
||||
// levels that also occupy the same vertical space. Accumulate in seg.forwardSegs
|
||||
function computeForwardSlotSegs(levels) {
|
||||
var i, level;
|
||||
var j, seg;
|
||||
var k;
|
||||
|
||||
for (i=0; i<levels.length; i++) {
|
||||
level = levels[i];
|
||||
|
||||
for (j=0; j<level.length; j++) {
|
||||
segForward = level[j];
|
||||
for (k=0; k<levels[i-1].length; k++) {
|
||||
segBack = levels[i-1][k];
|
||||
if (agendaSegsCollide(segForward, segBack)) {
|
||||
segBack.forward = Math.max(segBack.forward||0, (segForward.forward||0)+1);
|
||||
}
|
||||
seg = level[j];
|
||||
|
||||
seg.forwardSegs = [];
|
||||
for (k=i+1; k<levels.length; k++) {
|
||||
computeSlotSegCollisions(seg, levels[k], seg.forwardSegs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function agendaSegsCollide(seg1, seg2) {
|
||||
// Figure out which path forward (via seg.forwardSegs) results in the longest path until
|
||||
// the furthest edge is reached. The number of segments in this path will be seg.forwardPressure
|
||||
function computeSlotSegPressures(seg) {
|
||||
var forwardSegs = seg.forwardSegs;
|
||||
var forwardPressure = 0;
|
||||
var i, forwardSeg;
|
||||
|
||||
if (seg.forwardPressure === undefined) { // not already computed
|
||||
|
||||
for (i=0; i<forwardSegs.length; i++) {
|
||||
forwardSeg = forwardSegs[i];
|
||||
|
||||
// figure out the child's maximum forward path
|
||||
computeSlotSegPressures(forwardSeg);
|
||||
|
||||
// either use the existing maximum, or use the child's forward pressure
|
||||
// plus one (for the forwardSeg itself)
|
||||
forwardPressure = Math.max(
|
||||
forwardPressure,
|
||||
1 + forwardSeg.forwardPressure
|
||||
);
|
||||
}
|
||||
|
||||
seg.forwardPressure = forwardPressure;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range
|
||||
// from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and
|
||||
// seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left.
|
||||
//
|
||||
// The segment might be part of a "series", which means consecutive segments with the same pressure
|
||||
// who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of
|
||||
// segments behind this one in the current series, and `seriesBackwardCoord` is the starting
|
||||
// coordinate of the first segment in the series.
|
||||
function computeSlotSegCoords(seg, seriesBackwardPressure, seriesBackwardCoord) {
|
||||
var forwardSegs = seg.forwardSegs;
|
||||
var i;
|
||||
|
||||
if (seg.forwardCoord === undefined) { // not already computed
|
||||
|
||||
if (!forwardSegs.length) {
|
||||
|
||||
// if there are no forward segments, this segment should butt up against the edge
|
||||
seg.forwardCoord = 1;
|
||||
}
|
||||
else {
|
||||
|
||||
// sort highest pressure first
|
||||
forwardSegs.sort(compareForwardSlotSegs);
|
||||
|
||||
// this segment's forwardCoord will be calculated from the backwardCoord of the
|
||||
// highest-pressure forward segment.
|
||||
computeSlotSegCoords(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
|
||||
seg.forwardCoord = forwardSegs[0].backwardCoord;
|
||||
}
|
||||
|
||||
// calculate the backwardCoord from the forwardCoord. consider the series
|
||||
seg.backwardCoord = seg.forwardCoord -
|
||||
(seg.forwardCoord - seriesBackwardCoord) / // available width for series
|
||||
(seriesBackwardPressure + 1); // # of segments in the series
|
||||
|
||||
// use this segment's coordinates to computed the coordinates of the less-pressurized
|
||||
// forward segments
|
||||
for (i=0; i<forwardSegs.length; i++) {
|
||||
computeSlotSegCoords(forwardSegs[i], 0, seg.forwardCoord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Outputs a flat array of segments, from lowest to highest level
|
||||
function flattenSlotSegLevels(levels) {
|
||||
var segs = [];
|
||||
var i, level;
|
||||
var j;
|
||||
|
||||
for (i=0; i<levels.length; i++) {
|
||||
level = levels[i];
|
||||
|
||||
for (j=0; j<level.length; j++) {
|
||||
segs.push(level[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return segs;
|
||||
}
|
||||
|
||||
|
||||
// Find all the segments in `otherSegs` that vertically collide with `seg`.
|
||||
// Append into an optionally-supplied `results` array and return.
|
||||
function computeSlotSegCollisions(seg, otherSegs, results) {
|
||||
results = results || [];
|
||||
|
||||
for (var i=0; i<otherSegs.length; i++) {
|
||||
if (isSlotSegCollision(seg, otherSegs[i])) {
|
||||
results.push(otherSegs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
// Do these segments occupy the same vertical space?
|
||||
function isSlotSegCollision(seg1, seg2) {
|
||||
return seg1.end > seg2.start && seg1.start < seg2.end;
|
||||
}
|
||||
|
||||
|
||||
// A cmp function for determining which forward segment to rely on more when computing coordinates.
|
||||
function compareForwardSlotSegs(seg1, seg2) {
|
||||
// put higher-pressure first
|
||||
return seg2.forwardPressure - seg1.forwardPressure ||
|
||||
// put segments that are closer to initial edge first (and favor ones with no coords yet)
|
||||
(seg1.backwardCoord || 0) - (seg2.backwardCoord || 0) ||
|
||||
// do normal sorting...
|
||||
compareSlotSegs(seg1, seg2);
|
||||
}
|
||||
|
||||
|
||||
// A cmp function for determining which segment should be closer to the initial edge
|
||||
// (the left edge on a left-to-right calendar).
|
||||
function compareSlotSegs(seg1, seg2) {
|
||||
return seg1.start - seg2.start || // earlier start time goes first
|
||||
(seg2.end - seg2.start) - (seg1.end - seg1.start) || // tie? longer-duration goes first
|
||||
(seg1.event.title || '').localeCompare(seg2.event.title); // tie? alphabetically by title
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ setDefaults({
|
||||
agenda: .5
|
||||
},
|
||||
minTime: 0,
|
||||
maxTime: 24
|
||||
maxTime: 24,
|
||||
slotEventOverlap: true
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ function DayEventRenderer() {
|
||||
var clearSelection = t.clearSelection;
|
||||
var getHoverListener = t.getHoverListener;
|
||||
var rangeToSegments = t.rangeToSegments;
|
||||
var segmentCompare = t.segmentCompare;
|
||||
var cellToDate = t.cellToDate;
|
||||
var cellToCellOffset = t.cellToCellOffset;
|
||||
var cellOffsetToDayOffset = t.cellOffsetToDayOffset;
|
||||
@@ -485,7 +484,7 @@ function DayEventRenderer() {
|
||||
|
||||
// Give preference to elements with certain criteria, so they have
|
||||
// a chance to be closer to the top.
|
||||
segments.sort(segmentCompare);
|
||||
segments.sort(compareDaySegments);
|
||||
|
||||
var subrows = [];
|
||||
for (var i=0; i<segments.length; i++) {
|
||||
@@ -740,3 +739,13 @@ function segmentElementEach(segments, callback) { // TODO: use in AgendaView?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// A cmp function for determining which segments should appear higher up
|
||||
function compareDaySegments(a, b) {
|
||||
return (b.rightCol - b.leftCol) - (a.rightCol - a.leftCol) || // put wider events first
|
||||
b.event.allDay - a.event.allDay || // if tie, put all-day events first (booleans cast to 0/1)
|
||||
a.event.start - b.event.start || // if a tie, sort by event start date
|
||||
(a.event.title || '').localeCompare(b.event.title) // if a tie, sort by event title
|
||||
}
|
||||
|
||||
|
||||
@@ -307,7 +307,6 @@ function View(element, calendar, viewName) {
|
||||
t.cellOffsetToDayOffset = cellOffsetToDayOffset;
|
||||
t.dayOffsetToDate = dayOffsetToDate;
|
||||
t.rangeToSegments = rangeToSegments;
|
||||
t.segmentCompare = segmentCompare;
|
||||
|
||||
|
||||
// internals
|
||||
@@ -536,30 +535,6 @@ function View(element, calendar, viewName) {
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
|
||||
// Compare two event segments and determine which one takes priority (ex: rendered topmost/leftmost)
|
||||
// NOTE: only works with segments that have `event` properties!
|
||||
//
|
||||
// Returns a negative value if `a` should be first.
|
||||
// Returns a positive value of `b` should be first.
|
||||
function segmentCompare(a, b) {
|
||||
return _segmentCompare(a, b) // sort by dimension
|
||||
|| (a.event.start - b.event.start) // if a tie, sort by event start date
|
||||
|| (a.event.title || "").localeCompare(b.event.title) // if a tie, sort by event title
|
||||
}
|
||||
|
||||
// compare dimensions
|
||||
// NOTE: this is not modular! depends on subclass-specific segment schemas
|
||||
function _segmentCompare(a, b) {
|
||||
if ('msLength' in a) {
|
||||
// segment generated by AgendaEventRenderer
|
||||
return b.msLength - a.msLength; // put taller events first
|
||||
}
|
||||
// segment generated by DayEventRenderer
|
||||
return (b.rightCol - b.leftCol) - (a.rightCol - a.leftCol) // put wider events first
|
||||
|| b.event.allDay - a.event.allDay; // if tie, put all-day events first (booleans cast to 0/1)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link href='../build/out/fullcalendar.css' rel='stylesheet' />
|
||||
<link href='../build/out/fullcalendar.print.css' rel='stylesheet' media='print' />
|
||||
<script src='../build/out/jquery.js'></script>
|
||||
<script src='../build/out/jquery-ui.js'></script>
|
||||
<script src='../build/out/fullcalendar.js'></script>
|
||||
<script>
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#calendar').fullCalendar({
|
||||
header: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'month,agendaWeek,basicWeek,agendaDay,basicDay'
|
||||
},
|
||||
editable: true,
|
||||
defaultView: 'agendaDay',
|
||||
firstHour: 0,
|
||||
year: 2013,
|
||||
month: 6, // July
|
||||
date: 31,
|
||||
events: 'slot_event_overlap.json',
|
||||
eventAfterAllRender: function() {
|
||||
testOverlap($('#calendar'), true, false);
|
||||
}
|
||||
});
|
||||
|
||||
$('#calendar-nooverlap').fullCalendar({
|
||||
header: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'month,agendaWeek,basicWeek,agendaDay,basicDay'
|
||||
},
|
||||
editable: true,
|
||||
defaultView: 'agendaDay',
|
||||
firstHour: 0,
|
||||
year: 2013,
|
||||
month: 6, // July
|
||||
date: 31,
|
||||
events: 'slot_event_overlap.json',
|
||||
slotEventOverlap: false,
|
||||
eventAfterAllRender: function() {
|
||||
testOverlap($('#calendar-nooverlap'), false, false);
|
||||
}
|
||||
});
|
||||
|
||||
$('#calendar-rtl').fullCalendar({
|
||||
header: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'month,agendaWeek,basicWeek,agendaDay,basicDay'
|
||||
},
|
||||
editable: true,
|
||||
defaultView: 'agendaDay',
|
||||
firstHour: 0,
|
||||
year: 2013,
|
||||
month: 6, // July
|
||||
date: 31,
|
||||
events: 'slot_event_overlap.json',
|
||||
isRTL: true,
|
||||
eventAfterAllRender: function() {
|
||||
testOverlap($('#calendar-rtl'), true, true);
|
||||
}
|
||||
});
|
||||
|
||||
$('#calendar-rtl-nooverlap').fullCalendar({
|
||||
header: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'month,agendaWeek,basicWeek,agendaDay,basicDay'
|
||||
},
|
||||
editable: true,
|
||||
defaultView: 'agendaDay',
|
||||
firstHour: 0,
|
||||
year: 2013,
|
||||
month: 6, // July
|
||||
date: 31,
|
||||
events: 'slot_event_overlap.json',
|
||||
isRTL: true,
|
||||
slotEventOverlap: false,
|
||||
eventAfterAllRender: function() {
|
||||
testOverlap($('#calendar-rtl-nooverlap'), false, true);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function testOverlap(el, allowOverlap, isRTL) {
|
||||
if (_testOverlap(el, allowOverlap, isRTL)) {
|
||||
el.prev('h2').find('span').css('color', 'green').text('passed');
|
||||
}
|
||||
else {
|
||||
el.prev('h2').find('span').css('color', 'red').text('failed');
|
||||
}
|
||||
}
|
||||
|
||||
function _testOverlap(el, allowOverlap, isRTL) {
|
||||
var events = el.find('.fc-event');
|
||||
|
||||
for (var i=0; i<events.length; i++) {
|
||||
|
||||
var event = $(events[i]);
|
||||
var offset = event.offset();
|
||||
var top = Math.ceil(offset.top);
|
||||
var left = Math.ceil(offset.left);
|
||||
var width = Math.floor(event.outerWidth());
|
||||
var height = Math.floor(event.outerHeight());
|
||||
|
||||
if (allowOverlap) {
|
||||
width /= 2; // make sure nothing overlaps the first half of the event
|
||||
if (isRTL) {
|
||||
left += width;
|
||||
}
|
||||
}
|
||||
|
||||
for (var j=i+1; j<events.length; j++) {
|
||||
// only test again events that are ahead in the DOM, meaning they have a higher
|
||||
// implicit z-index and an top of the current event
|
||||
|
||||
var otherEvent = $(events[j]);
|
||||
var otherOffset = otherEvent.offset();
|
||||
var otherTop = Math.ceil(otherOffset.top);
|
||||
var otherLeft = Math.ceil(otherOffset.left);
|
||||
var otherWidth = Math.floor(otherEvent.outerWidth());
|
||||
var otherHeight = Math.floor(otherEvent.outerHeight());
|
||||
|
||||
if (
|
||||
top < otherTop + otherHeight &&
|
||||
top + height > otherTop &&
|
||||
left < otherLeft + otherWidth &&
|
||||
left + width > otherLeft
|
||||
) {
|
||||
console.log('failed on', event[0], otherEvent[0]);
|
||||
return false; // a collision
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!allowOverlap) {
|
||||
// we know of certain events that should have the same width
|
||||
// because they share a liquid area...
|
||||
|
||||
var equalwidth1 = events.filter('.equalwidth1');
|
||||
if (equalwidth1.eq(0).outerWidth() - equalwidth1.eq(1).outerWidth() > 1) {
|
||||
console.log('not equal width', equalwidth1.toArray());
|
||||
return false;
|
||||
}
|
||||
|
||||
var equalwidth2 = events.filter('.equalwidth2');
|
||||
if (equalwidth2.eq(0).outerWidth() - equalwidth2.eq(1).outerWidth() > 1) {
|
||||
console.log('not equal width', equalwidth2.toArray());
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
</script>
|
||||
<style>
|
||||
|
||||
body {
|
||||
font-size: 13px;
|
||||
font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
width: 900px;
|
||||
margin: 50px auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2>Default: <span></span></h2>
|
||||
<div id='calendar' class='calendar'></div>
|
||||
|
||||
<h2>No overlap: <span></span></h2>
|
||||
<div id='calendar-nooverlap' class='calendar'></div>
|
||||
|
||||
<h2>RTL: <span></span></h2>
|
||||
<div id='calendar-rtl' class='calendar'></div>
|
||||
|
||||
<h2>RTL, no overlap: <span></span></h2>
|
||||
<div id='calendar-rtl-nooverlap' class='calendar'></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,102 @@
|
||||
[
|
||||
{
|
||||
"title": "1",
|
||||
"start": "2013-07-31T00:30",
|
||||
"end": "2013-07-31T02:30",
|
||||
"allDay": false
|
||||
},
|
||||
{
|
||||
"title": "2",
|
||||
"start": "2013-07-31T00:30",
|
||||
"end": "2013-07-31T02:30",
|
||||
"allDay": false
|
||||
},
|
||||
{
|
||||
"title": "3",
|
||||
"start": "2013-07-31T02:30",
|
||||
"end": "2013-07-31T04:30",
|
||||
"allDay": false
|
||||
},
|
||||
{
|
||||
"title": "4",
|
||||
"start": "2013-07-31T02:30",
|
||||
"end": "2013-07-31T04:30",
|
||||
"allDay": false
|
||||
},
|
||||
{
|
||||
"title": "5",
|
||||
"start": "2013-07-31T02:30",
|
||||
"end": "2013-07-31T04:30",
|
||||
"allDay": false
|
||||
},
|
||||
{
|
||||
"title": "6",
|
||||
"start": "2013-07-31T01:00",
|
||||
"end": "2013-07-31T03:00",
|
||||
"allDay": false
|
||||
},
|
||||
{
|
||||
"title": "7",
|
||||
"start": "2013-07-31T01:30",
|
||||
"end": "2013-07-31T03:30",
|
||||
"allDay": false
|
||||
},
|
||||
{
|
||||
"title": "8",
|
||||
"start": "2013-07-31T02:50",
|
||||
"end": "2013-07-31T04:50",
|
||||
"allDay": false
|
||||
},
|
||||
{
|
||||
"title": "9",
|
||||
"start": "2013-07-31T04:30",
|
||||
"end": "2013-07-31T09:00",
|
||||
"allDay": false
|
||||
},
|
||||
{
|
||||
"title": "10",
|
||||
"start": "2013-07-31T02:00",
|
||||
"end": "2013-07-31T04:05",
|
||||
"allDay": false
|
||||
},
|
||||
{
|
||||
"title": "11",
|
||||
"start": "2013-07-31T02:50",
|
||||
"end": "2013-07-31T04:50",
|
||||
"allDay": false
|
||||
},
|
||||
{
|
||||
"title": "12",
|
||||
"start": "2013-07-31T02:50",
|
||||
"end": "2013-07-31T04:50",
|
||||
"allDay": false
|
||||
},
|
||||
{
|
||||
"title": "13",
|
||||
"start": "2013-07-31T04:00",
|
||||
"end": "2013-07-31T06:00",
|
||||
"allDay": false,
|
||||
"className": "equalwidth1"
|
||||
},
|
||||
{
|
||||
"title": "14",
|
||||
"start": "2013-07-31T04:10",
|
||||
"end": "2013-07-31T06:09",
|
||||
"allDay": false,
|
||||
"className": "equalwidth1"
|
||||
},
|
||||
{
|
||||
"title": "15",
|
||||
"start": "2013-07-31T06:30",
|
||||
"end": "2013-07-31T08:30",
|
||||
"allDay": false,
|
||||
"className": "equalwidth2"
|
||||
},
|
||||
{
|
||||
"title": "16",
|
||||
"start": "2013-07-31T06:30",
|
||||
"end": "2013-07-31T08:30",
|
||||
"allDay": false,
|
||||
"className": "equalwidth2"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user