mirror of
https://github.com/wassname/fullcalendar.git
synced 2026-07-09 00:20:27 +08:00
755 lines
20 KiB
JavaScript
755 lines
20 KiB
JavaScript
|
|
function ResourceEventRenderer() {
|
|
var t = this;
|
|
|
|
|
|
// exports
|
|
t.renderEvents = renderEvents;
|
|
t.clearEvents = clearEvents;
|
|
t.slotSegHtml = slotSegHtml;
|
|
|
|
|
|
// imports
|
|
DayEventRenderer.call(t);
|
|
var opt = t.opt;
|
|
var trigger = t.trigger;
|
|
var isEventDraggable = t.isEventDraggable;
|
|
var isEventResizable = t.isEventResizable;
|
|
var eventEnd = t.eventEnd;
|
|
var eventElementHandlers = t.eventElementHandlers;
|
|
var setHeight = t.setHeight;
|
|
var getDaySegmentContainer = t.getDaySegmentContainer;
|
|
var getSlotSegmentContainer = t.getSlotSegmentContainer;
|
|
var getHoverListener = t.getHoverListener;
|
|
var getMaxMinute = t.getMaxMinute;
|
|
var getMinMinute = t.getMinMinute;
|
|
var timePosition = t.timePosition;
|
|
var getIsCellAllDay = t.getIsCellAllDay;
|
|
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;
|
|
var getSnapMinutes = t.getSnapMinutes;
|
|
var getSlotContainer = t.getSlotContainer;
|
|
var reportEventElement = t.reportEventElement;
|
|
var showEvents = t.showEvents;
|
|
var hideEvents = t.hideEvents;
|
|
var eventDrop = t.eventDrop;
|
|
var eventResize = t.eventResize;
|
|
var renderDayOverlay = t.renderDayOverlay;
|
|
var clearOverlays = t.clearOverlays;
|
|
var renderDayEvents = t.renderDayEvents;
|
|
var calendar = t.calendar;
|
|
var formatDate = calendar.formatDate;
|
|
var formatDates = calendar.formatDates;
|
|
var resources = t.getResources;
|
|
|
|
|
|
// overrides
|
|
t.draggableDayEvent = draggableDayEvent;
|
|
|
|
|
|
|
|
/* Rendering
|
|
----------------------------------------------------------------------------*/
|
|
|
|
|
|
function renderEvents(events, modifiedEventId) {
|
|
var i, len=events.length,
|
|
dayEvents=[],
|
|
slotEvents=[];
|
|
for (i=0; i<len; i++) {
|
|
if (events[i].allDay) {
|
|
dayEvents.push(events[i]);
|
|
}else{
|
|
slotEvents.push(events[i]);
|
|
}
|
|
}
|
|
|
|
if (opt('allDaySlot')) {
|
|
renderDayEvents(dayEvents, modifiedEventId);
|
|
setHeight(); // no params means set to viewHeight
|
|
}
|
|
|
|
renderSlotSegs(compileSlotSegs(slotEvents), modifiedEventId);
|
|
}
|
|
|
|
|
|
function clearEvents() {
|
|
getDaySegmentContainer().empty();
|
|
getSlotSegmentContainer().empty();
|
|
}
|
|
|
|
|
|
function compileSlotSegs(events) {
|
|
var colCnt = getColCnt(),
|
|
minMinute = getMinMinute(),
|
|
maxMinute = getMaxMinute(),
|
|
d,
|
|
visEventEnds,
|
|
i, col,
|
|
j, level,
|
|
k, seg,
|
|
segs = [];
|
|
for (i=0; i<colCnt; i++) {
|
|
|
|
d = t.visStart.clone();
|
|
d.add('m', minMinute);
|
|
|
|
var resourceEvents = eventsForResource(resources()[i], events);
|
|
visEventEnds = $.map(resourceEvents, slotEventEnd);
|
|
col = stackAgendaSegs(
|
|
sliceSegs(
|
|
resourceEvents,
|
|
visEventEnds,
|
|
d,
|
|
d.clone().add('m', 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);
|
|
}
|
|
}
|
|
}
|
|
return segs;
|
|
}
|
|
|
|
|
|
function sliceSegs(events, visEventEnds, start, end) {
|
|
var segs = [],
|
|
i, len=events.length, event,
|
|
eventStart, eventEnd,
|
|
segStart, segEnd,
|
|
isStart, isEnd;
|
|
for (i=0; i<len; i++) {
|
|
event = events[i];
|
|
eventStart = event.start;
|
|
eventEnd = visEventEnds[i];
|
|
if (eventEnd > start && eventStart < end) {
|
|
if (eventStart < start) {
|
|
segStart = start.clone();
|
|
isStart = false;
|
|
}else{
|
|
segStart = eventStart;
|
|
isStart = true;
|
|
}
|
|
if (eventEnd > end) {
|
|
segEnd = end.clone();
|
|
isEnd = false;
|
|
}else{
|
|
segEnd = eventEnd;
|
|
isEnd = true;
|
|
}
|
|
segs.push({
|
|
event: event,
|
|
start: segStart,
|
|
end: segEnd,
|
|
isStart: isStart,
|
|
isEnd: isEnd,
|
|
msLength: segEnd - segStart
|
|
});
|
|
}
|
|
}
|
|
return segs.sort(segmentCompare);
|
|
}
|
|
|
|
function eventsForResource(resource, events) {
|
|
var resourceEvents = [];
|
|
for (var i = 0; i < events.length; i++) {
|
|
if (events[i].resources && $.inArray(resource.id, events[i].resources) >= 0) {
|
|
resourceEvents.push(events[i]);
|
|
}
|
|
}
|
|
return resourceEvents;
|
|
}
|
|
|
|
function slotEventEnd(event) {
|
|
if (event.end) {
|
|
return event.end.clone();
|
|
}else{
|
|
return event.start.clone().add('m', opt('defaultEventMinutes'));
|
|
}
|
|
}
|
|
|
|
|
|
// renders events in the 'time slots' at the bottom
|
|
// TODO: when we refactor this, when user returns `false` eventRender, don't have empty space
|
|
// TODO: refactor will include using pixels to detect collisions instead of dates (handy for seg cmp)
|
|
|
|
function renderSlotSegs(segs, modifiedEventId) {
|
|
|
|
var i, segCnt=segs.length, seg,
|
|
event,
|
|
classes,
|
|
top, bottom,
|
|
colI, levelI, forward,
|
|
leftmost,
|
|
availWidth,
|
|
outerWidth,
|
|
left,
|
|
html='',
|
|
eventElements,
|
|
eventElement,
|
|
triggerRes,
|
|
titleElement,
|
|
height,
|
|
slotSegmentContainer = getSlotSegmentContainer(),
|
|
rtl, dis;
|
|
|
|
if (rtl === opt('isRTL')) {
|
|
dis = -1;
|
|
}else{
|
|
dis = 1;
|
|
}
|
|
|
|
// calculate position/dimensions, create html
|
|
for (i=0; i<segCnt; i++) {
|
|
seg = segs[i];
|
|
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;
|
|
}
|
|
}
|
|
left = leftmost + // leftmost possible
|
|
(availWidth / (levelI + forward + 1) * levelI) * // indentation
|
|
dis + (rtl ? availWidth - outerWidth : 0); // rtl
|
|
seg.top = top;
|
|
seg.left = left;
|
|
seg.outerWidth = outerWidth;
|
|
seg.outerHeight = bottom - top;
|
|
html += slotSegHtml(event, seg);
|
|
}
|
|
slotSegmentContainer[0].innerHTML = html; // faster than html()
|
|
eventElements = slotSegmentContainer.children();
|
|
|
|
// retrieve elements, run through eventRender callback, bind event handlers
|
|
for (i=0; i<segCnt; i++) {
|
|
seg = segs[i];
|
|
event = seg.event;
|
|
eventElement = $(eventElements[i]); // faster than eq()
|
|
triggerRes = trigger('eventRender', event, event, eventElement);
|
|
if (triggerRes === false) {
|
|
eventElement.remove();
|
|
}else{
|
|
if (triggerRes && triggerRes !== true) {
|
|
eventElement.remove();
|
|
eventElement = $(triggerRes)
|
|
.css({
|
|
position: 'absolute',
|
|
top: seg.top,
|
|
left: seg.left
|
|
})
|
|
.appendTo(slotSegmentContainer);
|
|
}
|
|
seg.element = eventElement;
|
|
if (event._id === modifiedEventId) {
|
|
bindSlotSeg(event, eventElement, seg);
|
|
}else{
|
|
eventElement[0]._fci = i; // for lazySegBind
|
|
}
|
|
reportEventElement(event, eventElement);
|
|
}
|
|
}
|
|
|
|
lazySegBind(slotSegmentContainer, segs, bindSlotSeg);
|
|
|
|
// record event sides and title positions
|
|
for (i=0; i<segCnt; i++) {
|
|
seg = segs[i];
|
|
if (eventElement === seg.element) {
|
|
seg.vsides = vsides(eventElement, true);
|
|
seg.hsides = hsides(eventElement, true);
|
|
titleElement = eventElement.find('.fc-event-title');
|
|
if (titleElement.length) {
|
|
seg.contentTop = titleElement[0].offsetTop;
|
|
}
|
|
}
|
|
}
|
|
|
|
// set all positions/dimensions at once
|
|
for (i=0; i<segCnt; i++) {
|
|
seg = segs[i];
|
|
if (eventElement === seg.element) {
|
|
eventElement[0].style.width = Math.max(0, seg.outerWidth - seg.hsides) + 'px';
|
|
height = Math.max(0, seg.outerHeight - seg.vsides);
|
|
eventElement[0].style.height = height + 'px';
|
|
event = seg.event;
|
|
if (seg.contentTop !== undefined && height - seg.contentTop < 10) {
|
|
// not enough room for title, put it in the time (TODO: maybe make both display:inline instead)
|
|
eventElement.find('div.fc-event-time')
|
|
.text(formatDate(event.start, opt('timeFormat')) + ' - ' + event.title);
|
|
eventElement.find('div.fc-event-title')
|
|
.remove();
|
|
}
|
|
trigger('eventAfterRender', event, event, eventElement);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
function slotSegHtml(event, seg) {
|
|
var html = "<";
|
|
var url = event.url;
|
|
var skinCss = getSkinCss(event, opt);
|
|
var classes = ['fc-event', 'fc-event-vert'];
|
|
if (isEventDraggable(event)) {
|
|
classes.push('fc-event-draggable');
|
|
}
|
|
if (seg.isStart) {
|
|
classes.push('fc-event-start');
|
|
}
|
|
if (seg.isEnd) {
|
|
classes.push('fc-event-end');
|
|
}
|
|
classes = classes.concat(event.className);
|
|
if (event.source) {
|
|
classes = classes.concat(event.source.className || []);
|
|
}
|
|
if (url) {
|
|
html += "a href='" + htmlEscape(event.url) + "'";
|
|
}else{
|
|
html += "div";
|
|
}
|
|
html +=
|
|
" class='" + classes.join(' ') + "'" +
|
|
" style=" +
|
|
"'" +
|
|
"position:absolute;" +
|
|
"top:" + seg.top + "px;" +
|
|
"left:" + seg.left + "px;" +
|
|
skinCss +
|
|
"'" +
|
|
">" +
|
|
"<div class='fc-event-inner'>" +
|
|
"<div class='fc-event-time'>" +
|
|
htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
|
|
"</div>" +
|
|
"<div class='fc-event-title'>" +
|
|
htmlEscape(event.title || '') +
|
|
"</div>" +
|
|
"</div>" +
|
|
"<div class='fc-event-bg'></div>";
|
|
if (seg.isEnd && isEventResizable(event)) {
|
|
html +=
|
|
"<div class='ui-resizable-handle ui-resizable-s'>=</div>";
|
|
}
|
|
html +=
|
|
"</" + (url ? "a" : "div") + ">";
|
|
return html;
|
|
}
|
|
|
|
|
|
function bindSlotSeg(event, eventElement, seg) {
|
|
var timeElement = eventElement.find('div.fc-event-time');
|
|
if (isEventDraggable(event)) {
|
|
draggableSlotEvent(event, eventElement, timeElement);
|
|
}
|
|
if (seg.isEnd && isEventResizable(event)) {
|
|
resizableSlotEvent(event, eventElement, timeElement);
|
|
}
|
|
eventElementHandlers(event, eventElement);
|
|
}
|
|
|
|
|
|
|
|
/* Dragging
|
|
-----------------------------------------------------------------------------------*/
|
|
|
|
|
|
// when event starts out FULL-DAY
|
|
// overrides DayEventRenderer's version because it needs to account for dragging elements
|
|
// to and from the slot area.
|
|
|
|
function draggableDayEvent(event, eventElement, seg) {
|
|
var isStart = seg.isStart;
|
|
var origWidth;
|
|
var revert;
|
|
var allDay = true;
|
|
var dayDelta;
|
|
var hoverListener = getHoverListener();
|
|
var colWidth = getColWidth();
|
|
var snapHeight = getSnapHeight();
|
|
var snapMinutes = getSnapMinutes();
|
|
var minMinute = getMinMinute();
|
|
eventElement.draggable({
|
|
opacity: opt('dragOpacity', 'month'), // use whatever the month view was using
|
|
revertDuration: opt('dragRevertDuration'),
|
|
start: function(ev, ui) {
|
|
trigger('eventDragStart', eventElement, event, ev, ui);
|
|
hideEvents(event, eventElement);
|
|
origWidth = eventElement.width();
|
|
hoverListener.start(function(cell, origCell) {
|
|
clearOverlays();
|
|
if (cell) {
|
|
revert = false;
|
|
var origDate = cellToDate(0, origCell.col);
|
|
var date = cellToDate(0, cell.col);
|
|
dayDelta = dayDiff(date, origDate);
|
|
if (!cell.row) {
|
|
// on full-days
|
|
renderDayOverlay(
|
|
event.start.clone().add('d', dayDelta),
|
|
exclEndDay(event).add('d', dayDelta)
|
|
);
|
|
resetElement();
|
|
}else{
|
|
// mouse is over bottom slots
|
|
if (isStart) {
|
|
if (allDay) {
|
|
// convert event to temporary slot-event
|
|
eventElement.width(colWidth - 10); // don't use entire width
|
|
setOuterHeight(
|
|
eventElement,
|
|
snapHeight * Math.round(
|
|
(event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes')) /
|
|
snapMinutes
|
|
)
|
|
);
|
|
eventElement.draggable('option', 'grid', [colWidth, 1]);
|
|
allDay = false;
|
|
}
|
|
}else{
|
|
revert = true;
|
|
}
|
|
}
|
|
revert = revert || (allDay && !dayDelta);
|
|
}else{
|
|
resetElement();
|
|
revert = true;
|
|
}
|
|
eventElement.draggable('option', 'revert', revert);
|
|
}, ev, 'drag');
|
|
},
|
|
stop: function(ev, ui) {
|
|
hoverListener.stop();
|
|
clearOverlays();
|
|
trigger('eventDragStop', eventElement, event, ev, ui);
|
|
if (revert) {
|
|
// hasn't moved or is out of bounds (draggable has already reverted)
|
|
resetElement();
|
|
eventElement.css('filter', ''); // clear IE opacity side-effects
|
|
showEvents(event, eventElement);
|
|
}else{
|
|
// changed!
|
|
var minuteDelta = 0;
|
|
if (!allDay) {
|
|
minuteDelta = Math.round((eventElement.offset().top - getSlotContainer().offset().top) / snapHeight) *
|
|
snapMinutes +
|
|
minMinute -
|
|
(event.start.getHours() * 60 + event.start.getMinutes());
|
|
}
|
|
eventDrop(this, event, dayDelta, minuteDelta, allDay, ev, ui);
|
|
}
|
|
}
|
|
});
|
|
function resetElement() {
|
|
if (!allDay) {
|
|
eventElement
|
|
.width(origWidth)
|
|
.height('')
|
|
.draggable('option', 'grid', null);
|
|
allDay = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// when event starts out IN TIMESLOTS
|
|
|
|
function draggableSlotEvent(event, eventElement, timeElement) {
|
|
var coordinateGrid = t.getCoordinateGrid();
|
|
var colCnt = getColCnt();
|
|
var colWidth = getColWidth();
|
|
var snapHeight = getSnapHeight();
|
|
var snapMinutes = getSnapMinutes();
|
|
|
|
// states
|
|
var origPosition; // original position of the element, not the mouse
|
|
var origCell;
|
|
var isInBounds, prevIsInBounds;
|
|
var isAllDay, prevIsAllDay;
|
|
var colDelta, prevColDelta;
|
|
var dayDelta; // derived from colDelta
|
|
var resourceDelta; // derived from colDelta
|
|
var minuteDelta, prevMinuteDelta;
|
|
|
|
eventElement.draggable({
|
|
scroll: false,
|
|
grid: [ colWidth, snapHeight ],
|
|
axis: colCnt==1 ? 'y' : false,
|
|
opacity: opt('dragOpacity'),
|
|
revertDuration: opt('dragRevertDuration'),
|
|
start: function(ev, ui) {
|
|
|
|
trigger('eventDragStart', eventElement, event, ev, ui);
|
|
hideEvents(event, eventElement);
|
|
|
|
coordinateGrid.build();
|
|
|
|
// initialize states
|
|
origPosition = eventElement.position();
|
|
origCell = coordinateGrid.cell(ev.pageX, ev.pageY);
|
|
isInBounds = prevIsInBounds = true;
|
|
isAllDay = prevIsAllDay = getIsCellAllDay(origCell);
|
|
colDelta = prevColDelta = 0;
|
|
dayDelta = 0;
|
|
resourceDelta = 0;
|
|
minuteDelta = prevMinuteDelta = 0;
|
|
|
|
},
|
|
drag: function(ev, ui) {
|
|
|
|
// NOTE: this `cell` value is only useful for determining in-bounds and all-day.
|
|
// Bad for anything else due to the discrepancy between the mouse position and the
|
|
// element position while snapping. (problem revealed in PR #55)
|
|
//
|
|
// PS- the problem exists for draggableDayEvent() when dragging an all-day event to a slot event.
|
|
// We should overhaul the dragging system and stop relying on jQuery UI.
|
|
var cell = coordinateGrid.cell(ev.pageX, ev.pageY);
|
|
|
|
// update states
|
|
isInBounds = !!cell;
|
|
if (isInBounds) {
|
|
isAllDay = getIsCellAllDay(cell);
|
|
|
|
// calculate column delta
|
|
colDelta = Math.round((ui.position.left - origPosition.left) / colWidth);
|
|
if (colDelta != prevColDelta) {
|
|
// calculate the day delta based off of the original clicked column and the column delta
|
|
//var origDate = cellToDate(0, origCell.col);
|
|
//var col = origCell.col + colDelta;
|
|
//col = Math.max(0, col);
|
|
//col = Math.min(colCnt-1, col);
|
|
//var date = cellToDate(0, col);
|
|
//dayDelta = 0; //dayDiff(date, origDate);
|
|
resourceDelta = colDelta;
|
|
}
|
|
|
|
// calculate minute delta (only if over slots)
|
|
if (!isAllDay) {
|
|
minuteDelta = Math.round((ui.position.top - origPosition.top) / snapHeight) * snapMinutes;
|
|
}
|
|
}
|
|
|
|
// any state changes?
|
|
if (
|
|
isInBounds != prevIsInBounds ||
|
|
isAllDay != prevIsAllDay ||
|
|
colDelta != prevColDelta ||
|
|
minuteDelta != prevMinuteDelta
|
|
) {
|
|
|
|
updateUI();
|
|
|
|
// update previous states for next time
|
|
prevIsInBounds = isInBounds;
|
|
prevIsAllDay = isAllDay;
|
|
prevColDelta = colDelta;
|
|
prevMinuteDelta = minuteDelta;
|
|
}
|
|
|
|
// if out-of-bounds, revert when done, and vice versa.
|
|
eventElement.draggable('option', 'revert', !isInBounds);
|
|
|
|
},
|
|
stop: function(ev, ui) {
|
|
|
|
clearOverlays();
|
|
trigger('eventDragStop', eventElement, event, ev, ui);
|
|
|
|
//if (isInBounds && (isAllDay || dayDelta || minuteDelta)) { // changed!
|
|
if (isInBounds && (isAllDay || resourceDelta || minuteDelta)) { // changed!
|
|
event.resources = resources()[origCell.col + resourceDelta].id;
|
|
eventDrop(this, event, dayDelta, isAllDay ? 0 : minuteDelta, isAllDay, ev, ui);
|
|
}
|
|
else { // either no change or out-of-bounds (draggable has already reverted)
|
|
|
|
// reset states for next time, and for updateUI()
|
|
isInBounds = true;
|
|
isAllDay = false;
|
|
colDelta = 0;
|
|
dayDelta = 0;
|
|
minuteDelta = 0;
|
|
|
|
updateUI();
|
|
eventElement.css('filter', ''); // clear IE opacity side-effects
|
|
|
|
// sometimes fast drags make event revert to wrong position, so reset.
|
|
// also, if we dragged the element out of the area because of snapping,
|
|
// but the *mouse* is still in bounds, we need to reset the position.
|
|
eventElement.css(origPosition);
|
|
|
|
showEvents(event, eventElement);
|
|
}
|
|
}
|
|
});
|
|
|
|
function updateUI() {
|
|
clearOverlays();
|
|
if (isInBounds) {
|
|
if (isAllDay) {
|
|
timeElement.hide();
|
|
eventElement.draggable('option', 'grid', null); // disable grid snapping
|
|
renderDayOverlay(
|
|
event.start.clone().add('d', dayDelta),
|
|
exclEndDay(event).add('d', dayDelta)
|
|
);
|
|
}
|
|
else {
|
|
updateTimeText(minuteDelta);
|
|
timeElement.css('display', ''); // show() was causing display=inline
|
|
eventElement.draggable('option', 'grid', [colWidth, snapHeight]); // re-enable grid snapping
|
|
}
|
|
}
|
|
}
|
|
|
|
function updateTimeText(minuteDelta) {
|
|
var newStart = event.start.clone().add('m', minuteDelta);
|
|
var newEnd;
|
|
if (event.end) {
|
|
newEnd = event.end.clone().add('m', minuteDelta);
|
|
}
|
|
timeElement.text(formatDates(newStart, newEnd, opt('timeFormat')));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Resizing
|
|
--------------------------------------------------------------------------------------*/
|
|
|
|
|
|
function resizableSlotEvent(event, eventElement, timeElement) {
|
|
var snapDelta, prevSnapDelta;
|
|
var snapHeight = getSnapHeight();
|
|
var snapMinutes = getSnapMinutes();
|
|
eventElement.resizable({
|
|
handles: {
|
|
s: '.ui-resizable-handle'
|
|
},
|
|
grid: snapHeight,
|
|
start: function(ev, ui) {
|
|
snapDelta = prevSnapDelta = 0;
|
|
hideEvents(event, eventElement);
|
|
trigger('eventResizeStart', this, event, ev, ui);
|
|
},
|
|
resize: function(ev, ui) {
|
|
// don't rely on ui.size.height, doesn't take grid into account
|
|
snapDelta = Math.round((Math.max(snapHeight, eventElement.height()) - ui.originalSize.height) / snapHeight);
|
|
if (snapDelta != prevSnapDelta) {
|
|
timeElement.text(
|
|
formatDates(
|
|
event.start,
|
|
(!snapDelta && !event.end) ? null : // no change, so don't display time range
|
|
eventEnd(event).add('m', snapMinutes*snapDelta),
|
|
opt('timeFormat')
|
|
)
|
|
);
|
|
prevSnapDelta = snapDelta;
|
|
}
|
|
},
|
|
stop: function(ev, ui) {
|
|
trigger('eventResizeStop', this, event, ev, ui);
|
|
if (snapDelta) {
|
|
eventResize(this, event, 0, snapMinutes*snapDelta, ev, ui);
|
|
}else{
|
|
showEvents(event, eventElement);
|
|
// BUG: if event was really short, need to put title back in span
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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++) {
|
|
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{
|
|
break;
|
|
}
|
|
}
|
|
if (levels[j]) {
|
|
levels[j].push(seg);
|
|
}else{
|
|
levels[j] = [seg];
|
|
}
|
|
}
|
|
return levels;
|
|
}
|
|
|
|
|
|
function countForwardSegs(levels) {
|
|
var i, j, k, level, segForward, segBack;
|
|
for (i=levels.length-1; i>0; 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function agendaSegsCollide(seg1, seg2) {
|
|
return seg1.end > seg2.start && seg1.start < seg2.end;
|
|
}
|
|
|