2
0
Эх сурвалжийг харах

restructured HTML/CSS of event elements. solves issues 327 and 395

Adam Shaw 15 жил өмнө
parent
commit
afadc63b78

+ 272 - 273
src/agenda/AgendaEventRenderer.js

@@ -15,6 +15,9 @@ function AgendaEventRenderer() {
 	DayEventRenderer.call(t);
 	var opt = t.opt;
 	var trigger = t.trigger;
+	//var setOverflowHidden = t.setOverflowHidden;
+	var isEventDraggable = t.isEventDraggable;
+	var isEventResizable = t.isEventResizable;
 	var eventEnd = t.eventEnd;
 	var reportEvents = t.reportEvents;
 	var reportEventClear = t.reportEventClear;
@@ -171,13 +174,6 @@ function AgendaEventRenderer() {
 		for (i=0; i<segCnt; i++) {
 			seg = segs[i];
 			event = seg.event;
-			classes = ['fc-event', 'fc-event-vert'];
-			if (seg.isStart) {
-				classes.push('fc-corner-top');
-			}
-			if (seg.isEnd) {
-				classes.push('fc-corner-bottom');
-			}
 			top = timePosition(seg.start, seg.start);
 			bottom = timePosition(seg.start, seg.end);
 			colI = seg.col;
@@ -205,7 +201,7 @@ function AgendaEventRenderer() {
 			seg.left = left;
 			seg.outerWidth = outerWidth;
 			seg.outerHeight = bottom - top;
-			html += slotSegHtml(event, seg, classes);
+			html += slotSegHtml(event, seg);
 		}
 		slotSegmentContainer[0].innerHTML = html; // faster than html()
 		eventElements = slotSegmentContainer.children();
@@ -278,65 +274,75 @@ function AgendaEventRenderer() {
 	}
 	
 	
-	function slotSegHtml(event, seg, classes) {
-		classes = classes.concat(event.className, event.source.className);
+	function slotSegHtml(event, seg) {
+		var html = "<";
+		var url = event.url;
 		var skinCss = getSkinCss(event, opt);
 		var skinCssAttr = (skinCss ? " style='" + skinCss + "'" : '');
-		return "<div class='" + classes.join(' ') + "' style='position:absolute;z-index:8;top:" + seg.top + "px;left:" + seg.left + "px;" + skinCss + "'>" +
-			"<a class='fc-event-inner'" +
-				(event.url ? " href='" + htmlEscape(event.url) + "'" : '') + // good for escaping quotes?
-				skinCssAttr +
-				">" +
-				"<div class='fc-event-head'" + skinCssAttr + ">" +
-					"<div class='fc-event-time'>" +
-						htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
-					"</div>" +
-				"</div>" +
-				"<div class='fc-event-content'>" +
-					"<div class='fc-event-title'>" +
-						htmlEscape(event.title) +
-					"</div>" +
-				"</div>" +
-				"<div class='fc-event-bg'></div>" +
-			"</a>" +
-			((seg.isEnd &&
-				isEventEditable(event) &&
-				!opt('disableResizing') && // TODO: make like other source properties
-				$.fn.resizable)
-				?
-				"<div class='ui-resizable-handle ui-resizable-s'>=</div>"
-				:
-				''
-				) +
-		"</div>";
+		var classes = ['fc-event', 'fc-event-skin', 'fc-event-vert'];
+		if (isEventDraggable(event)) {
+			classes.push('fc-event-draggable');
+		}
+		if (seg.isStart) {
+			classes.push('fc-corner-top');
+		}
+		if (seg.isEnd) {
+			classes.push('fc-corner-bottom');
+		}
+		classes = classes.concat(event.className, event.source.className);
+		if (url) {
+			html += "a href='" + htmlEscape(event.url) + "'";
+		}else{
+			html += "div";
+		}
+		html +=
+			" class='" + classes.join(' ') + "'" +
+			" style='position:absolute;z-index:8;top:" + seg.top + "px;left:" + seg.left + "px;" + skinCss + "'" +
+			">" +
+			"<div class='fc-event-inner fc-event-skin'" + skinCssAttr + ">" +
+			"<div class='fc-event-head fc-event-skin'" + skinCssAttr + ">" +
+			"<div class='fc-event-time'>" +
+			htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
+			"</div>" +
+			"</div>" +
+			"<div class='fc-event-content'>" +
+			"<div class='fc-event-title'>" +
+			htmlEscape(event.title) +
+			"</div>" +
+			"</div>" +
+			"<div class='fc-event-bg'></div>" +
+			"</div>"; // close inner
+		if (seg.isEnd && isEventResizable(event)) {
+			html +=
+				"<div class='ui-resizable-handle ui-resizable-s'>=</div>";
+		}
+		html +=
+			"</" + (url ? "a" : "div") + ">";
+		return html;
 	}
 	
 	
 	function bindDaySeg(event, eventElement, seg) {
-		eventElementHandlers(event, eventElement);
-		if (isEventEditable(event)) {
+		if (isEventDraggable(event)) {
 			draggableDayEvent(event, eventElement, seg.isStart);
-			if (seg.isEnd) {
-				resizableDayEvent(event, eventElement, seg);
-			}
 		}
+		if (seg.isEnd && isEventResizable(event)) {
+			resizableDayEvent(event, eventElement, seg);
+		}
+		eventElementHandlers(event, eventElement);
+			// needs to be after, because resizableDayEvent might stopImmediatePropagation on click
 	}
 	
 	
 	function bindSlotSeg(event, eventElement, seg) {
-		eventElementHandlers(event, eventElement);
-		if (isEventEditable(event)) {
-			var timeElement = eventElement.find('span.fc-event-time');
+		var timeElement = eventElement.find('span.fc-event-time');
+		if (isEventDraggable(event)) {
 			draggableSlotEvent(event, eventElement, timeElement);
-			if (seg.isEnd) {
-				resizableSlotEvent(event, eventElement, timeElement);
-			}
 		}
-	}
-	
-	
-	function isEventEditable(event) {
-		return firstDefined(event.editable, event.source.editable, opt('editable'));
+		if (seg.isEnd && isEventResizable(event)) {
+			resizableSlotEvent(event, eventElement, timeElement);
+		}
+		eventElementHandlers(event, eventElement);
 	}
 	
 	
@@ -347,101 +353,98 @@ function AgendaEventRenderer() {
 	
 	// when event starts out FULL-DAY
 	
-	// TODO: bug when dragging an event that occupies first day, but is not the event's start (no rounded left side)
-	
-	// TODO: bug when dragging from day to slot, outer container doesn't seem to change height
-	
 	function draggableDayEvent(event, eventElement, isStart) {
-		if (!opt('disableDragging') && eventElement.draggable) {
-			var origWidth;
-			var revert;
-			var allDay=true;
-			var dayDelta;
-			var dis = opt('isRTL') ? -1 : 1;
-			var hoverListener = getHoverListener();
-			var colWidth = getColWidth();
-			var slotHeight = getSlotHeight();
-			var minMinute = getMinMinute();
-			eventElement.draggable({
-				zIndex: 9,
-				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, rowDelta, colDelta) {
-						clearOverlays();
-						if (cell) {
-							revert = false;
-							dayDelta = colDelta * dis;
-							if (!cell.row) {
-								// on full-days
-								renderDayOverlay(
-									addDays(cloneDate(event.start), dayDelta),
-									addDays(exclEndDay(event), 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,
-											slotHeight * Math.round(
-												(event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes'))
-												/ opt('slotMinutes')
-											)
-										);
-										eventElement.draggable('option', 'grid', [colWidth, 1]);
-										allDay = false;
-									}
-								}else{
-									revert = true;
+		var origWidth;
+		var revert;
+		var allDay=true;
+		var dayDelta;
+		var dis = opt('isRTL') ? -1 : 1;
+		var hoverListener = getHoverListener();
+		var colWidth = getColWidth();
+		var slotHeight = getSlotHeight();
+		var minMinute = getMinMinute();
+		eventElement.draggable({
+			zIndex: 9,
+			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, rowDelta, colDelta) {
+					clearOverlays();
+					if (cell) {
+						//setOverflowHidden(true);
+						revert = false;
+						dayDelta = colDelta * dis;
+						if (!cell.row) {
+							// on full-days
+							renderDayOverlay(
+								addDays(cloneDate(event.start), dayDelta),
+								addDays(exclEndDay(event), 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,
+										slotHeight * Math.round(
+											(event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes'))
+											/ opt('slotMinutes')
+										)
+									);
+									eventElement.draggable('option', 'grid', [colWidth, 1]);
+									allDay = false;
 								}
+							}else{
+								revert = true;
 							}
-							revert = revert || (allDay && !dayDelta);
-						}else{
-							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();
-						if ($.browser.msie) {
-							eventElement.css('filter', ''); // clear IE opacity side-effects
 						}
-						showEvents(event, eventElement);
+						revert = revert || (allDay && !dayDelta);
 					}else{
-						// changed!
-						eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link
-						var minuteDelta = 0;
-						if (!allDay) {
-							minuteDelta = Math.round((eventElement.offset().top - getBodyContent().offset().top) / slotHeight)
-								* opt('slotMinutes')
-								+ minMinute
-								- (event.start.getHours() * 60 + event.start.getMinutes());
-						}
-						eventDrop(this, event, dayDelta, minuteDelta, allDay, ev, ui);
+						resetElement();
+						//setOverflowHidden(false);
+						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();
+					if ($.browser.msie) {
+						eventElement.css('filter', ''); // clear IE opacity side-effects
+					}
+					showEvents(event, eventElement);
+				}else{
+					// changed!
+					var minuteDelta = 0;
+					if (!allDay) {
+						minuteDelta = Math.round((eventElement.offset().top - getBodyContent().offset().top) / slotHeight)
+							* opt('slotMinutes')
+							+ 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;
-				}
+				//setOverflowHidden(false);
+			}
+		});
+		function resetElement() {
+			if (!allDay) {
+				eventElement
+					.width(origWidth)
+					.height('')
+					.draggable('option', 'grid', null);
+				allDay = true;
 			}
 		}
 	}
@@ -450,102 +453,100 @@ function AgendaEventRenderer() {
 	// when event starts out IN TIMESLOTS
 	
 	function draggableSlotEvent(event, eventElement, timeElement) {
-		if (!opt('disableDragging') && eventElement.draggable) {
-			var origPosition;
-			var allDay=false;
-			var dayDelta;
-			var minuteDelta;
-			var prevMinuteDelta;
-			var dis = opt('isRTL') ? -1 : 1;
-			var hoverListener = getHoverListener();
-			var colCnt = getColCnt();
-			var colWidth = getColWidth();
-			var slotHeight = getSlotHeight();
-			eventElement.draggable({
-				zIndex: 9,
-				scroll: false,
-				grid: [colWidth, slotHeight],
-				axis: colCnt==1 ? 'y' : false,
-				opacity: opt('dragOpacity'),
-				revertDuration: opt('dragRevertDuration'),
-				start: function(ev, ui) {
-					trigger('eventDragStart', eventElement, event, ev, ui);
-					hideEvents(event, eventElement);
-					if ($.browser.msie) {
-						eventElement.find('div.fc-event-bg').hide(); // nested opacities mess up in IE, just hide
-					}
-					origPosition = eventElement.position();
-					minuteDelta = prevMinuteDelta = 0;
-					hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
-						eventElement.draggable('option', 'revert', !cell);
-						clearOverlays();
-						if (cell) {
-							dayDelta = colDelta * dis;
-							if (opt('allDaySlot') && !cell.row) {
-								// over full days
-								if (!allDay) {
-									// convert to temporary all-day event
-									allDay = true;
-									timeElement.hide();
-									eventElement.draggable('option', 'grid', null);
-								}
-								renderDayOverlay(
-									addDays(cloneDate(event.start), dayDelta),
-									addDays(exclEndDay(event), dayDelta)
-								);
-							}else{
-								// on slots
-								resetElement();
+		var origPosition;
+		var allDay=false;
+		var dayDelta;
+		var minuteDelta;
+		var prevMinuteDelta;
+		var dis = opt('isRTL') ? -1 : 1;
+		var hoverListener = getHoverListener();
+		var colCnt = getColCnt();
+		var colWidth = getColWidth();
+		var slotHeight = getSlotHeight();
+		eventElement.draggable({
+			zIndex: 9,
+			scroll: false,
+			grid: [colWidth, slotHeight],
+			axis: colCnt==1 ? 'y' : false,
+			opacity: opt('dragOpacity'),
+			revertDuration: opt('dragRevertDuration'),
+			start: function(ev, ui) {
+				trigger('eventDragStart', eventElement, event, ev, ui);
+				hideEvents(event, eventElement);
+				if ($.browser.msie) {
+					eventElement.find('div.fc-event-bg').hide(); // nested opacities mess up in IE, just hide
+				}
+				origPosition = eventElement.position();
+				minuteDelta = prevMinuteDelta = 0;
+				hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
+					eventElement.draggable('option', 'revert', !cell);
+					clearOverlays();
+					if (cell) {
+						dayDelta = colDelta * dis;
+						if (opt('allDaySlot') && !cell.row) {
+							// over full days
+							if (!allDay) {
+								// convert to temporary all-day event
+								allDay = true;
+								timeElement.hide();
+								eventElement.draggable('option', 'grid', null);
 							}
+							renderDayOverlay(
+								addDays(cloneDate(event.start), dayDelta),
+								addDays(exclEndDay(event), dayDelta)
+							);
+						}else{
+							// on slots
+							resetElement();
 						}
-					}, ev, 'drag');
-				},
-				drag: function(ev, ui) {
-					minuteDelta = Math.round((ui.position.top - origPosition.top) / slotHeight) * opt('slotMinutes');
-					if (minuteDelta != prevMinuteDelta) {
-						if (!allDay) {
-							updateTimeText(minuteDelta);
-						}
-						prevMinuteDelta = minuteDelta;
 					}
-				},
-				stop: function(ev, ui) {
-					var cell = hoverListener.stop();
-					clearOverlays();
-					trigger('eventDragStop', eventElement, event, ev, ui);
-					if (cell && (dayDelta || minuteDelta || allDay)) {
-						// changed!
-						eventDrop(this, event, dayDelta, allDay ? 0 : minuteDelta, allDay, ev, ui);
-					}else{
-						// either no change or out-of-bounds (draggable has already reverted)
-						resetElement();
-						eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position
-						updateTimeText(0);
-						if ($.browser.msie) { // TODO: dont use browser detection. base off of the presence of filter
-							eventElement
-								.css('filter', '') // clear IE opacity side-effects
-								.find('div.fc-event-bg')
-									.show();
-						}
-						showEvents(event, eventElement);
+				}, ev, 'drag');
+			},
+			drag: function(ev, ui) {
+				minuteDelta = Math.round((ui.position.top - origPosition.top) / slotHeight) * opt('slotMinutes');
+				if (minuteDelta != prevMinuteDelta) {
+					if (!allDay) {
+						updateTimeText(minuteDelta);
 					}
+					prevMinuteDelta = minuteDelta;
 				}
-			});
-			function updateTimeText(minuteDelta) {
-				var newStart = addMinutes(cloneDate(event.start), minuteDelta);
-				var newEnd;
-				if (event.end) {
-					newEnd = addMinutes(cloneDate(event.end), minuteDelta);
+			},
+			stop: function(ev, ui) {
+				var cell = hoverListener.stop();
+				clearOverlays();
+				trigger('eventDragStop', eventElement, event, ev, ui);
+				if (cell && (dayDelta || minuteDelta || allDay)) {
+					// changed!
+					eventDrop(this, event, dayDelta, allDay ? 0 : minuteDelta, allDay, ev, ui);
+				}else{
+					// either no change or out-of-bounds (draggable has already reverted)
+					resetElement();
+					eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position
+					updateTimeText(0);
+					if ($.browser.msie) { // TODO: dont use browser detection. base off of the presence of filter
+						eventElement
+							.css('filter', '') // clear IE opacity side-effects
+							.find('div.fc-event-bg')
+								.show();
+					}
+					showEvents(event, eventElement);
 				}
-				timeElement.text(formatDates(newStart, newEnd, opt('timeFormat')));
 			}
-			function resetElement() {
-				// convert back to original slot-event
-				if (allDay) {
-					timeElement.css('display', ''); // show() was causing display=inline
-					eventElement.draggable('option', 'grid', [colWidth, slotHeight]);
-					allDay = false;
-				}
+		});
+		function updateTimeText(minuteDelta) {
+			var newStart = addMinutes(cloneDate(event.start), minuteDelta);
+			var newEnd;
+			if (event.end) {
+				newEnd = addMinutes(cloneDate(event.end), minuteDelta);
+			}
+			timeElement.text(formatDates(newStart, newEnd, opt('timeFormat')));
+		}
+		function resetElement() {
+			// convert back to original slot-event
+			if (allDay) {
+				timeElement.css('display', ''); // show() was causing display=inline
+				eventElement.draggable('option', 'grid', [colWidth, slotHeight]);
+				allDay = false;
 			}
 		}
 	}
@@ -557,50 +558,48 @@ function AgendaEventRenderer() {
 	
 	
 	function resizableSlotEvent(event, eventElement, timeElement) {
-		if (!opt('disableResizing') && eventElement.resizable) {
-			var slotDelta, prevSlotDelta;
-			var slotHeight = getSlotHeight();
-			eventElement.resizable({
-				handles: {
-					s: 'div.ui-resizable-s'
-				},
-				grid: slotHeight,
-				start: function(ev, ui) {
-					slotDelta = prevSlotDelta = 0;
-					hideEvents(event, eventElement);
-					if ($.browser.msie && $.browser.version == '6.0') {
-						eventElement.css('overflow', 'hidden');
-					}
-					eventElement.css('z-index', 9);
-					trigger('eventResizeStart', this, event, ev, ui);
-				},
-				resize: function(ev, ui) {
-					// don't rely on ui.size.height, doesn't take grid into account
-					slotDelta = Math.round((Math.max(slotHeight, eventElement.height()) - ui.originalSize.height) / slotHeight);
-					if (slotDelta != prevSlotDelta) {
-						timeElement.text(
-							formatDates(
-								event.start,
-								(!slotDelta && !event.end) ? null : // no change, so don't display time range
-									addMinutes(eventEnd(event), opt('slotMinutes')*slotDelta),
-								opt('timeFormat')
-							)
-						);
-						prevSlotDelta = slotDelta;
-					}
-				},
-				stop: function(ev, ui) {
-					trigger('eventResizeStop', this, event, ev, ui);
-					if (slotDelta) {
-						eventResize(this, event, 0, opt('slotMinutes')*slotDelta, ev, ui);
-					}else{
-						eventElement.css('z-index', 8);
-						showEvents(event, eventElement);
-						// BUG: if event was really short, need to put title back in span
-					}
+		var slotDelta, prevSlotDelta;
+		var slotHeight = getSlotHeight();
+		eventElement.resizable({
+			handles: {
+				s: 'div.ui-resizable-s'
+			},
+			grid: slotHeight,
+			start: function(ev, ui) {
+				slotDelta = prevSlotDelta = 0;
+				hideEvents(event, eventElement);
+				if ($.browser.msie && $.browser.version == '6.0') {
+					eventElement.css('overflow', 'hidden');
 				}
-			});
-		}
+				eventElement.css('z-index', 9);
+				trigger('eventResizeStart', this, event, ev, ui);
+			},
+			resize: function(ev, ui) {
+				// don't rely on ui.size.height, doesn't take grid into account
+				slotDelta = Math.round((Math.max(slotHeight, eventElement.height()) - ui.originalSize.height) / slotHeight);
+				if (slotDelta != prevSlotDelta) {
+					timeElement.text(
+						formatDates(
+							event.start,
+							(!slotDelta && !event.end) ? null : // no change, so don't display time range
+								addMinutes(eventEnd(event), opt('slotMinutes')*slotDelta),
+							opt('timeFormat')
+						)
+					);
+					prevSlotDelta = slotDelta;
+				}
+			},
+			stop: function(ev, ui) {
+				trigger('eventResizeStop', this, event, ev, ui);
+				if (slotDelta) {
+					eventResize(this, event, 0, opt('slotMinutes')*slotDelta, ev, ui);
+				}else{
+					eventElement.css('z-index', 8);
+					showEvents(event, eventElement);
+					// BUG: if event was really short, need to put title back in span
+				}
+			}
+		});
 	}
 	
 

+ 4 - 3
src/agenda/AgendaView.js

@@ -695,6 +695,8 @@ function AgendaView(element, calendar, viewName) {
 								.appendTo(slotContent);
 						}
 					}else{
+						rect.isStart = true; // conside rect a "seg" now
+						rect.isEnd = true;   //
 						selectionHelper = $(slotSegHtml(
 							{
 								title: '',
@@ -704,11 +706,10 @@ function AgendaView(element, calendar, viewName) {
 								editable: false,
 								source: {}
 							},
-							rect,
-							['fc-event', 'fc-event-vert', 'fc-corner-top', 'fc-corner-bottom']
+							rect
 						));
 						if ($.browser.msie) {
-							selectionHelper.find('span.fc-event-bg').hide(); // nested opacities mess up in IE, just hide
+							selectionHelper.find('div.fc-event-bg').hide(); // nested opacities mess up in IE, just hide
 						}
 						selectionHelper.css('opacity', opt('dragOpacity'));
 					}

+ 46 - 42
src/basic/BasicEventRenderer.js

@@ -14,6 +14,9 @@ function BasicEventRenderer() {
 	DayEventRenderer.call(t);
 	var opt = t.opt;
 	var trigger = t.trigger;
+	//var setOverflowHidden = t.setOverflowHidden;
+	var isEventDraggable = t.isEventDraggable;
+	var isEventResizable = t.isEventResizable;
 	var reportEvents = t.reportEvents;
 	var reportEventClear = t.reportEventClear;
 	var eventElementHandlers = t.eventElementHandlers;
@@ -76,13 +79,14 @@ function BasicEventRenderer() {
 	
 	
 	function bindDaySeg(event, eventElement, seg) {
-		eventElementHandlers(event, eventElement);
-		if (firstDefined(event.editable, event.source.editable, opt('editable'))) {
+		if (isEventDraggable(event)) {
 			draggableDayEvent(event, eventElement);
-			if (seg.isEnd) {
-				resizableDayEvent(event, eventElement, seg);
-			}
 		}
+		if (seg.isEnd && isEventResizable(event)) {
+			resizableDayEvent(event, eventElement, seg);
+		}
+		eventElementHandlers(event, eventElement);
+			// needs to be after, because resizableDayEvent might stopImmediatePropagation on click
 	}
 	
 	
@@ -92,47 +96,47 @@ function BasicEventRenderer() {
 	
 	
 	function draggableDayEvent(event, eventElement) {
-		if (!opt('disableDragging') && eventElement.draggable) {
-			var hoverListener = getHoverListener();
-			var dayDelta;
-			eventElement.draggable({
-				zIndex: 9,
-				delay: 50,
-				opacity: opt('dragOpacity'),
-				revertDuration: opt('dragRevertDuration'),
-				start: function(ev, ui) {
-					trigger('eventDragStart', eventElement, event, ev, ui);
-					hideEvents(event, eventElement);
-					hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
-						eventElement.draggable('option', 'revert', !cell || !rowDelta && !colDelta);
-						clearOverlays();
-						if (cell) {
-							dayDelta = rowDelta*7 + colDelta * (opt('isRTL') ? -1 : 1);
-							renderDayOverlay(
-								addDays(cloneDate(event.start), dayDelta),
-								addDays(exclEndDay(event), dayDelta)
-							);
-						}else{
-							dayDelta = 0;
-						}
-					}, ev, 'drag');
-				},
-				stop: function(ev, ui) {
-					hoverListener.stop();
+		var hoverListener = getHoverListener();
+		var dayDelta;
+		eventElement.draggable({
+			zIndex: 9,
+			delay: 50,
+			opacity: opt('dragOpacity'),
+			revertDuration: opt('dragRevertDuration'),
+			start: function(ev, ui) {
+				trigger('eventDragStart', eventElement, event, ev, ui);
+				hideEvents(event, eventElement);
+				hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
+					eventElement.draggable('option', 'revert', !cell || !rowDelta && !colDelta);
 					clearOverlays();
-					trigger('eventDragStop', eventElement, event, ev, ui);
-					if (dayDelta) {
-						eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link
-						eventDrop(this, event, dayDelta, 0, event.allDay, ev, ui);
+					if (cell) {
+						//setOverflowHidden(true);
+						dayDelta = rowDelta*7 + colDelta * (opt('isRTL') ? -1 : 1);
+						renderDayOverlay(
+							addDays(cloneDate(event.start), dayDelta),
+							addDays(exclEndDay(event), dayDelta)
+						);
 					}else{
-						if ($.browser.msie) {
-							eventElement.css('filter', ''); // clear IE opacity side-effects
-						}
-						showEvents(event, eventElement);
+						//setOverflowHidden(false);
+						dayDelta = 0;
 					}
+				}, ev, 'drag');
+			},
+			stop: function(ev, ui) {
+				hoverListener.stop();
+				clearOverlays();
+				trigger('eventDragStop', eventElement, event, ev, ui);
+				if (dayDelta) {
+					eventDrop(this, event, dayDelta, 0, event.allDay, ev, ui);
+				}else{
+					if ($.browser.msie) {
+						eventElement.css('filter', ''); // clear IE opacity side-effects
+					}
+					showEvents(event, eventElement);
 				}
-			});
-		}
+				//setOverflowHidden(false);
+			}
+		});
 	}
 
 

+ 121 - 89
src/common/DayEventRenderer.js

@@ -11,6 +11,8 @@ function DayEventRenderer() {
 	// imports
 	var opt = t.opt;
 	var trigger = t.trigger;
+	var isEventDraggable = t.isEventDraggable;
+	var isEventResizable = t.isEventResizable;
 	var eventEnd = t.eventEnd;
 	var reportEventElement = t.reportEventElement;
 	var showEvents = t.showEvents;
@@ -119,6 +121,7 @@ function DayEventRenderer() {
 		var segCnt=segs.length;
 		var seg;
 		var event;
+		var url;
 		var classes;
 		var bounds = allDayBounds();
 		var minLeft = bounds.left;
@@ -132,7 +135,10 @@ function DayEventRenderer() {
 		for (i=0; i<segCnt; i++) {
 			seg = segs[i];
 			event = seg.event;
-			classes = ['fc-event', 'fc-event-hori'];
+			classes = ['fc-event', 'fc-event-skin', 'fc-event-hori'];
+			if (isEventDraggable(event)) {
+				classes.push('fc-event-draggable');
+			}
 			if (rtl) {
 				if (seg.isStart) {
 					classes.push('fc-corner-right');
@@ -157,34 +163,39 @@ function DayEventRenderer() {
 				right = seg.isEnd ? colContentRight(cols[1]) : maxLeft;
 			}
 			classes = classes.concat(event.className, event.source.className);
+			url = event.url;
 			skinCss = getSkinCss(event, opt);
+			if (url) {
+				html += "<a href='" + htmlEscape(url) + "'";
+			}else{
+				html += "<div";
+			}
 			html +=
-				"<div class='" + classes.join(' ') + "' " +
-					"style='position:absolute;z-index:8;left:"+left+"px;" + skinCss + "'" +
-					">" +
-					"<a class='fc-event-inner'" +
-						(event.url ? " href='" + htmlEscape(event.url) + "'" : '') +
-						(skinCss ? " style='" + skinCss + "'" : '') +
-						">" +
-						(!event.allDay && seg.isStart ?
-							"<span class='fc-event-time'>" +
-								htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
-							"</span>"
-						:'') +
-						"<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" +
-					"</a>" +
-					((seg.isEnd &&
-						firstDefined(event.editable, event.source.editable, opt('editable')) &&
-						!opt('disableResizing')) // TODO: make this like the other source options
-						?
-						"<div class='ui-resizable-handle ui-resizable-" + (rtl ? 'w' : 'e') + "'></div>"
-						:
-						''
-						) +
+				" class='" + classes.join(' ') + "'" +
+				" style='position:absolute;z-index:8;left:"+left+"px;" + skinCss + "'" +
+				">" +
+				"<div" +
+				" class='fc-event-inner fc-event-skin'" +
+				(skinCss ? " style='" + skinCss + "'" : '') +
+				">";
+			if (!event.allDay && seg.isStart) {
+				html +=
+					"<span class='fc-event-time'>" +
+					htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
+					"</span>";
+			}
+			html +=
+				"<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" +
 				"</div>";
+			if (seg.isEnd && isEventResizable(event)) {
+				html +=
+					"<div class='ui-resizable-handle ui-resizable-" + (rtl ? 'w' : 'e') + "'></div>";
+			}
+			html +=
+				"</" + (url ? "a" : "div" ) + ">";
 			seg.left = left;
 			seg.outerWidth = right - left;
-			cols.sort(cmp);
+			cols.sort(cmp); // is this still needed now that cols are always left-to-right?
 			seg.startCol = cols[0];
 			seg.endCol = cols[1] + 1;
 		}
@@ -368,78 +379,99 @@ function DayEventRenderer() {
 	
 	
 	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 rtl = opt('isRTL');
+		var direction = rtl ? 'w' : 'e';
+		var handle = element.find('div.ui-resizable-' + direction);
+		var isResizing = false;
+		
+		// TODO: look into using jquery-ui mouse widget for this stuff
+		disableTextSelection(element); // prevent native <a> selection for IE
+		element
+			.mousedown(function(ev) { // prevent native <a> selection for others
+				ev.preventDefault();
+			})
+			.click(function(ev) {
+				if (isResizing) {
+					ev.preventDefault(); // prevent link from being visited (only method that worked in IE6)
+					ev.stopImmediatePropagation(); // prevent fullcalendar eventClick handler from being called
+					                               // (eventElementHandlers needs to be bound after resizableDayEvent)
 				}
-				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*7 + c*dis+dit) - (origCell.row*7 + 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);
+			});
+		
+		handle.mousedown(function(ev) {
+			if (ev.which != 1) {
+				return; // needs to be left mouse button
+			}
+			isResizing = true;
+			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{
-							if (helpers) {
-								showEvents(event);
-								helpers.remove();
-								helpers = null;
-							}
+							c = Math.max(minCell.col, c);
 						}
-						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();
+					dayDelta = (r*7 + c*dis+dit) - (origCell.row*7 + origCell.col*dis+dit);
+					var newEnd = addDays(eventEnd(event), dayDelta, true);
 					if (dayDelta) {
-						eventResize(this, event, dayDelta, 0, ev);
-						// event redraw will clear helpers
+						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;
+						}
 					}
-					// otherwise, the drag handler already restored the old events
+					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', '');
+				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
+				
+				setTimeout(function() { // make this happen after the element's click event
+					isResizing = false;
+				},0);
+			}
+			
+		});
 	}
 	
 

+ 25 - 0
src/common/View.js

@@ -10,6 +10,9 @@ function View(element, calendar, viewName) {
 	t.name = viewName;
 	t.opt = opt;
 	t.trigger = trigger;
+	//t.setOverflowHidden = setOverflowHidden;
+	t.isEventDraggable = isEventDraggable;
+	t.isEventResizable = isEventResizable;
 	t.reportEvents = reportEvents;
 	t.eventEnd = eventEnd;
 	t.reportEventElement = reportEventElement;
@@ -55,6 +58,28 @@ function View(element, calendar, viewName) {
 	}
 	
 	
+	/*
+	function setOverflowHidden(bool) {
+		element.css('overflow', bool ? 'hidden' : '');
+	}
+	*/
+	
+	
+	function isEventDraggable(event) {
+		return isEventEditable(event) && !opt('disableDragging');
+	}
+	
+	
+	function isEventResizable(event) { // but also need to make sure the seg.isEnd == true
+		return isEventEditable(event) && !opt('disableResizing');
+	}
+	
+	
+	function isEventEditable(event) {
+		return firstDefined(event.editable, event.source.editable, opt('editable'));
+	}
+	
+	
 	
 	/* Event Data
 	------------------------------------------------------------------------------*/

+ 16 - 11
src/common/common.css

@@ -133,34 +133,39 @@
 
 /* Global Event Styles
 ------------------------------------------------------------------------*/
-
-.fc-event,                  /* TODO: change this to a fc-event-skin class (and change print.css) */
-.fc-event-inner,
-.fc-event-head {
-	border-color: #36c;     /* default BORDER color */
-	background-color: #36c; /* default BACKGROUND color */
-	color: #fff;            /* default TEXT color */
-	}
 	 
 .fc-event {
 	border-style: solid;
 	border-width: 0;
 	font-size: .85em;
+	cursor: default;
+	}
+	
+a.fc-event,
+.fc-event-draggable {
+	cursor: pointer;
+	}
+	
+a.fc-event {
+	text-decoration: none;
 	}
 	
 .fc-rtl .fc-event {
 	text-align: right;
 	}
 	
+.fc-event-skin {
+	border-color: #36c;     /* default BORDER color */
+	background-color: #36c; /* default BACKGROUND color */
+	color: #fff;            /* default TEXT color */
+	}
+	
 .fc-event-inner {
 	position: relative;
-	display: block; /* might be an <a> tag */
 	width: 100%;
 	height: 100%;
 	border-style: solid;
 	border-width: 0;
-	text-decoration: none;
-	cursor: pointer;
 	}
 	
 .fc-event-time,

+ 1 - 3
src/common/print.css

@@ -13,9 +13,7 @@
  /* Events
 -----------------------------------------------------*/
  
-.fc-event,
-.fc-event-inner,
-.fc-event-head {
+.fc-event-skin {
 	background: none !important;
 	color: #000 !important;
 	}

+ 1 - 0
src/main.css

@@ -110,6 +110,7 @@ html .fc,
 	
 .fc-view {
 	width: 100%; /* needed for view switching (when view is absolute) */
+	overflow: hidden;
 	}
 	
 	

+ 11 - 1
tests/sources_new.html

@@ -56,8 +56,11 @@ $(document).ready(function() {
 			right: 'month,agendaWeek,basicWeek,agendaDay,basicDay'
 		},
 		editable: true,
+		//disableResizing: true,
+		//disableDragging: true,
 		selectable: true,
 		selectHelper: true,
+		dragOpacity: .5,
 		eventSources: [
 		
 			{
@@ -67,6 +70,7 @@ $(document).ready(function() {
 				success: function(events) {
 					console.log('successfully loaded gcal event data!', events);
 				},
+				editable: true
 			},
 			
 			/*
@@ -143,7 +147,13 @@ $(document).ready(function() {
 				]
 			}
 			
-		]
+		],
+		eventClick: function(event) {
+			if (event.url) {
+				window.open(event.url);
+			}
+			return false;
+		}
 	});
 	
 });