resize event to different week (issue 308), necessary event rendering refactoring

This commit is contained in:
Adam Shaw 2010-11-09 22:54:41 -08:00
parent c1a19a24c8
commit 64b84cc385
7 changed files with 315 additions and 108 deletions

View file

@ -5,6 +5,7 @@ function AgendaEventRenderer() {
// exports // exports
t.renderEvents = renderEvents; t.renderEvents = renderEvents;
t.compileDaySegs = compileDaySegs; // for DayEventRenderer
t.clearEvents = clearEvents; t.clearEvents = clearEvents;
t.slotSegHtml = slotSegHtml; t.slotSegHtml = slotSegHtml;
t.bindDaySeg = bindDaySeg; t.bindDaySeg = bindDaySeg;
@ -296,7 +297,7 @@ function AgendaEventRenderer() {
if (event.editable || event.editable === undefined && opt('editable')) { if (event.editable || event.editable === undefined && opt('editable')) {
draggableDayEvent(event, eventElement, seg.isStart); draggableDayEvent(event, eventElement, seg.isStart);
if (seg.isEnd) { if (seg.isEnd) {
resizableDayEvent(event, eventElement, getColWidth()); resizableDayEvent(event, eventElement, seg);
} }
} }
} }

View file

@ -30,6 +30,7 @@ function AgendaView(element, calendar, viewName) {
t.defaultEventEnd = defaultEventEnd; t.defaultEventEnd = defaultEventEnd;
t.timePosition = timePosition; t.timePosition = timePosition;
t.dayOfWeekCol = dayOfWeekCol; t.dayOfWeekCol = dayOfWeekCol;
t.dateCell = dateCell;
t.cellDate = cellDate; t.cellDate = cellDate;
t.cellIsAllDay = cellIsAllDay; t.cellIsAllDay = cellIsAllDay;
t.allDayTR = allDayTR; t.allDayTR = allDayTR;
@ -493,7 +494,15 @@ function AgendaView(element, calendar, viewName) {
function dayOfWeekCol(dayOfWeek) { function dayOfWeekCol(dayOfWeek) {
return ((dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt)*dis+dit; return ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt)*dis+dit;
}
function dateCell(date) {
return {
row: Math.floor(dayDiff(date, t.visStart) / 7),
col: dayOfWeekCol(date.getDay())
};
} }

View file

@ -5,6 +5,7 @@ function BasicEventRenderer() {
// exports // exports
t.renderEvents = renderEvents; t.renderEvents = renderEvents;
t.compileDaySegs = compileSegs; // for DayEventRenderer
t.clearEvents = clearEvents; t.clearEvents = clearEvents;
t.bindDaySeg = bindDaySeg; t.bindDaySeg = bindDaySeg;
@ -79,7 +80,7 @@ function BasicEventRenderer() {
if (event.editable || event.editable === undefined && opt('editable')) { if (event.editable || event.editable === undefined && opt('editable')) {
draggableDayEvent(event, eventElement); draggableDayEvent(event, eventElement);
if (seg.isEnd) { if (seg.isEnd) {
resizableDayEvent(event, eventElement); resizableDayEvent(event, eventElement, seg);
} }
} }
} }

View file

@ -25,6 +25,7 @@ function BasicView(element, calendar, viewName) {
t.colContentLeft = colContentLeft; t.colContentLeft = colContentLeft;
t.colContentRight = colContentRight; t.colContentRight = colContentRight;
t.dayOfWeekCol = dayOfWeekCol; t.dayOfWeekCol = dayOfWeekCol;
t.dateCell = dateCell;
t.cellDate = cellDate; t.cellDate = cellDate;
t.cellIsAllDay = function() { return true }; t.cellIsAllDay = function() { return true };
t.allDayTR = allDayTR; t.allDayTR = allDayTR;
@ -335,7 +336,7 @@ function BasicView(element, calendar, viewName) {
function renderSelection(startDate, endDate, allDay) { function renderSelection(startDate, endDate, allDay) {
renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); // rebuild every time???
} }
@ -427,7 +428,15 @@ function BasicView(element, calendar, viewName) {
function dayOfWeekCol(dayOfWeek) { function dayOfWeekCol(dayOfWeek) {
return (dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt; return (dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt;
}
function dateCell(date) {
return {
row: Math.floor(dayDiff(date, t.visStart) / 7),
col: dayOfWeekCol(date.getDay())*dis + dit
};
} }

View file

@ -11,6 +11,7 @@ function DayEventRenderer() {
// imports // imports
var opt = t.opt; var opt = t.opt;
var trigger = t.trigger; var trigger = t.trigger;
var eventEnd = t.eventEnd;
var reportEventElement = t.reportEventElement; var reportEventElement = t.reportEventElement;
var showEvents = t.showEvents; var showEvents = t.showEvents;
var hideEvents = t.hideEvents; var hideEvents = t.hideEvents;
@ -23,40 +24,102 @@ function DayEventRenderer() {
var colContentLeft = t.colContentLeft; var colContentLeft = t.colContentLeft;
var colContentRight = t.colContentRight; var colContentRight = t.colContentRight;
var dayOfWeekCol = t.dayOfWeekCol; var dayOfWeekCol = t.dayOfWeekCol;
var dateCell = t.dateCell;
var compileDaySegs = t.compileDaySegs;
var getDaySegmentContainer = t.getDaySegmentContainer; var getDaySegmentContainer = t.getDaySegmentContainer;
var bindDaySeg = t.bindDaySeg; //TODO: streamline this var bindDaySeg = t.bindDaySeg; //TODO: streamline this
var formatDates = t.calendar.formatDates; var formatDates = t.calendar.formatDates;
var renderDayOverlay = t.renderDayOverlay;
var clearOverlays = t.clearOverlays;
var clearSelection = t.clearSelection;
/* Rendering /* Rendering
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
function renderDaySegs(segs, modifiedEventId) { function renderDaySegs(segs, modifiedEventId) {
var segmentContainer = getDaySegmentContainer();
var rtl=opt('isRTL'), var rowDivs;
i, segCnt=segs.length, seg, var rowCnt;
event, var i;
className, var rowI;
left, right, var top;
html='', var levelI;
eventElements, var levelHeight;
eventElement, var segCnt = segs.length;
triggerRes, var seg;
hsideCache={}, segmentContainer[0].innerHTML = daySegHTML(segs); // faster than .html()
vmarginCache={}, daySegElementResolve(segs, segmentContainer.children());
key, val, daySegElementReport(segs);
rowI, top, levelI, levelHeight, daySegHandlers(segs, segmentContainer, modifiedEventId);
rowDivs=[], daySegCalcHSides(segs);
rowDivTops=[], daySegSetWidths(segs);
bounds = allDayBounds(), daySegCalcHeights(segs);
minLeft = bounds.left, rowDivs = getRowDivs();
maxLeft = bounds.right, rowCnt = rowDivs.length;
rowCnt = getRowCnt(), // set row heights, calculate event tops (in relation to row top)
colCnt = getColCnt(), for (i=0, rowI=0; rowI<rowCnt; rowI++) {
segmentContainer = getDaySegmentContainer(); top = levelI = levelHeight = 0;
while (i<segCnt && (seg = segs[i]).row == rowI) {
if (seg.level != levelI) {
top += levelHeight;
levelHeight = 0;
levelI++;
}
levelHeight = Math.max(levelHeight, seg.outerHeight||0);
seg.top = top;
i++;
}
rowDivs[rowI].height(top + levelHeight);
}
daySegSetTops(segs, getRowTops(rowDivs));
}
function renderTempDaySegs(segs, adjustRow, adjustTop) {
var tempContainer = $("<div/>");
var elements;
var segmentContainer = getDaySegmentContainer();
var i;
var segCnt = segs.length;
var element;
tempContainer[0].innerHTML = daySegHTML(segs); // faster than .html()
elements = tempContainer.children();
segmentContainer.append(elements);
daySegElementResolve(segs, elements);
daySegCalcHSides(segs);
daySegSetWidths(segs);
daySegCalcHeights(segs);
daySegSetTops(segs, getRowTops(getRowDivs()));
elements = [];
for (i=0; i<segCnt; i++) {
element = segs[i].element;
if (element) {
if (segs[i].row === adjustRow) {
element.css('top', adjustTop);
}
elements.push(element[0]);
}
}
return $(elements);
}
function daySegHTML(segs) { // also sets seg.left and seg.outerWidth
var rtl = opt('isRTL');
var i;
var segCnt=segs.length;
var seg;
var event;
var className;
var bounds = allDayBounds();
var minLeft = bounds.left;
var maxLeft = bounds.right;
var left;
var right;
var html = '';
// calculate desired position/dimensions, create html // calculate desired position/dimensions, create html
for (i=0; i<segCnt; i++) { for (i=0; i<segCnt; i++) {
seg = segs[i]; seg = segs[i];
@ -91,106 +154,184 @@ function DayEventRenderer() {
:'') + :'') +
"<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" + "<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" +
"</a>" + "</a>" +
((event.editable || event.editable === undefined && opt('editable')) && !opt('disableResizing') && $.fn.resizable ? (seg.isEnd && (event.editable || event.editable === undefined && opt('editable')) && !opt('disableResizing') && $.fn.resizable ?
"<div class='ui-resizable-handle ui-resizable-" + (rtl ? 'w' : 'e') + "'></div>" "<div class='ui-resizable-handle ui-resizable-" + (rtl ? 'w' : 'e') + "'></div>"
: '') + : '') +
"</div>"; "</div>";
seg.left = left; seg.left = left;
seg.outerWidth = right - left; seg.outerWidth = right - left;
} }
segmentContainer[0].innerHTML = html; // faster than html() return html;
eventElements = segmentContainer.children(); }
// retrieve elements, run through eventRender callback, bind handlers
function daySegElementResolve(segs, elements) { // sets seg.element
var i;
var segCnt = segs.length;
var seg;
var event;
var element;
var triggerRes;
for (i=0; i<segCnt; i++) { for (i=0; i<segCnt; i++) {
seg = segs[i]; seg = segs[i];
eventElement = $(eventElements[i]); // faster than eq()
event = seg.event; event = seg.event;
triggerRes = trigger('eventRender', event, event, eventElement); element = $(elements[i]); // faster than .eq()
triggerRes = trigger('eventRender', event, event, element);
if (triggerRes === false) { if (triggerRes === false) {
eventElement.remove(); element.remove();
}else{ }else{
if (triggerRes && triggerRes !== true) { if (triggerRes && triggerRes !== true) {
eventElement.remove(); triggerRes = $(triggerRes)
eventElement = $(triggerRes)
.css({ .css({
position: 'absolute', position: 'absolute',
left: seg.left left: seg.left
}) });
.appendTo(segmentContainer); element.replaceWith(triggerRes);
element = triggerRes;
} }
seg.element = eventElement; seg.element = element;
if (event._id === modifiedEventId) {
bindDaySeg(event, eventElement, seg);
}else{
eventElement[0]._fci = i; // for lazySegBind
}
reportEventElement(event, eventElement);
} }
} }
}
function daySegElementReport(segs) {
var i;
var segCnt = segs.length;
var seg;
var element;
for (i=0; i<segCnt; i++) {
seg = segs[i];
element = seg.element;
if (element) {
reportEventElement(seg.event, element);
}
}
}
function daySegHandlers(segs, segmentContainer, modifiedEventId) {
var i;
var segCnt = segs.length;
var seg;
var element;
var event;
// retrieve elements, run through eventRender callback, bind handlers
for (i=0; i<segCnt; i++) {
seg = segs[i];
element = seg.element;
if (element) {
event = seg.event;
if (event._id === modifiedEventId) {
bindDaySeg(event, element, seg);
}else{
element[0]._fci = i; // for lazySegBind
}
}
}
lazySegBind(segmentContainer, segs, bindDaySeg); lazySegBind(segmentContainer, segs, bindDaySeg);
}
function daySegCalcHSides(segs) { // also sets seg.key
var i;
var segCnt = segs.length;
var seg;
var element;
var key, val;
var hsideCache = {};
// record event horizontal sides // record event horizontal sides
for (i=0; i<segCnt; i++) { for (i=0; i<segCnt; i++) {
seg = segs[i]; seg = segs[i];
if (eventElement = seg.element) { element = seg.element;
val = hsideCache[key = seg.key = cssKey(eventElement[0])]; if (element) {
seg.hsides = val === undefined ? (hsideCache[key] = hsides(eventElement[0], true)) : val; key = seg.key = cssKey(element[0]);
val = hsideCache[key];
if (val === undefined) {
val = hsideCache[key] = hsides(element[0], true);
}
seg.hsides = val;
} }
} }
}
// set event widths
function daySegSetWidths(segs) {
var i;
var segCnt = segs.length;
var seg;
var element;
for (i=0; i<segCnt; i++) { for (i=0; i<segCnt; i++) {
seg = segs[i]; seg = segs[i];
if (eventElement = seg.element) { element = seg.element;
eventElement[0].style.width = Math.max(0, seg.outerWidth - seg.hsides) + 'px'; if (element) {
element[0].style.width = Math.max(0, seg.outerWidth - seg.hsides) + 'px';
} }
} }
}
function daySegCalcHeights(segs) {
var i;
var segCnt = segs.length;
var seg;
var element;
var key, val;
var vmarginCache = {};
// record event heights // record event heights
for (i=0; i<segCnt; i++) { for (i=0; i<segCnt; i++) {
seg = segs[i]; seg = segs[i];
if (eventElement = seg.element) { element = seg.element;
val = vmarginCache[key = seg.key]; if (element) {
seg.outerHeight = eventElement[0].offsetHeight + ( key = seg.key; // created in daySegCalcHSides
val === undefined ? (vmarginCache[key] = vmargins(eventElement[0])) : val val = vmarginCache[key];
); if (val === undefined) {
} val = vmarginCache[key] = vmargins(element[0]);
}
// set row heights, calculate event tops (in relation to row top)
for (i=0, rowI=0; rowI<rowCnt; rowI++) {
top = levelI = levelHeight = 0;
while (i<segCnt && (seg = segs[i]).row == rowI) {
if (seg.level != levelI) {
top += levelHeight;
levelHeight = 0;
levelI++;
} }
levelHeight = Math.max(levelHeight, seg.outerHeight||0); seg.outerHeight = element[0].offsetHeight + val;
seg.top = top;
i++;
} }
rowDivs[rowI] = allDayTR(rowI).find('td:first div.fc-day-content > div') // optimal selector?
.height(top + levelHeight);
} }
}
// calculate row tops
for (rowI=0; rowI<rowCnt; rowI++) { function getRowDivs() {
rowDivTops[rowI] = rowDivs[rowI][0].offsetTop; var i;
var rowCnt = getRowCnt();
var rowDivs = [];
for (i=0; i<rowCnt; i++) {
rowDivs[i] = allDayTR(i)
.find('td:first div.fc-day-content > div'); // optimal selector?
} }
return rowDivs;
}
// set event tops
function getRowTops(rowDivs) {
var i;
var rowCnt = rowDivs.length;
var tops = [];
for (i=0; i<rowCnt; i++) {
tops[i] = rowDivs[i][0].offsetTop;
}
return tops;
}
function daySegSetTops(segs, rowTops) { // also triggers eventAfterRender
var i;
var segCnt = segs.length;
var seg;
var element;
var event;
for (i=0; i<segCnt; i++) { for (i=0; i<segCnt; i++) {
seg = segs[i]; seg = segs[i];
if (eventElement = seg.element) { element = seg.element;
eventElement[0].style.top = rowDivTops[seg.row] + seg.top + 'px'; if (element) {
element[0].style.top = rowTops[seg.row] + (seg.top||0) + 'px';
event = seg.event; event = seg.event;
trigger('eventAfterRender', event, event, eventElement); trigger('eventAfterRender', event, event, element);
} }
} }
} }
@ -199,30 +340,76 @@ function DayEventRenderer() {
-----------------------------------------------------------------------------------*/ -----------------------------------------------------------------------------------*/
function resizableDayEvent(event, eventElement) { function resizableDayEvent(event, element, seg) {
if (!opt('disableResizing') && eventElement.resizable) { if (!opt('disableResizing') && seg.isEnd) {
var colWidth = getColWidth(); var rtl = opt('isRTL');
eventElement.resizable({ var direction = rtl ? 'w' : 'e';
handles: opt('isRTL') ? {w:'div.ui-resizable-w'} : {e:'div.ui-resizable-e'}, var handle = element.find('div.ui-resizable-' + direction);
grid: colWidth, handle.mousedown(function(ev) {
minWidth: colWidth/2, // need this or else IE throws errors when too small if (ev.which != 1) {
containment: t.element.parent().parent(), // the main element... return; // needs to be left mouse button
// ... a fix. wouldn't allow extending to last column in agenda views (jq ui bug?) }
start: function(ev, ui) { var hoverListener = t.getHoverListener();
eventElement.css('z-index', 9); var rowCnt = getRowCnt();
hideEvents(event, eventElement); var colCnt = getColCnt();
trigger('eventResizeStart', this, event, ev, ui); var dis = rtl ? -1 : 1;
}, var dit = rtl ? colCnt : 0;
stop: function(ev, ui) { var elementTop = element.css('top');
trigger('eventResizeStop', this, event, ev, ui); var dayDelta;
// ui.size.width wasn't working with grid correctly, use .width() var helpers;
var dayDelta = Math.round((eventElement.width() - ui.originalSize.width) / colWidth); var eventCopy = $.extend({}, event);
if (dayDelta) { var minCell = dateCell(event.start);
eventResize(this, event, dayDelta, 0, ev, ui); clearSelection();
}else{ $('body')
eventElement.css('z-index', 8); .css('cursor', direction + '-resize')
showEvents(event, eventElement); .one('mouseup', mouseup);
trigger('eventResizeStart', this, event, ev);
hoverListener.start(function(cell, origCell) {
if (cell) {
var r = Math.max(minCell.row, cell.row);
var c = cell.col;
if (rowCnt == 1) {
r = 0; // hack for all-day area in agenda views
}
if (r == minCell.row) {
if (rtl) {
c = Math.min(minCell.col, c);
}else{
c = Math.max(minCell.col, c);
}
}
dayDelta = (r * colCnt + c*dis+dit) - (origCell.row * colCnt + origCell.col*dis+dit);
var newEnd = addDays(eventEnd(event), dayDelta, true);
if (dayDelta) {
eventCopy.end = newEnd;
var oldHelpers = helpers;
helpers = renderTempDaySegs(compileDaySegs([eventCopy]), seg.row, elementTop);
helpers.find('*').css('cursor', direction + '-resize');
if (oldHelpers) {
oldHelpers.remove();
}
hideEvents(event);
}else{
if (helpers) {
showEvents(event);
helpers.remove();
helpers = null;
}
}
clearOverlays();
renderDayOverlay(event.start, addDays(cloneDate(newEnd), 1)); // coordinate grid already rebuild at hoverListener.start
} }
}, ev);
function mouseup(ev) {
trigger('eventResizeStop', this, event, ev);
$('body').css('cursor', 'auto');
hoverListener.stop();
clearOverlays();
if (dayDelta) {
eventResize(this, event, dayDelta, 0, ev);
// event redraw will clear helpers
}
// otherwise, the drag handler already restored the old events
} }
}); });
} }

View file

@ -139,7 +139,7 @@ function View(element, calendar, viewName) {
var elements = eventElementsByID[event._id], var elements = eventElementsByID[event._id],
i, len = elements.length; i, len = elements.length;
for (i=0; i<len; i++) { for (i=0; i<len; i++) {
if (elements[i][0] != exceptElement[0]) { if (!exceptElement || elements[i][0] != exceptElement[0]) {
elements[i][funcName](); elements[i][funcName]();
} }
} }

View file

@ -61,7 +61,7 @@
/* resizable */ /* resizable */
.fc .ui-resizable-handle { .fc .ui-resizable-handle { /*** TODO: don't use ui-resizable anoymore, change class ***/
display: block; display: block;
position: absolute; position: absolute;
z-index: 99999; z-index: 99999;