Переглянути джерело

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

Adam Shaw 15 роки тому
батько
коміт
64b84cc385

+ 2 - 1
src/agenda/AgendaEventRenderer.js

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

+ 10 - 1
src/agenda/AgendaView.js

@@ -30,6 +30,7 @@ function AgendaView(element, calendar, viewName) {
 	t.defaultEventEnd = defaultEventEnd;
 	t.timePosition = timePosition;
 	t.dayOfWeekCol = dayOfWeekCol;
+	t.dateCell = dateCell;
 	t.cellDate = cellDate;
 	t.cellIsAllDay = cellIsAllDay;
 	t.allDayTR = allDayTR;
@@ -493,7 +494,15 @@ function AgendaView(element, calendar, viewName) {
 	
 	
 	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())
+		};
 	}
 	
 	

+ 2 - 1
src/basic/BasicEventRenderer.js

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

+ 11 - 2
src/basic/BasicView.js

@@ -25,6 +25,7 @@ function BasicView(element, calendar, viewName) {
 	t.colContentLeft = colContentLeft;
 	t.colContentRight = colContentRight;
 	t.dayOfWeekCol = dayOfWeekCol;
+	t.dateCell = dateCell;
 	t.cellDate = cellDate;
 	t.cellIsAllDay = function() { return true };
 	t.allDayTR = allDayTR;
@@ -335,7 +336,7 @@ function BasicView(element, calendar, viewName) {
 	
 	
 	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) {
-		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
+		};
 	}
 	
 	

+ 284 - 97
src/common/DayEventRenderer.js

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

+ 1 - 1
src/common/View.js

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

+ 1 - 1
src/common/common.css

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