Quellcode durchsuchen

fixed a bunch of incorrect sizing bugs, ie bugs

Adam Shaw vor 16 Jahren
Ursprung
Commit
e4ea11c3df
6 geänderte Dateien mit 268 neuen und 178 gelöschten Zeilen
  1. 35 29
      src/agenda.js
  2. 2 0
      src/css/main.css
  3. 18 27
      src/grid.js
  4. 104 122
      src/main.js
  5. 7 0
      src/util.js
  6. 102 0
      tests/liquidwidth.html

+ 35 - 29
src/agenda.js

@@ -21,7 +21,7 @@ setDefaults({
 
 views.agendaWeek = function(element, options) {
 	return new Agenda(element, options, {
-		render: function(date, delta, width, height, fetchEvents) {
+		render: function(date, delta) {
 			if (delta) {
 				addDays(date, delta * 7);
 			}
@@ -43,9 +43,7 @@ views.agendaWeek = function(element, options) {
 			);
 			this.renderAgenda(
 				options.weekends ? 7 : 5,
-				this.option('columnFormat'),
-				width, height,
-				fetchEvents
+				this.option('columnFormat')
 			);
 		}
 	});
@@ -53,7 +51,7 @@ views.agendaWeek = function(element, options) {
 
 views.agendaDay = function(element, options) {
 	return new Agenda(element, options, {
-		render: function(date, delta, width, height, fetchEvents) {
+		render: function(date, delta) {
 			if (delta) {
 				addDays(date, delta);
 				if (!options.weekends) {
@@ -65,9 +63,7 @@ views.agendaDay = function(element, options) {
 			this.end = this.visEnd = addDays(cloneDate(this.start), 1);
 			this.renderAgenda(
 				1,
-				this.option('columnFormat'),
-				width, height,
-				fetchEvents
+				this.option('columnFormat')
 			);
 		}
 	});
@@ -97,7 +93,8 @@ function Agenda(element, options, methods) {
 		renderEvents: renderEvents,
 		rerenderEvents: rerenderEvents,
 		clearEvents: clearEvents,
-		updateSize: updateSize,
+		setHeight: setHeight,
+		setWidth: setWidth,
 		shown: resetScroll,
 		defaultEventEnd: function(event) {
 			var start = cloneDate(event.start);
@@ -115,12 +112,12 @@ function Agenda(element, options, methods) {
 	-----------------------------------------------------------------------------*/
 	
 	
-	element.addClass('fc-agenda').css('position', 'relative');
+	element.addClass('fc-agenda');
 	if (element.disableSelection) {
 		element.disableSelection();
 	}
 	
-	function renderAgenda(c, colFormat, width, height, fetchEvents) {
+	function renderAgenda(c, colFormat) {
 		colCnt = c;
 		
 		// update option-derived variables
@@ -261,10 +258,6 @@ function Agenda(element, options, methods) {
 		
 		}
 		
-		updateSize(width, height);
-		resetScroll();
-		fetchEvents(renderEvents);
-		
 	};
 	
 	
@@ -284,21 +277,35 @@ function Agenda(element, options, methods) {
 	}
 	
 	
-	function updateSize(width, height) {
-		viewWidth = width;
+	function setHeight(height) {
 		viewHeight = height;
-		colContentPositions.clear();
 		slotTopCache = {};
 		
-		body.width(width);
 		body.height(height - head.height());
+		
+		slotHeight = body.find('tr:first div').height() + 1;
+		
+		bg.css({
+			top: head.find('tr').height(),
+			height: height
+		});
+		
+		resetScroll(); //TODO: not the best place for this
+	}
+	
+	
+	function setWidth(width) {
+		viewWidth = width;
+		colContentPositions.clear();
+		
+		body.width(width);
 		bodyTable.width('');
 		
 		var topTDs = head.find('tr:first th'),
 			stripeTDs = bg.find('td'),
-			contentWidth = slotSegmentContainer.width(); // body[0].clientWidth isn't reliable here in IE6
+			clientWidth = slotSegmentContainer.width(); // body[0].clientWidth isn't reliable here in IE6
 			
-		bodyTable.width(contentWidth);
+		bodyTable.width(clientWidth);
 		
 		// time-axis width
 		axisWidth = 0;
@@ -312,21 +319,20 @@ function Agenda(element, options, methods) {
 		);
 		
 		// column width
-		colWidth = Math.floor((contentWidth - axisWidth) / colCnt);
+		colWidth = Math.floor((clientWidth - axisWidth) / colCnt);
 		setOuterWidth(stripeTDs.slice(0, -1), colWidth);
 		setOuterWidth(topTDs.slice(1, -2), colWidth);
-		setOuterWidth(topTDs.slice(-2, -1), contentWidth - axisWidth - colWidth*(colCnt-1));
+		setOuterWidth(topTDs.slice(-2, -1), clientWidth - axisWidth - colWidth*(colCnt-1));
 		
 		bg.css({
-			top: head.find('tr').height(),
 			left: axisWidth,
-			width: contentWidth - axisWidth,
-			height: height
+			width: clientWidth - axisWidth
 		});
-		
-		slotHeight = body.find('tr:first div').height() + 1;
 	}
 	
+	
+	
+	
 	function slotClick(ev) {
 		var col = Math.floor((ev.pageX - bg.offset().left) / colWidth),
 			date = addDays(cloneDate(view.visStart), dit + dis*col),
@@ -448,7 +454,7 @@ function Agenda(element, options, methods) {
 				bindDaySegHandlers,
 				modifiedEventId
 			);
-			updateSize(viewWidth, viewHeight); // might have pushed the body down, so resize
+			setHeight(viewHeight); // might have pushed the body down, so resize
 		}
 	}
 	

+ 2 - 0
src/css/main.css

@@ -18,6 +18,7 @@
 	
 .fc {
 	direction: ltr;
+	text-align: left;
 	}
 	
 .fc table {
@@ -49,6 +50,7 @@ table.fc-header {
 	
 .fc-header-center {
 	width: 50%;
+	text-align: center;
 	}
 	
 .fc-header-center table {

+ 18 - 27
src/grid.js

@@ -8,7 +8,7 @@ setDefaults({
 
 views.month = function(element, options) {
 	return new Grid(element, options, {
-		render: function(date, delta, width, height, fetchEvents) {
+		render: function(date, delta) {
 			if (delta) {
 				addMonths(date, delta);
 				date.setDate(1);
@@ -43,9 +43,7 @@ views.month = function(element, options) {
 			this.renderGrid(
 				rowCnt, options.weekends ? 7 : 5,
 				this.option('columnFormat'),
-				true,
-				width, height,
-				fetchEvents
+				true
 			);
 		}
 	});
@@ -53,7 +51,7 @@ views.month = function(element, options) {
 
 views.basicWeek = function(element, options) {
 	return new Grid(element, options, {
-		render: function(date, delta, width, height, fetchEvents) {
+		render: function(date, delta) {
 			if (delta) {
 				addDays(date, delta * 7);
 			}
@@ -76,9 +74,7 @@ views.basicWeek = function(element, options) {
 			this.renderGrid(
 				1, options.weekends ? 7 : 5,
 				this.option('columnFormat'),
-				false,
-				width, height,
-				fetchEvents
+				false
 			);
 		}
 	});
@@ -86,7 +82,7 @@ views.basicWeek = function(element, options) {
 
 views.basicDay = function(element, options) {
 	return new Grid(element, options, {
-		render: function(date, delta, width, height, fetchEvents) {
+		render: function(date, delta) {
 			if (delta) {
 				addDays(date, delta);
 				if (!options.weekends) {
@@ -99,9 +95,7 @@ views.basicDay = function(element, options) {
 			this.renderGrid(
 				1, 1,
 				this.option('columnFormat'),
-				false,
-				width, height,
-				fetchEvents
+				false
 			);
 		}
 	});
@@ -135,7 +129,8 @@ function Grid(element, options, methods) {
 		renderEvents: renderEvents,
 		rerenderEvents: rerenderEvents,
 		clearEvents: clearEvents,
-		updateSize: updateSize,
+		setHeight: setHeight,
+		setWidth: setWidth,
 		defaultEventEnd: function(event) { // calculates an end if event doesnt have one, mostly for resizing
 			return cloneDate(event.start);
 		}
@@ -148,12 +143,12 @@ function Grid(element, options, methods) {
 	-----------------------------------------------------------------------------*/
 	
 	
-	element.addClass('fc-grid').css('position', 'relative');
+	element.addClass('fc-grid');
 	if (element.disableSelection) {
 		element.disableSelection();
 	}
 
-	function renderGrid(r, c, colFormat, showNumbers, width, height, fetchEvents) {
+	function renderGrid(r, c, colFormat, showNumbers) {
 		rowCnt = r;
 		colCnt = c;
 		
@@ -302,9 +297,6 @@ function Grid(element, options, methods) {
 			}
 		
 		}
-		
-		updateSize(width, height);
-		fetchEvents(renderEvents);
 	
 	};
 	
@@ -319,22 +311,18 @@ function Grid(element, options, methods) {
 	}
 	
 	
-	function updateSize(width, height) { // does not render/position the events
-		viewWidth = width;
+	
+	function setHeight(height) {
 		viewHeight = height;
-		dayContentPositions.clear();
-		
 		var leftTDs = tbody.find('tr td:first-child'),
 			tbodyHeight = viewHeight - thead.height(),
 			rowHeight1, rowHeight2;
-		
 		if (options.weekMode == 'variable') {
 			rowHeight1 = rowHeight2 = Math.floor(tbodyHeight / (rowCnt==1 ? 2 : 6));
 		}else{
 			rowHeight1 = Math.floor(tbodyHeight / rowCnt);
 			rowHeight2 = tbodyHeight - rowHeight1*(rowCnt-1);
 		}
-		
 		if (tdHeightBug == undefined) {
 			// bug in firefox where cell height includes padding
 			var tr = tbody.find('tr:first'),
@@ -342,7 +330,6 @@ function Grid(element, options, methods) {
 			td.height(rowHeight1);
 			tdHeightBug = rowHeight1 != td.height();
 		}
-		
 		if (tdHeightBug) {
 			leftTDs.slice(0, -1).height(rowHeight1);
 			leftTDs.slice(-1).height(rowHeight2);
@@ -350,12 +337,16 @@ function Grid(element, options, methods) {
 			setOuterHeight(leftTDs.slice(0, -1), rowHeight1);
 			setOuterHeight(leftTDs.slice(-1), rowHeight2);
 		}
-		
+	}
+	
+	
+	function setWidth(width) {
+		viewWidth = width;
+		dayContentPositions.clear();
 		setOuterWidth(
 			thead.find('th').slice(0, -1),
 			colWidth = Math.floor(viewWidth / colCnt)
 		);
-		
 	}
 
 	

+ 104 - 122
src/main.js

@@ -147,10 +147,17 @@ $.fn.fullCalendar = function(options) {
 		// element
 		var _element = this,
 			element = $(_element).addClass('fc'),
-			elementWidth,
-			content = $("<div class='fc-content " + tm + "-widget-content' style='position:relative'/>").prependTo(_element), // relative for ie6
-			contentWidth,
-			contentHeight;
+			elementOuterWidth,
+			content = $("<div class='fc-content " + tm + "-widget-content' style='position:relative'/>").prependTo(_element),
+			suggestedViewHeight,
+			resizeUID = 0,
+			ignoreWindowResize = 0,
+			date = new Date(),
+			viewName,  // the current view name (TODO: look into getting rid of)
+			view,      // the current view
+			viewInstances = {};
+			
+			
 			
 		if (options.isRTL) {
 			element.addClass('fc-rtl');
@@ -158,11 +165,6 @@ $.fn.fullCalendar = function(options) {
 		if (options.theme) {
 			element.addClass('ui-widget');
 		}
-		
-		// view managing
-		var date = new Date(),
-			viewName, view, // the current view
-			viewInstances = {};
 			
 		if (options.year != undefined && options.year != date.getFullYear()) {
 			date.setDate(1);
@@ -184,14 +186,19 @@ $.fn.fullCalendar = function(options) {
 		
 		function changeView(v) {
 			if (v != viewName) {
-				fixContentSize();
-				if (view) {
-					if (view.eventsChanged) {
-						eventsDirtyExcept(view);
-						view.eventsChanged = false;
+				ignoreWindowResize++;
+				
+				var oldView = view;
+				if (oldView) {
+					if (oldView.eventsChanged) {
+						eventsDirty();
+						oldView.eventDirty = oldView.eventsChanged = false;
 					}
-					view.element.hide();
+					setMinHeight(content, content.height()); // needs to be first
+					content.css('overflow', 'hidden');
+					oldView.element.hide();
 				}
+				
 				if (viewInstances[v]) {
 					(view = viewInstances[v]).element.show();
 					if (view.shown) {
@@ -199,51 +206,55 @@ $.fn.fullCalendar = function(options) {
 					}
 				}else{
 					view = viewInstances[v] = $.fullCalendar.views[v](
-						$("<div class='fc-view fc-view-" + v + "'/>").appendTo(content),
+						$("<div class='fc-view fc-view-" + v + "' style='position:relative'/>").appendTo(content),
 						options);
 				}
+				
 				if (header) {
 					// update 'active' view button
 					header.find('div.fc-button-' + viewName).removeClass(tm + '-state-active');
 					header.find('div.fc-button-' + v).addClass(tm + '-state-active');
 				}
+				
 				view.name = viewName = v;
 				render();
-				unfixContentSize();
+				if (oldView) {
+					content.css('overflow', ''); // needs to be first
+					setMinHeight(content, 0);
+				}
+				
+				ignoreWindowResize--;
 			}
 		}
 		
-		function render(inc, forceUpdateSize) {
-			if ((elementWidth = _element.offsetWidth) !== 0) { // visible on the screen
-				if (!contentHeight || forceUpdateSize) {
-					contentWidth = content.width();
-					contentHeight = calculateContentHeight();
-				}
-				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)
-						if (!eventStart || view.visStart < eventStart || view.visEnd > eventEnd) {
-							fetchEvents(callback);
-						}else{
-							callback(events); // no refetching
-						}
-					});
-					unfixContentSize();
-					view.date = cloneDate(date);
-				}
-				else if (view.sizeDirty || forceUpdateSize) {
-					view.updateSize(contentWidth, contentHeight);
-					view.clearEvents();
-					view.renderEvents(events);
+		function render(inc) {
+			if (_element.offsetWidth !== 0) { // visible on the screen
+				ignoreWindowResize++;
+				
+				if (!view.start || inc || date < view.start || date >= view.end) {
+					view.render(date, inc || 0); // responsible for clearing events
+					setSize();
+					if (!eventStart || view.visStart < eventStart || view.visEnd > eventEnd) {
+						fetchEvents(function(events) {
+							ignoreWindowResize++;
+							view.renderEvents(events);
+							ignoreWindowResize--;
+						});
+					}else{
+						view.renderEvents(events); // don't refetch
+					}
 				}
-				else if (view.eventsDirty) {
-					// ensure events are rerendered if another view messed with them
-					// pass in 'events' b/c event might have been added/removed
-					// executed on a changeView
+				else if (view.sizeDirty || view.eventsDirty) {
 					view.clearEvents();
+					if (view.sizeDirty) {
+						setSize();
+					}
 					view.renderEvents(events);
 				}
+				elementOuterWidth = element.outerWidth();
+				view.sizeDirty = false;
+				view.eventsDirty = false;
+				
 				if (header) {
 					// update title text
 					header.find('h2.fc-header-title').html(view.title);
@@ -255,65 +266,45 @@ $.fn.fullCalendar = function(options) {
 						header.find('div.fc-button-today').removeClass(tm + '-state-disabled');
 					}
 				}
-				view.sizeDirty = false;
-				view.eventsDirty = false;
+				
 				view.trigger('viewDisplay', _element);
+				ignoreWindowResize--;
 			}
 		}
 		
-		// marks other views' events as dirty
-		function eventsDirtyExcept(exceptView) { // TODO: otherViewsEventsDirty
-			$.each(viewInstances, function() {
-				if (this != exceptView) {
-					this.eventsDirty = true;
-				}
-			});
-		}
-		
 		// called when any event objects have been added/removed/changed, rerenders
 		function eventsChanged() {
+			eventsDirty();
 			view.clearEvents();
 			view.renderEvents(events);
-			eventsDirtyExcept(view);
+			view.eventsDirty = false;
 		}
 		
-		// marks other views' sizes as dirty
-		function sizesDirtyExcept(exceptView) {
+		// marks other views' events as dirty
+		function eventsDirty() {
 			$.each(viewInstances, function() {
-				if (this != exceptView) {
-					this.sizeDirty = true;
-				}
+				this.eventsDirty = true;
 			});
 		}
 		
 		// called when we know the element size has changed
-		function sizeChanged(fix) {
-			contentWidth = content.width();
-			contentHeight = calculateContentHeight();
-			if (fix) {
-				fixContentSize();
-			}
-			view.updateSize(contentWidth, contentHeight);
-			if (fix) {
-				unfixContentSize();
-			}
-			sizesDirtyExcept(view);
+		function sizeChanged() {
+			sizesDirty();
+			calcSize();
+			setSize();
 			view.rerenderEvents();
 		}
 		
-		// calculate what the height of the content should be
-		function calculateContentHeight() {
-			if (options.contentHeight) {
-				return options.contentHeight;
-			}
-			else if (options.height) {
-				return options.height - (header ? header.height() : 0) - vsides(content[0]);
-			}
-			return Math.round(contentWidth / Math.max(options.aspectRatio, .5));
+		// marks other views' sizes as dirty
+		function sizesDirty() {
+			$.each(viewInstances, function() {
+				this.sizeDirty = true;
+			});
 		}
 		
 		
 		
+		
 		/* Event Sources and Fetching
 		-----------------------------------------------------------------------------*/
 		
@@ -411,9 +402,9 @@ $.fn.fullCalendar = function(options) {
 		var publicMethods = {
 		
 			render: function() {
-				render(0, true); // true forces size to updated
-				sizesDirtyExcept(view);
-				eventsDirtyExcept(view);
+				sizesDirty();
+				eventsDirty();
+				render();
 			},
 			
 			changeView: changeView,
@@ -590,10 +581,7 @@ $.fn.fullCalendar = function(options) {
 				return events; // else, return all
 			},
 			
-			rerenderEvents: function() {
-				view.rerenderEvents(); 
-				eventsDirtyExcept(view);
-			},
+			rerenderEvents: eventsChanged, // TODO: think of renaming eventsChanged
 			
 			//
 			// Event Source
@@ -734,48 +722,39 @@ $.fn.fullCalendar = function(options) {
 		/* Resizing
 		-----------------------------------------------------------------------------*/
 		
-		var contentSizeFixed = false,
-			resizeCnt = 0;
 		
-		function fixContentSize() {
-			if (!contentSizeFixed) {
-				contentSizeFixed = true;
-				content.css({
-					overflow: 'hidden',
-					height: contentHeight
-				});
-				// TODO: previous action might have caused scrollbars
-				// which will make the window width more narrow, possibly changing the aspect ratio
+		function calcSize() {
+			if (options.contentHeight) {
+				suggestedViewHeight = options.contentHeight;
+			}
+			else if (options.height) {
+				suggestedViewHeight = options.height - (header ? header.height() : 0) - vsides(content[0]);
+			}
+			else {
+				suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5));
 			}
 		}
 		
-		function unfixContentSize() {
-			if (contentSizeFixed) {
-				content.css({
-					overflow: 'visible',
-					height: ''
-				});
-				if ($.browser.msie && ($.browser.version=='6.0' || $.browser.version=='7.0')) {
-					// in IE6/7 the inside of the content div was invisible
-					// bizarre hack to get this work... need both lines
-					content[0].clientHeight;
-					content.hide().show();
-				}
-				contentSizeFixed = false;
-			}
+		
+		function setSize() {
+			ignoreWindowResize++;
+			view.setHeight(suggestedViewHeight);
+			view.setWidth(content.width());
+			ignoreWindowResize--;
 		}
 		
+		
 		function windowResize() {
-			if (!contentSizeFixed) {
-				if (view.date) { // view has already been rendered
-					var rcnt = ++resizeCnt; // add a delay
-					setTimeout(function() {
-						if (rcnt == resizeCnt && !contentSizeFixed) {
-							var newWidth = element.width();
-							if (newWidth != elementWidth) {
-								elementWidth = newWidth;
-								sizeChanged(true);
+			if (!ignoreWindowResize) {
+				if (view.start) { // view has already been rendered
+					var uid = ++resizeUID;
+					setTimeout(function() { // add a delay
+						if (uid == resizeUID && !ignoreWindowResize) {
+							if (elementOuterWidth != (elementOuterWidth = element.outerWidth())) {
+								ignoreWindowResize++;
+								sizeChanged();
 								view.trigger('windowResize', _element);
+								ignoreWindowResize--;
 							}
 						}
 					}, 200);
@@ -789,8 +768,10 @@ $.fn.fullCalendar = function(options) {
 		
 		
 		// let's begin...
+		calcSize();
 		changeView(options.defaultView);
 		
+		
 		// in IE, when in 0x0 iframe, initial resize never gets called, so do this...
 		if ($.browser.msie && !$('body').width()) {
 			setTimeout(function() {
@@ -799,6 +780,7 @@ $.fn.fullCalendar = function(options) {
 				view.rerenderEvents(); // needed for IE 7 // TODO: could probably skip recompile
 			}, 0);
 		}
+
 	
 	});
 	

+ 7 - 0
src/util.js

@@ -338,6 +338,13 @@ function vmargins(_element) {
 
 
 
+
+function setMinHeight(element, h) {
+	element.css('min-height', h)[0].style.cssText += ';_height:' + (typeof h == 'number' ? h + 'px' : h);
+}
+
+
+
 /* Position Calculation
 -----------------------------------------------------------------------------*/
 // nasty bugs in opera 9.25

+ 102 - 0
tests/liquidwidth.html

@@ -0,0 +1,102 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<link rel='stylesheet' type='text/css' href='../examples/redmond/theme.css' />
+<script type='text/javascript' src='loader.js'></script>
+<script type='text/javascript'>
+
+	$(document).ready(function() {
+	
+		var date = new Date();
+		var d = date.getDate();
+		var m = date.getMonth();
+		var y = date.getFullYear();
+		
+		$('#calendar').fullCalendar({
+			//defaultView: 'agendaWeek',
+			
+			//weekMode: 'variable',
+			
+			header: {
+				left: 'prev,next today',
+				center: 'title',
+				right: 'month,agendaWeek,basicWeek,agendaDay,basicDay'
+			},
+			editable: true,
+			
+			events: [
+				{
+					title: 'All Day Event',
+					start: new Date(y, m, 1)
+				},
+				{
+					title: 'Long Event',
+					start: new Date(y, m, d-5),
+					end: new Date(y, m, d-2)
+				},
+				{
+					id: 999,
+					title: 'Repeating Event',
+					start: new Date(y, m, d-3, 16, 0),
+					allDay: false
+				},
+				{
+					id: 999,
+					title: 'Repeating Event',
+					start: new Date(y, m, d+4, 16, 0),
+					allDay: false
+				},
+				{
+					title: 'Meeting',
+					start: new Date(y, m, d, 10, 30),
+					allDay: false
+				},
+				{
+					title: 'Lunch',
+					start: new Date(y, m, d, 12, 5),
+					end: new Date(y, m, d, 14, 43),
+					allDay: false
+				},
+				{
+					title: 'Birthday Party',
+					start: new Date(y, m, d+1, 19, 0),
+					end: new Date(y, m, d+1, 22, 30),
+					allDay: false
+				},
+				{
+					title: 'Click for Google',
+					start: new Date(y, m, 28),
+					end: new Date(y, m, 29),
+					url: 'http://google.com/'
+				}
+			]
+			
+		});
+		
+	});
+
+</script>
+<style type='text/css'>
+
+	html {
+		overflow: auto;
+		}
+
+	body {
+		margin-top: 40px;
+		text-align: center;
+		font-size: 13px;
+		font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
+		}
+
+	#calendar {
+		width: 80%;
+		margin: 0 auto;
+		}
+
+</style>
+</head>
+<body>
+<div id='calendar'></div>
+</body>
+</html>