Răsfoiți Sursa

safeguards against bad dragging

Adam Shaw 10 ani în urmă
părinte
comite
92575096b5
2 a modificat fișierele cu 45 adăugiri și 30 ștergeri
  1. 13 15
      src/common/Grid.events.js
  2. 32 15
      src/common/Grid.js

+ 13 - 15
src/common/Grid.events.js

@@ -47,17 +47,7 @@ Grid.mixin({
 	// Unrenders all events currently rendered on the grid
 	unrenderEvents: function() {
 		this.handleSegMouseout(); // trigger an eventMouseout if user's mouse is over an event
-
-		// if an API method rerenders events mid-drag,
-		// or if for some reason the user action that should initiate the event doesn't fire
-		// (probably a touch device that locked the DOM while scrolling)
-		// then forcefully stop listening to dragging.
-		if (this.segDragListener) {
-			this.segDragListener.endInteraction(); // will clear this.segDragListener
-		}
-		if (this.segResizeListener) {
-			this.segResizeListener.endInteraction(); // will clear this.segResizeListener
-		}
+		this.clearDragListeners();
 
 		this.unrenderFgSegs();
 		this.unrenderBgSegs();
@@ -185,11 +175,16 @@ Grid.mixin({
 
 	// Attaches event-element-related handlers to the container element and leverage bubbling
 	bindSegHandlers: function() {
-		this.bindSegHandler('mouseenter', this.handleSegMouseover);
-		this.bindSegHandler('mouseleave', this.handleSegMouseout);
-		this.bindSegHandler('mousedown', this.handleSegMousedown);
+		if (FC.isTouchEnabled) {
+			this.bindSegHandler('touchstart', this.handleSegTouchStart);
+		}
+		else {
+			this.bindSegHandler('mouseenter', this.handleSegMouseover);
+			this.bindSegHandler('mouseleave', this.handleSegMouseout);
+			this.bindSegHandler('mousedown', this.handleSegMousedown);
+		}
+
 		this.bindSegHandler('click', this.handleSegClick);
-		this.bindSegHandler('touchstart', this.handleSegTouchStart);
 	},
 
 
@@ -249,6 +244,7 @@ Grid.mixin({
 		}
 
 		if (!isResizing && view.isEventDraggable(event)) {
+			this.clearDragListeners();
 			dragListener = this.buildSegDragListener(seg);
 
 			dragListener._dragStart = function() { // TODO: better way of binding
@@ -269,6 +265,7 @@ Grid.mixin({
 		var isResizing = this.startSegResize(seg, ev, { distance: 5 });
 
 		if (!isResizing && this.view.isEventDraggable(seg.event)) {
+			this.clearDragListeners();
 			this.buildSegDragListener(seg)
 				.startInteraction(ev, {
 					distance: 5
@@ -281,6 +278,7 @@ Grid.mixin({
 	// `dragOptions` are optional
 	startSegResize: function(seg, ev, dragOptions) {
 		if ($(ev.target).is('.fc-resizer') && this.view.isEventResizable(seg.event)) {
+			this.clearDragListeners();
 			this.buildSegResizeListener(seg, $(ev.target).is('.fc-start-resizer'))
 				.startInteraction(ev, dragOptions);
 			return true;

+ 32 - 15
src/common/Grid.js

@@ -35,7 +35,6 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
 		this.view = view;
 		this.isRTL = view.opt('isRTL');
 		this.elsByFill = {};
-		this.dayDragListener = this.buildDayDragListener();
 	},
 
 
@@ -171,8 +170,12 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
 		this.el = el;
 		preventSelection(el);
 
-		this.bindDayHandler('mousedown', this.dayMousedown);
-		this.bindDayHandler('touchstart', this.dayTouchStart);
+		if (FC.isTouchEnabled) {
+			this.bindDayHandler('touchstart', this.dayTouchStart);
+		}
+		else {
+			this.bindDayHandler('mousedown', this.dayMousedown);
+		}
 
 		// attach event-element-related handlers. in Grid.events
 		// same garbage collection note as above.
@@ -202,15 +205,7 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
 	// DOES NOT remove any content beforehand (doesn't clear events or call unrenderDates), unlike View
 	removeElement: function() {
 		this.unbindGlobalHandlers();
-
-		// if an API method somehow rerenders the grid w/o user action initiating drag end,
-		// then forcefully stop listening to dragging.
-		if (this.dayDragListener) {
-			this.dayDragListener.endInteraction(); // will clear this.dayDragListener
-		}
-		if (this.externalDragListener) {
-			this.externalDragListener.endInteraction(); // will clear this.externalDragListener
-		}
+		this.clearDragListeners();
 
 		this.el.remove();
 
@@ -258,14 +253,16 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
 
 	// Process a mousedown on an element that represents a day. For day clicking and selecting.
 	dayMousedown: function(ev) {
-		this.dayDragListener.startInteraction(ev, {
+		this.clearDragListeners();
+		this.buildDayDragListener().startInteraction(ev, {
 			//distance: 5, // needs more work if we want dayClick to fire correctly
 		});
 	},
 
 
 	dayTouchStart: function(ev) {
-		this.dayDragListener.startInteraction(ev, {
+		this.clearDragListeners();
+		this.buildDayDragListener().startInteraction(ev, {
 			delay: 1000
 		});
 	},
@@ -283,7 +280,7 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
 		// this listener tracks a mousedown on a day element, and a subsequent drag.
 		// if the drag ends on the same day, it is a 'dayClick'.
 		// if 'selectable' is enabled, this listener also detects selections.
-		var dragListener = new HitDragListener(this, {
+		var dragListener = this.dayDragListener = new HitDragListener(this, {
 			scroll: view.opt('dragScroll'),
 			dragStart: function() {
 				view.unselect(); // since we could be rendering a new selection, we want to clear any old one
@@ -324,6 +321,7 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
 					view.reportSelection(selectionSpan, ev);
 				}
 				enableCursor();
+				_this.dayDragListener = null;
 			}
 		});
 
@@ -331,6 +329,25 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
 	},
 
 
+	// Kills all in-progress dragging.
+	// Useful for when public API methods that result in re-rendering are invoked during a drag.
+	// Also useful for when touch devices misbehave and don't fire their touchend.
+	clearDragListeners: function() {
+		if (this.dayDragListener) {
+			this.dayDragListener.endInteraction(); // will clear this.dayDragListener
+		}
+		if (this.segDragListener) {
+			this.segDragListener.endInteraction(); // will clear this.segDragListener
+		}
+		if (this.segResizeListener) {
+			this.segResizeListener.endInteraction(); // will clear this.segResizeListener
+		}
+		if (this.externalDragListener) {
+			this.externalDragListener.endInteraction(); // will clear this.externalDragListener
+		}
+	},
+
+
 	/* Event Helper
 	------------------------------------------------------------------------------------------------------------------*/
 	// TODO: should probably move this to Grid.events, like we did event dragging / resizing