Bläddra i källkod

final round of optimizations for event rendering. fixed some new bugs

Adam Shaw 16 år sedan
förälder
incheckning
0e3e04bbdf
9 ändrade filer med 272 tillägg och 198 borttagningar
  1. 113 64
      src/agenda.js
  2. 116 82
      src/grid.js
  3. 2 2
      src/main.js
  4. 29 37
      src/util.js
  5. 1 1
      src/view.js
  6. 3 3
      tests/many_events.html
  7. 0 0
      tests/many_events_json.txt
  8. 7 8
      tests/triggers.html
  9. 1 1
      version.txt

+ 113 - 64
src/agenda.js

@@ -89,6 +89,7 @@ function Agenda(element, options, methods) {
 		colContentPositions = new HorizontalPositionCache(function(col) {
 			return bg.find('td:eq(' + col + ') div div');
 		}),
+		slotTopCache = {},
 		// ...
 		
 	view = $.extend(this, viewMethods, methods, {
@@ -167,7 +168,7 @@ function Agenda(element, options, methods) {
 				s += "<tr class='fc-all-day'>" +
 						"<th class='fc-axis fc-leftmost " + tm + "-state-default'>" + options.allDayText + "</th>" +
 						"<td colspan='" + colCnt + "' class='" + tm + "-state-default'>" +
-							"<div class='fc-day-content'><div>&nbsp;</div></div></td>" +
+							"<div class='fc-day-content'><div style='position:relative'>&nbsp;</div></div></td>" +
 						"<th class='" + tm + "-state-default'>&nbsp;</th>" +
 					"</tr><tr class='fc-divider fc-last'><th colspan='" + (colCnt+2) + "' class='" +
 						tm + "-state-default fc-leftmost'><div/></th></tr>";
@@ -177,7 +178,7 @@ function Agenda(element, options, methods) {
 			head.find('td').click(slotClick);
 			
 			// all-day event container
-			daySegmentContainer = $("<div/>").appendTo(head);
+			daySegmentContainer = $("<div style='position:absolute;top:0;left:0'/>").appendTo(head);
 			
 			// body
 			d = zeroDate();
@@ -191,7 +192,7 @@ function Agenda(element, options, methods) {
 					"'><th class='fc-axis fc-leftmost " + tm + "-state-default'>" +
 					((!slotNormal || minutes==0) ? formatDate(d, options.axisFormat) : '&nbsp;') + 
 					"</th><td class='fc-slot" + i + ' ' +
-						tm + "-state-default'><div>&nbsp;</div></td></tr>";
+						tm + "-state-default'><div style='position:relative'>&nbsp;</div></td></tr>";
 				addMinutes(d, options.slotMinutes);
 			}
 			s += "</table>";
@@ -287,6 +288,7 @@ function Agenda(element, options, methods) {
 		viewWidth = width;
 		viewHeight = height;
 		colContentPositions.clear();
+		slotTopCache = {};
 		
 		body.width(width);
 		body.height(height - head.height());
@@ -357,7 +359,7 @@ function Agenda(element, options, methods) {
 				slotEvents.push(events[i]);
 			}
 		}
-		renderDaySegs(stackSegs(view.sliceSegs(dayEvents, $.map(dayEvents, visEventEnd), view.visStart, view.visEnd)));
+		renderDaySegs(compileDaySegs(dayEvents));
 		renderSlotSegs(compileSlotSegs(slotEvents));
 	}
 	
@@ -375,19 +377,49 @@ function Agenda(element, options, methods) {
 	}
 	
 	
+	
+	
+	
+	function compileDaySegs(events) {
+		var levels = stackSegs(view.sliceSegs(events, $.map(events, visEventEnd), view.visStart, view.visEnd)),
+			i, levelCnt=levels.length, level,
+			j, seg,
+			segs=[];
+		for (i=0; i<levelCnt; i++) {
+			level = levels[i];
+			for (j=0; j<level.length; j++) {
+				seg = level[j];
+				seg.row = 0;
+				seg.level = i;
+				segs.push(seg);
+			}
+		}
+		return segs;
+	}
+	
+	
 	function compileSlotSegs(events) {
 		var d = addMinutes(cloneDate(view.visStart), minMinute),
-			ends = $.map(events, visEventEnd),
-			levels,
-			segCols = [],
-			i=0;
-		for (; i<colCnt; i++) {
-			levels = stackSegs(view.sliceSegs(events, ends, d, addMinutes(cloneDate(d), maxMinute-minMinute)));
-			countForwardSegs(levels);
-			segCols.push(levels);
+			visEventEnds = $.map(events, visEventEnd),
+			i, col,
+			j, level,
+			k, seg,
+			segs=[];
+		for (i=0; i<colCnt; i++) {
+			col = stackSegs(view.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);
+				}
+			}
 			addDays(d, 1, true);
 		}
-		return segCols;
+		return segs;
 	}
 	
 	
@@ -395,10 +427,11 @@ function Agenda(element, options, methods) {
 	
 	// renders 'all-day' events at the top
 	
-	function renderDaySegs(segRow) {
+	function renderDaySegs(segs) {
 		if (options.allDaySlot) {
 			_renderDaySegs(
-				[segRow],
+				segs,
+				1,
 				view,
 				axisWidth,
 				viewWidth,
@@ -422,32 +455,30 @@ function Agenda(element, options, methods) {
 	
 	// renders events in the 'time slots' at the bottom
 	
-	function renderSlotSegs(segCols) {
-			
-		var event,
+	function renderSlotSegs(segs) {
+	
+		var i, segCnt=segs.length, seg,
+			event,
 			className,
-			top,
-			bottom,
+			top, bottom,
+			colI, levelI, forward,
 			leftmost,
 			availWidth,
-			forward,
-			width,
+			outerWidth,
 			left,
-			eventTops=[],
-			eventLefts=[],
-			eventOuterWidths=[],
-			eventOuterHeights=[],
 			html='',
-			eventElements,
+			_eventElements,
 			eventElement,
 			triggerRes,
-			eventVSides=[],
-			eventHSides=[],
-			eventTitlePositions=[],
+			vsideCache={},
+			hsideCache={},
+			key, val,
+			titleSpan,
 			height;
 			
-		// calculate desired position/dimensions, create html
-		eachLeaf(segCols, function(l, seg, segI, levelI, colI) {
+		// calculate position/dimensions, create html
+		for (i=0; i<segCnt; i++) {
+			seg = segs[i];
 			event = seg.event;
 			className = 'fc-event fc-event-vert ';
 			if (seg.isStart) {
@@ -458,48 +489,51 @@ function Agenda(element, options, methods) {
 			}
 			top = timePosition(seg.start, seg.start);
 			bottom = timePosition(seg.start, seg.end);
+			colI = seg.col;
+			levelI = seg.level;
+			forward = seg.forward || 0;
 			leftmost = axisWidth + colContentPositions.left(colI*dis + dit);
 			availWidth = axisWidth + colContentPositions.right(colI*dis + dit) - leftmost;
 			availWidth = Math.min(availWidth-6, availWidth*.95); // TODO: move this to CSS
-			forward = seg.forward || 0;
 			if (levelI) {
 				// indented and thin
-				width = availWidth / (levelI + forward + 1);
+				outerWidth = availWidth / (levelI + forward + 1);
 			}else{
 				if (forward) {
 					// moderately wide, aligned left still
-					width = ((availWidth / (forward + 1)) - (12/2)) * 2; // 12 is the predicted width of resizer =
+					outerWidth = ((availWidth / (forward + 1)) - (12/2)) * 2; // 12 is the predicted width of resizer =
 				}else{
 					// can be entire width, aligned left
-					width = availWidth;
+					outerWidth = availWidth;
 				}
 			}
 			left = leftmost +                                  // leftmost possible
 				(availWidth / (levelI + forward + 1) * levelI) // indentation
-				* dis + (rtl ? availWidth - width : 0);        // rtl
-			eventTops[l] = top;
-			eventLefts[l] = left;
-			eventOuterWidths[l] = width;
-			eventOuterHeights[l] = bottom - top;
+				* dis + (rtl ? availWidth - outerWidth : 0);   // rtl
+			seg.top = top;
+			seg.left = left;
+			seg.outerWidth = outerWidth;
+			seg.outerHeight = bottom - top;
 			html +=
 				"<div class='" + className + event.className.join(' ') + "' style='position:absolute;z-index:8;top:" + top + "px;left:" + left + "px'>" +
 					"<a" + (event.url ? " href='" + htmlEscape(event.url) + "'" : '') + ">" +
+						"<span class='fc-event-bg'></span>" +
 						"<span class='fc-event-time'>" + htmlEscape(formatDates(event.start, event.end, view.option('timeFormat'))) + "</span>" +
 						"<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" +
-						"<span class='fc-event-bg'/>" +
 					"</a>" +
 					((event.editable || event.editable == undefined && options.editable) && !options.disableResizing && $.fn.resizable ?
 						"<div class='ui-resizable-handle ui-resizable-s'>=</div>"
 						: '') +
 				"</div>";
-		});
-		slotSegmentContainer.html(html);
-		eventElements = slotSegmentContainer.children();
+		}
+		slotSegmentContainer[0].innerHTML = html;
+		_eventElements = $.makeArray(slotSegmentContainer[0].childNodes); // TODO: look at .children() again
 		
-		// retrieve elements, run through eventRender callback, record outer-edge dimensions
-		eachLeaf(segCols, function(l, seg) {
+		// retrieve elements, run through eventRender callback, bind event handlers
+		for (i=0; i<segCnt; i++) {
+			seg = segs[i];
 			event = seg.event;
-			eventElement = eventElements.eq(l);
+			eventElement = $(_eventElements[i]);
 			triggerRes = view.trigger('eventRender', event, event, eventElement);
 			if (triggerRes === false) {
 				eventElement.remove();
@@ -509,28 +543,40 @@ function Agenda(element, options, methods) {
 					eventElement = $(triggerRes)
 						.css({
 							position: 'absolute',
-							top: eventTops[l],
-							left: eventLefts[l]
+							top: seg.top,
+							left: seg.left
 						})
 						.appendTo(slotSegmentContainer);
 				}
 				seg.element = eventElement;
-				eventVSides[l] = vsides(eventElement, true);
-				eventHSides[l] = hsides(eventElement, true);
-				eventTitlePositions[l] = eventElement.find('span.fc-event-title').position();
 				bootstrapSlotEventHandlers(event, seg, eventElement);
 				view.reportEventElement(event, eventElement);
 			}
-		});
+		}
+		
+		// record event sides and title positions
+		for (i=0; i<segCnt; i++) {
+			seg = segs[i];
+			if (eventElement = seg.element) {
+				val = vsideCache[key = seg.key = cssKey(eventElement[0])];
+				seg.vsides = val == undefined ? (vsideCache[key] = vsides(eventElement[0], true)) : val;
+				val = hsideCache[key];
+				seg.hsides = val == undefined ? (hsideCache[key] = hsides(eventElement[0], true)) : val;
+				titleSpan = eventElement.find('span.fc-event-title');
+				if (titleSpan.length) {
+					seg.titleTop = titleSpan[0].offsetTop;
+				}
+			}
+		}
 		
 		// set all positions/dimensions at once
-		eachLeaf(segCols, function(l, seg) {
+		for (i=0; i<segCnt; i++) {
+			seg = segs[i];
 			if (eventElement = seg.element) {
-				eventElement
-					.width(eventOuterWidths[l] - eventHSides[l])
-					.height(height = eventOuterHeights[l] - eventVSides[l]);
+				eventElement[0].style.width = seg.outerWidth - seg.hsides + 'px';
+				eventElement[0].style.height = (height = seg.outerHeight - seg.vsides) + 'px';
 				event = seg.event;
-				if (eventTitlePositions[l] && height - eventTitlePositions[l].top < 10) {
+				if (seg.titleTop != undefined && height - seg.titleTop < 10) {
 					// not enough room for title, put it in the time header
 					eventElement.find('span.fc-event-time')
 						.text(formatDate(event.start, view.option('timeFormat')) + ' - ' + event.title);
@@ -539,7 +585,7 @@ function Agenda(element, options, methods) {
 				}
 				view.trigger('eventAfterRender', event, event, eventElement);
 			}
-		});
+		}
 					
 	}
 	
@@ -874,7 +920,7 @@ function Agenda(element, options, methods) {
 	
 	// get the Y coordinate of the given time on the given day (both Date objects)
 	
-	function timePosition(day, time) { // both date object. day holds 00:00 of current day
+	function timePosition(day, time) { // both date objects. day holds 00:00 of current day
 		day = cloneDate(day, true);
 		if (time < addMinutes(cloneDate(day), minMinute)) {
 			return 0;
@@ -885,15 +931,18 @@ function Agenda(element, options, methods) {
 		var slotMinutes = options.slotMinutes,
 			minutes = time.getHours()*60 + time.getMinutes() - minMinute,
 			slotI = Math.floor(minutes / slotMinutes),
-			td = body.find('tr:eq(' + slotI + ') td'),
-			innerDiv = td.find('div');
+			slotTop = slotTopCache[slotI];
+		if (slotTop == undefined) {
+			slotTop = slotTopCache[slotI] = body.find('tr:eq(' + slotI + ') td div')[0].offsetTop;
+		}
 		return Math.max(0, Math.round(
-			innerDiv.position().top + topCorrect(td) - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes)
+			slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes)
 		));
 	}
 	
 	
 	
+	
 	function day2col(dayOfWeek) {
 		return ((dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt)*dis+dit;
 	}

+ 116 - 82
src/grid.js

@@ -205,7 +205,7 @@ function Grid(element, options, methods) {
 						' fc-today '+tm+'-state-highlight' :
 						' fc-not-today') + "'>" +
 						(showNumbers ? "<div class='fc-day-number'>" + d.getDate() + "</div>" : '') +
-						"<div class='fc-day-content'><div>&nbsp;</div></div></td>";
+						"<div class='fc-day-content'><div style='position:relative'>&nbsp;</div></div></td>";
 					addDays(d, 1);
 					if (nwe) {
 						skipWeekend(d);
@@ -216,7 +216,7 @@ function Grid(element, options, methods) {
 			tbody = $(s + "</tbody>").appendTo(table);
 			tbody.find('td').click(dayClick);
 			
-			segmentContainer = $("<div/>").appendTo(element);
+			segmentContainer = $("<div/>").appendTo(element); // style='position:absolute;top:0;left:0' // made it a little slower for some reason
 		
 		}else{ // NOT first time, reuse as many cells as possible
 		
@@ -236,7 +236,7 @@ function Grid(element, options, methods) {
 							tm + '-state-default fc-new fc-day' + (i*colCnt+j) +
 							(j==dit ? ' fc-leftmost' : '') + "'>" +
 							(showNumbers ? "<div class='fc-day-number'></div>" : '') +
-							"<div class='fc-day-content'><div>&nbsp;</div></div>" +
+							"<div class='fc-day-content'><div style='position:relative'>&nbsp;</div></div>" +
 							"</td>";
 						addDays(d, 1);
 						if (nwe) {
@@ -385,31 +385,42 @@ function Grid(element, options, methods) {
 	function compileSegs(events) {
 		var d1 = cloneDate(view.visStart),
 			d2 = addDays(cloneDate(d1), colCnt),
-			rows = [],
-			i=0;
-		for (; i<rowCnt; i++) {
-			rows.push(stackSegs(view.sliceSegs(events, $.map(events, visEventEnd), d1, d2)));
+			visEventsEnds = $.map(events, visEventEnd),
+			i, row,
+			j, level,
+			k, seg,
+			segs=[];
+		for (i=0; i<rowCnt; i++) {
+			row = stackSegs(view.sliceSegs(events, visEventsEnds, d1, d2));
+			for (j=0; j<row.length; j++) {
+				level = row[j];
+				for (k=0; k<level.length; k++) {
+					seg = level[k];
+					seg.row = i;
+					seg.level = j;
+					segs.push(seg);
+				}
+			}
 			addDays(d1, 7);
 			addDays(d2, 7);
 		}
-		return rows;
+		return segs;
 	}
 	
 	
 	
-	function renderSegs(segCols) {
+	function renderSegs(segs) {
 		_renderDaySegs(
-			segCols,
+			segs,
+			rowCnt,
 			view,
 			0,
 			viewWidth,
-			function(i) {
-				return tbody.find('tr:eq('+i+')');
-			},
+			function(i) { return tbody.find('tr:eq('+i+')') },
 			dayContentPositions.left,
 			dayContentPositions.right,
 			segmentContainer,
-			bootstrapEventHandlers
+			mouseoverBind
 		);
 	}
 	
@@ -426,11 +437,12 @@ function Grid(element, options, methods) {
 	
 	
 	
-	function bootstrapEventHandlers(event, seg, eventElement) {
+	
+	function mouseoverBind(event, seg, eventElement) {
 		function mouseover(ev) {
-			view.trigger('eventMouseover', this, event, ev);
 			eventElement.unbind('mouseover', mouseover);
-			setTimeout(function() { // because IE will immediately trigger eventElementHandlers's mouseover
+			view.trigger('eventMouseover', this, event, ev);
+			setTimeout(function() { // because IE will immediately trigger the new mouseover handlers
 				view.eventElementHandlers(event, eventElement);
 				if (event.editable || event.editable == undefined && options.editable) {
 					draggableEvent(event, eventElement);
@@ -507,34 +519,28 @@ function Grid(element, options, methods) {
 };
 
 
-function _renderDaySegs(segRows, view, minLeft, maxLeft, getTr, dayContentLeft, dayContentRight, segmentContainer, bootstrapEventHandlers) {
+function _renderDaySegs(segs, rowCnt, view, minLeft, maxLeft, getRow, dayContentLeft, dayContentRight, segmentContainer, mouseoverBind) {
 
 	var options=view.options,
 		rtl=options.isRTL,
+		i, segCnt=segs.length, seg,
 		event,
 		className,
-		left,
-		right,
-		eventLefts=[],
-		eventRights=[],
+		left, right,
 		html='',
-		eventElements,
+		_eventElements,
 		eventElement,
 		triggerRes,
-		eventOuterHeights=[],
-		eventHSides=[],
-		l=0,
-		i=0, len=segRows.length, levels,
-		td,
-		innerDiv,
-		top,
-		rowContentHeight,
-		j, segs,
-		levelHeight,
-		k, seg;
+		hsideCache={},
+		vmarginCache={},
+		key, val,
+		rowI, top, levelI, levelHeight,
+		rowDivs=[],
+		rowDivTops=[];
 		
 	// calculate desired position/dimensions, create html
-	eachLeaf(segRows, function(l, seg) {
+	for (i=0; i<segCnt; i++) {
+		seg = segs[i];
 		event = seg.event;
 		className = 'fc-event fc-event-hori ';
 		if (rtl) {
@@ -556,8 +562,6 @@ function _renderDaySegs(segRows, view, minLeft, maxLeft, getTr, dayContentLeft,
 			left = seg.isStart ? dayContentLeft(seg.start.getDay()) : minLeft;
 			right = seg.isEnd ? dayContentRight(seg.end.getDay()-1) : maxLeft;
 		}
-		eventLefts[l] = left;
-		eventRights[l] = right;
 		html +=
 			"<div class='" + className + event.className.join(' ') + "' style='position:absolute;z-index:8;left:"+left+"px'>" +
 				"<a" + (event.url ? " href='" + htmlEscape(event.url) + "'" : '') + ">" +
@@ -569,14 +573,17 @@ function _renderDaySegs(segRows, view, minLeft, maxLeft, getTr, dayContentLeft,
 					"<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" +
 				"</a>" +
 			"</div>";
-	});			
-	segmentContainer.html(html);
-	eventElements = segmentContainer.children();
+		seg.left = left;
+		seg.outerWidth = right - left;
+	}
+	segmentContainer[0].innerHTML = html;
+	_eventElements = $.makeArray(segmentContainer[0].childNodes); // TODO: look at .children() again
 	
-	// retrieve elements, run through eventRender callback, record outer-edge dimensions
-	eachLeaf(segRows, function(l, seg) {
+	// retrieve elements, run through eventRender callback, bind handlers
+	for (i=0; i<segCnt; i++) {
+		seg = segs[i];
+		eventElement = $(_eventElements[i]);
 		event = seg.event;
-		eventElement = eventElements.eq(l);
 		triggerRes = view.trigger('eventRender', event, event, eventElement);
 		if (triggerRes === false) {
 			eventElement.remove();
@@ -586,54 +593,81 @@ function _renderDaySegs(segRows, view, minLeft, maxLeft, getTr, dayContentLeft,
 				eventElement = $(triggerRes)
 					.css({
 						position: 'absolute',
-						left: eventLefts[l]
+						left: seg.left
 					})
 					.appendTo(segmentContainer);
 			}
-			seg.element = eventElement; // will be useful for future rerender optimizations
-			eventOuterHeights[l] = eventElement.outerHeight(true);
-			eventHSides[l] = hsides(eventElement, true);
-			bootstrapEventHandlers(event, seg, eventElement);
+			seg.element = eventElement;
+			mouseoverBind(event, seg, eventElement);
 			view.reportEventElement(event, eventElement);
 		}
-	});
+	}
 	
-	// set all positions/dimensions at once
-	for (; i<len; i++) {
-		levels = segRows[i];
-		td = getTr(i).find('td:first');
-		innerDiv = td.find('div.fc-day-content div')
-			.css('position', 'relative')
-			.height(''); // this is needed for IE7 to get an accurate position
-		top = innerDiv.position().top + topCorrect(td);
-		rowContentHeight = 0;
-		for (j=0; j<levels.length; j++) {
-			segs = levels[j];
-			levelHeight = 0;
-			for (k=0; k<segs.length; k++) {
-				seg = segs[k];
-				if (eventElement = seg.element) {
-					eventElement.css('top', top);
-					//IE6 right-to-left sort-of-off-by-one bug
-					//if (rtl && rtlLeftDiff == undefined) {
-					//	// bug in IE6 where offsets are miscalculated with direction:rtl
-					//	rtlLeftDiff = eventLefts[l] - eventElement.position().left;
-					//	if (rtlLeftDiff) {
-					//		eventElement.css('left', eventLefts[l] + rtlLeftDiff);
-					//	}
-					//}
-					eventElement.width(eventRights[l] - eventLefts[l] - eventHSides[l]);
-					event = seg.event;
-					view.trigger('eventAfterRender', event, event, eventElement);
-					levelHeight = Math.max(levelHeight, eventOuterHeights[l]);
-				}
-				l++;
+	// 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;
+		}
+	}
+	
+	// set event widths
+	for (i=0; i<segCnt; i++) {
+		seg = segs[i];
+		if (eventElement = seg.element) {
+			eventElement[0].style.width = seg.outerWidth - seg.hsides + 'px';
+		}
+	}
+	
+	// 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
+			);
+		}
+	}
+	
+	// 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++;
 			}
-			rowContentHeight += levelHeight;
-			top += levelHeight;
+			levelHeight = Math.max(levelHeight, seg.outerHeight||0);
+			seg.top = top;
+			i++;
+		}
+		rowDivs[rowI] = getRow(rowI).find('td:first div.fc-day-content > div') // > is optimal???
+			.height(top + levelHeight);
+	}
+	
+	// calculate row tops
+	for (rowI=0; rowI<rowCnt; rowI++) {
+		rowDivTops[rowI] = rowDivs[rowI][0].offsetTop;
+	}
+	
+	// set event tops
+	for (i=0; i<segCnt; i++) {
+		seg = segs[i];
+		if (eventElement = seg.element) {
+			eventElement[0].style.top = rowDivTops[seg.row] + seg.top + 'px';
+			event = seg.event;
+			view.trigger('eventAfterRender', event, event, eventElement);
 		}
-		innerDiv.height(rowContentHeight);
 	}
 	
 }
 
+
+function cssKey(_element) {
+	return _element.id + '/' + _element.className + '/' + _element.style.cssText.replace(/(^|;)\s*(top|left|width|height)\s*:[^;]*/ig, '');
+}
+
+

+ 2 - 2
src/main.js

@@ -219,7 +219,7 @@ $.fn.fullCalendar = function(options) {
 					contentWidth = content.width();
 					contentHeight = calculateContentHeight();
 				}
-				if (inc || !view.date || date < view.visStart || date >= view.visEnd) { // !view.date means it hasn't been rendered yet
+				if (inc || !view.date || date < view.start || date >= view.end) { // !view.date means it hasn't been rendered yet
 					fixContentSize();
 					view.render(date, inc || 0, contentWidth, contentHeight, function(callback) {
 						// dont refetch if new view contains the same events (or a subset)
@@ -307,7 +307,7 @@ $.fn.fullCalendar = function(options) {
 				return options.contentHeight;
 			}
 			else if (options.height) {
-				return options.height - (header ? header.height() : 0) - vsides(content);
+				return options.height - (header ? header.height() : 0) - vsides(content[0]);
 			}
 			return Math.round(contentWidth / Math.max(options.aspectRatio, .5));
 		}

+ 29 - 37
src/util.js

@@ -298,37 +298,42 @@ var dateFormatters = {
 -----------------------------------------------------------------------------*/
 
 function setOuterWidth(element, width, includeMargins) {
-	element.each(function() {
-		var e = $(this);
-		e.width(width - hsides(e, includeMargins));
+	element.each(function(i, _element) {
+		_element.style.width = width - hsides(_element, includeMargins) + 'px';
 	});
 }
 
-function hsides(e, includeMargins) {
-	return (parseInt(e.css('border-left-width')) || 0) +
-		(parseInt(e.css('padding-left')) || 0) +
-		(parseInt(e.css('padding-right')) || 0) +
-		(parseInt(e.css('border-right-width')) || 0) +
-		(includeMargins ?
-			(parseInt(e.css('margin-left')) || 0) + (parseInt(e.css('margin-right')) || 0)
-			: 0);
-}
-
 function setOuterHeight(element, height, includeMargins) {
-	element.each(function() {
-		var e = $(this);
-		e.height(height - vsides(e, includeMargins));
+	element.each(function(i, _element) {
+		_element.style.height = height - vsides(_element, includeMargins) + 'px';
 	});
 }
 
-function vsides(e, includeMargins) {
-	return (parseInt(e.css('border-top-width')) || 0) +
-		(parseInt(e.css('padding-top')) || 0) +
-		(parseInt(e.css('padding-bottom')) || 0) +
-		(parseInt(e.css('border-bottom-width')) || 0) +
-		(includeMargins ?
-			(parseInt(e.css('margin-top')) || 0) + (parseInt(e.css('margin-bottom')) || 0)
-			: 0);
+
+function hsides(_element, includeMargins) {
+	return (parseFloat(jQuery.curCSS(_element, 'paddingLeft', true)) || 0) +
+	       (parseFloat(jQuery.curCSS(_element, 'paddingRight', true)) || 0) +
+	       (parseFloat(jQuery.curCSS(_element, 'borderLeftWidth', true)) || 0) +
+	       (parseFloat(jQuery.curCSS(_element, 'borderRightWidth', true)) || 0) +
+	       (includeMargins ? hmargins(_element) : 0);
+}
+
+function hmargins(_element) {
+	return (parseFloat(jQuery.curCSS(_element, 'marginLeft', true)) || 0) +
+	       (parseFloat(jQuery.curCSS(_element, 'marginRight', true)) || 0);
+}
+
+function vsides(_element, includeMargins) {
+	return (parseFloat(jQuery.curCSS(_element, 'paddingTop', true)) || 0) +
+	       (parseFloat(jQuery.curCSS(_element, 'paddingBottom', true)) || 0) +
+	       (parseFloat(jQuery.curCSS(_element, 'borderTopWidth', true)) || 0) +
+	       (parseFloat(jQuery.curCSS(_element, 'borderBottomWidth', true)) || 0) +
+	       (includeMargins ? vmargins(_element) : 0);
+}
+
+function vmargins(_element) {
+	return (parseFloat(jQuery.curCSS(_element, 'marginTop', true)) || 0) +
+	       (parseFloat(jQuery.curCSS(_element, 'marginBottom', true)) || 0);
 }
 
 
@@ -455,18 +460,6 @@ function htmlEscape(s) {
 		.replace(/"/g, '&quot;')
 }
 
-function eachLeaf(node, callback, leafIndex, indexTrail) {
-	if (node.pop == arrayPop) { // is an array?
-		for (var i=0, len=node.length; i<len; i++) {
-			leafIndex = eachLeaf(node[i], callback, leafIndex||0, [i].concat(indexTrail||[]));
-		}
-		return leafIndex;
-	}
-	callback.apply(node, [leafIndex, node].concat(indexTrail));
-	return leafIndex + 1;
-}
-
-
 
 
 function HorizontalPositionCache(getElement) {
@@ -501,4 +494,3 @@ function HorizontalPositionCache(getElement) {
 
 
 
-

+ 1 - 1
src/view.js

@@ -105,7 +105,7 @@ var viewMethods = {
 		var elements = this.eventElementsByID[event._id],
 			i, len = elements.length;
 		for (i=0; i<len; i++) {
-			if (elements[i] != exceptElement) {
+			if (elements[i][0] != exceptElement[0]) { // AHAHAHAHAHAHAHAH
 				elements[i][funcName]();
 			}
 		}

+ 3 - 3
tests/many_events.html

@@ -9,15 +9,15 @@
 	$(document).ready(function() {
 		
 		$('#calendar').fullCalendar({
-			year: 2009,
-			month: 11,
+			year: 2010,
+			month: 0,
 			header: {
 				left: 'prev,next today',
 				center: 'title',
 				right: 'month,agendaWeek,basicWeek,agendaDay,basicDay'
 			},
 			editable: true,
-			events: 'many_events_json.txt'
+			events: "many_events_json.txt"
 		});
 		
 	});

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
tests/many_events_json.txt


+ 7 - 8
tests/triggers.html

@@ -50,14 +50,13 @@
 					return false;
 				}
 				else if (event.id == 777) {
-					return $("<div style='background:green'/>").text(event.title);
+					return $("<div style='background:green'>").text(event.title);
 				}
 				else if (event.id == 999) {
 					element.css('border-color', 'red');
 					//console.log('renderEvent (' + event.title + ') - ' + view.title);
 				}
 			},
-			
 			eventAfterRender: function(event, element, view) {
 				//console.log('after render for "' + event.title + '":');
 				//console.log(element);
@@ -99,9 +98,9 @@
 				console.log(dayDelta + ' days');
 				console.log(minuteDelta + ' minutes');
 				console.log('allday: ' + allDay);
-				/*setTimeout(function() {
-					revertFunc();
-				}, 2000);*/
+				//setTimeout(function() {
+				//	revertFunc();
+				//}, 2000);
 				//console.log(jsEvent);
 				//console.log(ui);
 				//console.log(view.title);
@@ -120,9 +119,9 @@
 				console.log('RESIZE!! ' + event.title);
 				console.log(dayDelta + ' days');
 				console.log(minuteDelta + ' minutes');
-				/*setTimeout(function() {
-					revertFunc();
-				}, 2000);*/
+				//setTimeout(function() {
+				//	revertFunc();
+				//}, 2000);
 				//console.log(jsEvent);
 				//console.log(ui);
 				//console.log(view.title);

+ 1 - 1
version.txt

@@ -1 +1 @@
-1.4.3
+1.4.4

Vissa filer visades inte eftersom för många filer har ändrats