Просмотр исходного кода

move things over to CoordChronoComponent

Adam Shaw 8 лет назад
Родитель
Сommit
a13cf80700

+ 6 - 6
src.json

@@ -21,16 +21,16 @@
     "common/GlobalEmitter.js",
     "common/MouseFollower.js",
     "common/ChronoComponent.js",
+    "common/CoordChronoComponent.js",
+    "common/CoordChronoComponent.day-click.js",
+    "common/CoordChronoComponent.day-selection.js",
+    "common/CoordChronoComponent.event-dragging.js",
+    "common/CoordChronoComponent.event-resizing.js",
+    "common/CoordChronoComponent.external-dropping.js",
     "common/FillSystemMixin.js",
     "common/EventRenderingUtilsMixin.js",
     "common/SegChronoComponentMixin.js",
     "common/Grid.js",
-    "common/Grid.day-click.js",
-    "common/Grid.day-selection.js",
-    "common/Grid.event-interaction.js",
-    "common/Grid.event-dragging.js",
-    "common/Grid.event-resizing.js",
-    "common/Grid.external-dropping.js",
     "common/DayTableMixin.js",
     "common/DayGrid.js",
     "common/DayGrid.events.js",

+ 39 - 0
src/common/ChronoComponent.js

@@ -245,6 +245,8 @@ var ChronoComponent = Model.extend({
 
 	// Hit Areas
 	// ---------------------------------------------------------------------------------------------------------------
+	// just because all ChronoComponents support this interface
+	// doesn't mean they need to have their own internal coord system. they can defer to sub-components.
 
 
 	hitsNeeded: function() {
@@ -493,6 +495,43 @@ var ChronoComponent = Model.extend({
 	},
 
 
+	/* Converting eventRange -> eventFootprint
+	------------------------------------------------------------------------------------------------------------------*/
+
+
+	eventRangesToEventFootprints: function(eventRanges) {
+		var eventFootprints = [];
+		var i;
+
+		for (i = 0; i < eventRanges.length; i++) {
+			eventFootprints.push.apply(eventFootprints,
+				this.eventRangeToEventFootprints(eventRanges[i])
+			);
+		}
+
+		return eventFootprints;
+	},
+
+
+	// Given an event's unzoned date range, return an array of eventSpan objects.
+	// eventSpan - { start, end, isStart, isEnd, otherthings... }
+	// Subclasses can override.
+	// Subclasses are obligated to forward eventRange.isStart/isEnd to the resulting spans.
+	// TODO: somehow more DRY with Calendar::eventRangeToEventFootprints
+	eventRangeToEventFootprints: function(eventRange) {
+		return [
+			new EventFootprint(
+				new ComponentFootprint(
+					eventRange.unzonedRange,
+					eventRange.eventDef.isAllDay()
+				),
+				eventRange.eventDef,
+				eventRange.eventInstance // might not exist
+			)
+		];
+	},
+
+
 	// Date Utils
 	// ---------------------------------------------------------------------------------------------------------------
 

+ 1 - 1
src/common/Grid.day-click.js → src/common/CoordChronoComponent.day-click.js

@@ -1,5 +1,5 @@
 
-Grid.mixin({
+CoordChronoComponent.mixin({
 
 	// Creates a listener that tracks the user's drag across day elements, for day clicking.
 	buildDayClickListener: function() {

+ 1 - 1
src/common/Grid.day-selection.js → src/common/CoordChronoComponent.day-selection.js

@@ -1,5 +1,5 @@
 
-Grid.mixin({
+CoordChronoComponent.mixin({
 
 	// Creates a listener that tracks the user's drag across day elements, for day selecting.
 	buildDaySelectListener: function() {

+ 2 - 2
src/common/Grid.event-dragging.js → src/common/CoordChronoComponent.event-dragging.js

@@ -1,9 +1,9 @@
 
 /*
-Wired up via Grid.event-interation.js by calling
+Wired up by calling
 buildSegDragListener
 */
-Grid.mixin({
+CoordChronoComponent.mixin({
 
 	isDraggingSeg: false, // is a segment being dragged? boolean
 

+ 2 - 2
src/common/Grid.event-resizing.js → src/common/CoordChronoComponent.event-resizing.js

@@ -1,9 +1,9 @@
 
 /*
-Wired up via Grid.event-interation.js by calling
+Wired up by calling
 startSegResize
 */
-Grid.mixin({
+CoordChronoComponent.mixin({
 
 	isResizingSeg: false, // is a segment being resized? boolean
 

+ 2 - 2
src/common/Grid.external-dropping.js → src/common/CoordChronoComponent.external-dropping.js

@@ -1,9 +1,9 @@
 
 /*
-Wired up via Grid.js by calling
+Wired up by calling
 externalDragStart
 */
-Grid.mixin({
+CoordChronoComponent.mixin({
 
 	isDraggingExternal: false, // jqui-dragging an external element? boolean
 

+ 213 - 7
src/common/Grid.event-interaction.js → src/common/CoordChronoComponent.js

@@ -1,11 +1,5 @@
 
-/*
-Contains:
-- event clicking/mouseover/mouseout
-- things that are common to event dragging AND resizing
-- event helper rendering
-*/
-Grid.mixin({
+var CoordChronoComponent = ChronoComponent.extend({
 
 	// self-config, overridable by subclasses
 	segSelector: '.fc-event-container > *', // what constitutes an event element?
@@ -17,6 +11,25 @@ Grid.mixin({
 	// TODO: port isTimeScale into same system?
 	largeUnit: null,
 
+	dayClickListener: null,
+	daySelectListener: null,
+	segDragListener: null,
+	segResizeListener: null,
+	externalDragListener: null,
+
+	hitsNeededDepth: 0, // necessary because multiple callers might need the same hits
+
+	// self-config, overridable by subclasses
+	hasDayInteractions: true, // can user click/select ranges of time?
+
+
+	constructor: function() {
+		ChronoComponent.apply(this, arguments);
+
+		this.dayClickListener = this.buildDayClickListener();
+		this.daySelectListener = this.buildDaySelectListener();
+	},
+
 
 	// Diffs the two dates, returning a duration, based on granularity of the grid
 	// TODO: port isTimeScale into this system?
@@ -30,6 +43,106 @@ Grid.mixin({
 	},
 
 
+	// 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() {
+		this.dayClickListener.endInteraction();
+		this.daySelectListener.endInteraction();
+
+		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
+		}
+	},
+
+
+	/* Rendering
+	------------------------------------------------------------------------------------------------------------------*/
+
+
+	// Sets the container element that the grid should render inside of.
+	// Does other DOM-related initializations.
+	setElement: function(el) {
+		ChronoComponent.prototype.setElement.apply(this, arguments);
+
+		if (this.hasDayInteractions) {
+			preventSelection(el);
+
+			this.bindDayHandler('touchstart', this.dayTouchStart);
+			this.bindDayHandler('mousedown', this.dayMousedown);
+		}
+
+		// attach event-element-related handlers. in Grid.events
+		// same garbage collection note as above.
+		this.bindSegHandlers();
+	},
+
+
+	// Removes the grid's container element from the DOM. Undoes any other DOM-related attachments.
+	// DOES NOT remove any content beforehand (doesn't clear events or call unrenderDates), unlike View
+	removeElement: function() {
+		ChronoComponent.prototype.removeElement.apply(this, arguments);
+
+		this.clearDragListeners();
+	},
+
+
+	unrenderEvents: function() {
+		ChronoComponent.prototype.unrenderEvents.apply(this, arguments);
+
+		this.clearDragListeners(); // we wanted to add this action to event rendering teardown
+	},
+
+
+	/* Binding
+	------------------------------------------------------------------------------------------------------------------*/
+
+
+	// Binds DOM handlers to elements that reside outside the grid, such as the document
+	bindGlobalHandlers: function() {
+		ChronoComponent.prototype.bindGlobalHandlers.apply(this, arguments);
+
+		this.listenTo($(document), {
+			dragstart: this.externalDragStart, // jqui
+			sortstart: this.externalDragStart // jqui
+		});
+	},
+
+
+	// Unbinds DOM handlers from elements that reside outside the grid
+	unbindGlobalHandlers: function() {
+		ChronoComponent.prototype.unbindGlobalHandlers.apply(this, arguments);
+
+		this.stopListeningTo($(document));
+	},
+
+
+	bindDayHandler: function(name, handler) {
+		var _this = this;
+
+		// attach a handler to the grid's root element.
+		// jQuery will take care of unregistering them when removeElement gets called.
+		this.el.on(name, function(ev) {
+			if (
+				!$(ev.target).is(
+					_this.segSelector + ',' + // directly on an event element
+					_this.segSelector + ' *,' + // within an event element
+					'.fc-more,' + // a "more.." link
+					'a[data-goto]' // a clickable nav link
+				)
+			) {
+				return handler.call(_this, ev);
+			}
+		});
+	},
+
+
 	// Attaches event-element-related handlers for *all* rendered event segments of the view.
 	bindSegHandlers: function() {
 		this.bindSegHandlersToEl(this.el);
@@ -62,6 +175,56 @@ Grid.mixin({
 	},
 
 
+	/* Handlers
+	------------------------------------------------------------------------------------------------------------------*/
+
+
+	// Process a mousedown on an element that represents a day. For day clicking and selecting.
+	dayMousedown: function(ev) {
+
+		// HACK
+		// This will still work even though bindDayHandler doesn't use GlobalEmitter.
+		if (GlobalEmitter.get().shouldIgnoreMouse()) {
+			return;
+		}
+
+		this.dayClickListener.startInteraction(ev);
+
+		if (this.opt('selectable')) {
+			this.daySelectListener.startInteraction(ev, {
+				distance: this.opt('selectMinDistance')
+			});
+		}
+	},
+
+
+	dayTouchStart: function(ev) {
+		var view = this.view;
+		var selectLongPressDelay;
+
+		// On iOS (and Android?) when a new selection is initiated overtop another selection,
+		// the touchend never fires because the elements gets removed mid-touch-interaction (my theory).
+		// HACK: simply don't allow this to happen.
+		// ALSO: prevent selection when an *event* is already raised.
+		if (view.isSelected || view.selectedEvent) {
+			return;
+		}
+
+		selectLongPressDelay = this.opt('selectLongPressDelay');
+		if (selectLongPressDelay == null) {
+			selectLongPressDelay = this.opt('longPressDelay'); // fallback
+		}
+
+		this.dayClickListener.startInteraction(ev);
+
+		if (this.opt('selectable')) {
+			this.daySelectListener.startInteraction(ev, {
+				delay: selectLongPressDelay
+			});
+		}
+	},
+
+
 	handleSegClick: function(seg, ev) {
 		var res = this.publiclyTrigger('eventClick', { // can return `false` to cancel
 			context: seg.el[0],
@@ -160,6 +323,10 @@ Grid.mixin({
 	},
 
 
+	/* Misc
+	------------------------------------------------------------------------------------------------------------------*/
+
+
 	// seg isn't draggable, but let's use a generic DragListener
 	// simply for the delay, so it can be selected.
 	// Has side effect of setting/unsetting `segDragListener`
@@ -244,6 +411,45 @@ Grid.mixin({
 		dummyInstance = dummyEvent.buildInstance();
 
 		return new EventFootprint(componentFootprint, dummyEvent, dummyInstance);
+	},
+
+
+	/* Hit Area
+	------------------------------------------------------------------------------------------------------------------*/
+
+
+	hitsNeeded: function() {
+		if (!(this.hitsNeededDepth++)) {
+			this.prepareHits();
+		}
+	},
+
+
+	hitsNotNeeded: function() {
+		if (this.hitsNeededDepth && !(--this.hitsNeededDepth)) {
+			this.releaseHits();
+		}
+	},
+
+
+	getSafeHitFootprint: function(hit) {
+		var footprint = this.getHitFootprint(hit);
+
+		if (!this.view.activeUnzonedRange.containsRange(footprint.unzonedRange)) {
+			return null;
+		}
+
+		return footprint;
+	},
+
+
+	getHitFootprint: function(hit) {
+	},
+
+
+	// Given position-level information about a date-related area within the grid,
+	// should return a jQuery element that best represents it. passed to dayClick callback.
+	getHitEl: function(hit) {
 	}
 
 });

+ 4 - 234
src/common/Grid.js

@@ -10,32 +10,17 @@ Contains:
 - initializing event rendering-related options
 */
 
-var Grid = FC.Grid = ChronoComponent.extend(SegChronoComponentMixin, {
-
-	// self-config, overridable by subclasses
-	hasDayInteractions: true, // can user click/select ranges of time?
+var Grid = FC.Grid = CoordChronoComponent.extend(SegChronoComponentMixin, {
 
 	view: null, // a View object
-	isRTL: null, // shortcut to the view's isRTL option
-
-	hitsNeededDepth: 0, // necessary because multiple callers might need the same hits
-
-	dayClickListener: null,
-	daySelectListener: null,
-	segDragListener: null,
-	segResizeListener: null,
-	externalDragListener: null,
 
 
 	constructor: function(view) {
 		this.view = view; // do this first, for opt()
 
-		ChronoComponent.call(this);
+		CoordChronoComponent.apply(this, arguments);
 
 		this.initFillSystem(); // TODO: SegChronoComponentMixin should be responsible
-
-		this.dayClickListener = this.buildDayClickListener();
-		this.daySelectListener = this.buildDaySelectListener();
 	},
 
 
@@ -69,224 +54,9 @@ var Grid = FC.Grid = ChronoComponent.extend(SegChronoComponentMixin, {
 
 
 	unrenderEvents: function() {
-		this.clearDragListeners(); // we wanted to add this action to event rendering teardown
-		SegChronoComponentMixin.unrenderEvents.apply(this, arguments);
-	},
-
-
-
-	/* Hit Area
-	------------------------------------------------------------------------------------------------------------------*/
-
-
-	hitsNeeded: function() {
-		if (!(this.hitsNeededDepth++)) {
-			this.prepareHits();
-		}
-	},
-
-
-	hitsNotNeeded: function() {
-		if (this.hitsNeededDepth && !(--this.hitsNeededDepth)) {
-			this.releaseHits();
-		}
-	},
-
-
-	getSafeHitFootprint: function(hit) {
-		var footprint = this.getHitFootprint(hit);
-
-		if (!this.view.activeUnzonedRange.containsRange(footprint.unzonedRange)) {
-			return null;
-		}
-
-		return footprint;
-	},
-
-
-	getHitFootprint: function(hit) {
-	},
-
-
-	// Given position-level information about a date-related area within the grid,
-	// should return a jQuery element that best represents it. passed to dayClick callback.
-	getHitEl: function(hit) {
-	},
-
-
-	/* Rendering
-	------------------------------------------------------------------------------------------------------------------*/
-
-
-	// Sets the container element that the grid should render inside of.
-	// Does other DOM-related initializations.
-	setElement: function(el) {
-		ChronoComponent.prototype.setElement.apply(this, arguments);
-
-		if (this.hasDayInteractions) {
-			preventSelection(el);
-
-			this.bindDayHandler('touchstart', this.dayTouchStart);
-			this.bindDayHandler('mousedown', this.dayMousedown);
-		}
-
-		// attach event-element-related handlers. in Grid.events
-		// same garbage collection note as above.
-		this.bindSegHandlers();
-	},
-
-
-	bindDayHandler: function(name, handler) {
-		var _this = this;
-
-		// attach a handler to the grid's root element.
-		// jQuery will take care of unregistering them when removeElement gets called.
-		this.el.on(name, function(ev) {
-			if (
-				!$(ev.target).is(
-					_this.segSelector + ',' + // directly on an event element
-					_this.segSelector + ' *,' + // within an event element
-					'.fc-more,' + // a "more.." link
-					'a[data-goto]' // a clickable nav link
-				)
-			) {
-				return handler.call(_this, ev);
-			}
-		});
-	},
-
+		CoordChronoComponent.prototype.unrenderEvents.apply(this, arguments);
 
-	// Removes the grid's container element from the DOM. Undoes any other DOM-related attachments.
-	// DOES NOT remove any content beforehand (doesn't clear events or call unrenderDates), unlike View
-	removeElement: function() {
-		ChronoComponent.prototype.removeElement.apply(this, arguments);
-
-		this.clearDragListeners();
-	},
-
-
-	/* Handlers
-	------------------------------------------------------------------------------------------------------------------*/
-
-
-	// Binds DOM handlers to elements that reside outside the grid, such as the document
-	bindGlobalHandlers: function() {
-		ChronoComponent.prototype.bindGlobalHandlers.apply(this, arguments);
-
-		this.listenTo($(document), {
-			dragstart: this.externalDragStart, // jqui
-			sortstart: this.externalDragStart // jqui
-		});
-	},
-
-
-	// Unbinds DOM handlers from elements that reside outside the grid
-	unbindGlobalHandlers: function() {
-		ChronoComponent.prototype.unbindGlobalHandlers.apply(this, arguments);
-
-		this.stopListeningTo($(document));
-	},
-
-
-	// Process a mousedown on an element that represents a day. For day clicking and selecting.
-	dayMousedown: function(ev) {
-
-		// HACK
-		// This will still work even though bindDayHandler doesn't use GlobalEmitter.
-		if (GlobalEmitter.get().shouldIgnoreMouse()) {
-			return;
-		}
-
-		this.dayClickListener.startInteraction(ev);
-
-		if (this.opt('selectable')) {
-			this.daySelectListener.startInteraction(ev, {
-				distance: this.opt('selectMinDistance')
-			});
-		}
-	},
-
-
-	dayTouchStart: function(ev) {
-		var view = this.view;
-		var selectLongPressDelay;
-
-		// On iOS (and Android?) when a new selection is initiated overtop another selection,
-		// the touchend never fires because the elements gets removed mid-touch-interaction (my theory).
-		// HACK: simply don't allow this to happen.
-		// ALSO: prevent selection when an *event* is already raised.
-		if (view.isSelected || view.selectedEvent) {
-			return;
-		}
-
-		selectLongPressDelay = this.opt('selectLongPressDelay');
-		if (selectLongPressDelay == null) {
-			selectLongPressDelay = this.opt('longPressDelay'); // fallback
-		}
-
-		this.dayClickListener.startInteraction(ev);
-
-		if (this.opt('selectable')) {
-			this.daySelectListener.startInteraction(ev, {
-				delay: selectLongPressDelay
-			});
-		}
-	},
-
-
-	// 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() {
-		this.dayClickListener.endInteraction();
-		this.daySelectListener.endInteraction();
-
-		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
-		}
-	},
-
-
-	/* Converting eventRange -> eventFootprint
-	------------------------------------------------------------------------------------------------------------------*/
-
-
-	eventRangesToEventFootprints: function(eventRanges) {
-		var eventFootprints = [];
-		var i;
-
-		for (i = 0; i < eventRanges.length; i++) {
-			eventFootprints.push.apply(eventFootprints,
-				this.eventRangeToEventFootprints(eventRanges[i])
-			);
-		}
-
-		return eventFootprints;
-	},
-
-
-	// Given an event's unzoned date range, return an array of eventSpan objects.
-	// eventSpan - { start, end, isStart, isEnd, otherthings... }
-	// Subclasses can override.
-	// Subclasses are obligated to forward eventRange.isStart/isEnd to the resulting spans.
-	// TODO: somehow more DRY with Calendar::eventRangeToEventFootprints
-	eventRangeToEventFootprints: function(eventRange) {
-		return [
-			new EventFootprint(
-				new ComponentFootprint(
-					eventRange.unzonedRange,
-					eventRange.eventDef.isAllDay()
-				),
-				eventRange.eventDef,
-				eventRange.eventInstance // might not exist
-			)
-		];
+		SegChronoComponentMixin.unrenderEvents.apply(this, arguments);
 	}
 
 });