Kaynağa Gözat

span -> footprint

Adam Shaw 8 yıl önce
ebeveyn
işleme
db7428d3c8

+ 2 - 2
src.json

@@ -44,8 +44,8 @@
     "locale.js",
     "Header.js",
     "models/UnzonedRange.js",
-    "models/Span.js",
-    "models/EventSpan.js",
+    "models/ComponentFootprint.js",
+    "models/EventFootprint.js",
     "models/EventRange.js",
     "models/EventDateProfile.js",
     "models/EventDateMutation.js",

+ 24 - 0
src/EventManager.js

@@ -1460,3 +1460,27 @@ Calendar.prototype._buildBusinessGroup = function(wholeDay, rawDefs, ignoreNoDow
 
 	return new EventInstanceGroup(eventInstances);
 };
+
+
+Calendar.prototype.buildMutatedEventInstanceGroup = function(eventId, eventMutation) {
+	var viewRange = this.getView().activeRange;
+	var defs = this.eventDefCollection.getById(eventId);
+	var i;
+	var allInstances = [];
+
+	for (i = 0; i < defs.length; i++) {
+		defCopy = defs[i].clone();
+
+		if (defCopy instanceof SingleEventDefinition) {
+
+			eventMutation.mutateSingleEventDefinition(defCopy);
+			eventInstances = defCopy.buildInstances(viewRange.start, viewRange.end);
+
+			allInstances.push.apply(allInstances,
+				eventInstances
+			);
+		}
+	}
+
+	return new EventInstanceGroup(allInstances);
+};

+ 9 - 6
src/agenda/AgendaView.js

@@ -280,9 +280,9 @@ var AgendaView = FC.AgendaView = View.extend({
 	// forward all hit-related method calls to the grids (dayGrid might not be defined)
 
 
-	getHitSpan: function(hit) {
+	getHitFootprint: function(hit) {
 		// TODO: hit.component is set as a hack to identify where the hit came from
-		return hit.component.getHitSpan(hit);
+		return hit.component.getHitFootprint(hit);
 	},
 
 
@@ -331,12 +331,15 @@ var AgendaView = FC.AgendaView = View.extend({
 
 
 	// A returned value of `true` signals that a mock "helper" event has been rendered.
-	renderDrag: function(dropLocation, seg) {
-		if (dropLocation.start.hasTime()) {
-			return this.timeGrid.renderDrag(dropLocation, seg);
+	renderDrag: function(eventRanges, seg) {
+		var isAllDay = eventRanges.length &&
+			eventRanges[0].eventInstance.eventDateProfile.isAllDay();
+
+		if (!isAllDay) {
+			return this.timeGrid.renderDrag(eventRanges, seg);
 		}
 		else if (this.dayGrid) {
-			return this.dayGrid.renderDrag(dropLocation, seg);
+			return this.dayGrid.renderDrag(eventRanges, seg);
 		}
 	},
 

+ 2 - 2
src/common/ChronoComponent.js

@@ -204,8 +204,8 @@ var ChronoComponent = Model.extend({
 	// Renders a visual indication of a event or external-element drag over the given drop zone.
 	// If an external-element, seg will be `null`.
 	// Must return elements used for any mock events.
-	renderDrag: function(dropLocation, seg) {
-		this.callChildren('renderDrag', dropLocation, seg);
+	renderDrag: function(eventRanges, seg) {
+		this.callChildren('renderDrag', eventRanges, seg);
 	},
 
 

+ 16 - 11
src/common/DayGrid.js

@@ -224,7 +224,7 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
 
 	// Slices up the given span (unzoned start/end with other misc data) into an array of segments
 	componentFootprintToSegs: function(componentFootprint) {
-		var segs = this.sliceRangeByRow({ start: componentFootprint.dateRange.getStart(), end: componentFootprint.dateRange.getEnd() });
+		var segs = this.sliceRangeByRow(componentFootprint.dateRange.getRange());
 		var i, seg;
 
 		for (i = 0; i < segs.length; i++) {
@@ -272,8 +272,13 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
 	},
 
 
-	getHitSpan: function(hit) {
-		return this.getCellRange(hit.row, hit.col);
+	getHitFootprint: function(hit) {
+		var range = this.getCellRange(hit.row, hit.col);
+
+		return new ComponentFootprint(
+			new UnzonedRange(range.start, range.end),
+			true // all-day?
+		);
 	},
 
 
@@ -312,18 +317,17 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
 
 	// Renders a visual indication of an event or external element being dragged.
 	// `eventLocation` has zoned start and end (optional)
-	renderDrag: function(eventLocation, seg) {
-		var eventSpans = this.eventToSpans(eventLocation);
+	renderDrag: function(eventRanges, seg) {
+		var eventFootprints = this.eventRangesToEventFootprints(eventRanges);
 		var i;
 
-		// always render a highlight underneath
-		for (i = 0; i < eventSpans.length; i++) {
-			this.renderHighlight(eventSpans[i]);
+		for (i = 0; i < eventFootprints.length; i++) {
+			this.renderHighlight(eventFootprints[i].componentFootprint);
 		}
 
 		// if a segment from the same calendar but another component is being dragged, render a helper event
 		if (seg && seg.component !== this) {
-			return this.renderEventLocationHelper(eventLocation, seg); // returns mock event elements
+			return this.renderEventLocationHelper(eventRanges, seg); // returns mock event elements
 		}
 	},
 
@@ -364,9 +368,10 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
 
 
 	// Renders a mock "helper" event. `sourceSeg` is the associated internal segment object. It can be null.
-	renderHelper: function(event, sourceSeg) {
+	renderHelper: function(eventRanges, sourceSeg) {
 		var helperNodes = [];
-		var segs = this.eventToSegs(event);
+		var eventFootprints = this.eventRangesToEventFootprints(eventRanges);
+		var segs = this.eventFootprintsToSegs(eventFootprints);
 		var rowStructs;
 
 		segs = this.renderFgSegEls(segs); // assigns each seg's el and returns a subset of segs that were rendered

+ 97 - 97
src/common/Grid.events.js

@@ -1,15 +1,6 @@
 
 /* Event-rendering and event-interaction methods for the abstract Grid class
 ----------------------------------------------------------------------------------------------------------------------
-
-Data Types:
-	event - { title, id, start, (end), whatever }
-	location - { start, (end), allDay }
-	rawEventRange - { start, end }
-	eventRange - { start, end, isStart, isEnd }
-	eventSpan - { start, end, isStart, isEnd, whatever }
-	eventSeg - { event, whatever }
-	seg - { whatever }
 */
 
 Grid.mixin({
@@ -26,23 +17,27 @@ Grid.mixin({
 
 	renderEventRanges: function(eventRanges) {
 		var i, eventRange;
+		var eventFootprints;
+		var eventSegs;
 		var eventRendering;
 		var bgSegs = [];
 		var fgSegs = [];
 
 		for (i = 0; i < eventRanges.length; i++) {
 			eventRange = eventRanges[i];
+			eventFootprints = this.eventRangeToEventFootprints(eventRange);
+			eventSegs = this.eventFootprintsToSegs(eventFootprints);
 			eventRendering = eventRange.eventInstance.eventDefinition.rendering;
 
 			// TODO: query up to event's source and calendar
 			if (eventRendering === 'background' || eventRendering === 'inverse-background') {
 				bgSegs.push.apply(bgSegs, // append
-					this.eventRangeToSegs(eventRange)
+					eventSegs
 				);
 			}
 			else {
 				fgSegs.push.apply(fgSegs, // append
-					this.eventRangeToSegs(eventRange)
+					eventSegs
 				);
 			}
 		}
@@ -133,9 +128,10 @@ Grid.mixin({
 	// Caller must ask if whole-day business hours are needed.
 	// If no `businessHours` configuration value is specified, assumes the calendar default.
 	buildBusinessHourSegs: function(wholeDay, businessHours) {
-		return this.eventRangesToSegs(
-			this.buildBusinessHourRanges(wholeDay, businessHours)
-		);
+		var eventRanges = this.buildBusinessHourRanges(wholeDay, businessHours);
+		var eventFootprints = this.eventRangesToEventFootprints(eventRanges);
+
+		return this.eventFootprintsToSegs(eventFootprints);
 	},
 
 
@@ -313,7 +309,7 @@ Grid.mixin({
 		var event = seg.event;
 		var isDragging;
 		var mouseFollower; // A clone of the original element that will move with the mouse
-		var dropLocation; // zoned event date properties
+		var eventMutation;
 
 		if (this.segDragListener) {
 			return this.segDragListener;
@@ -350,8 +346,9 @@ Grid.mixin({
 			},
 			hitOver: function(hit, isOrig, origHit) {
 				var isAllowed = true;
-				var origHitSpan;
-				var hitSpan;
+				var origFootprint;
+				var footprint;
+				var eventInstanceGroup;
 				var dragHelperEls;
 
 				// starting hit could be forced (DayGrid.limit)
@@ -360,24 +357,35 @@ Grid.mixin({
 				}
 
 				// hit might not belong to this grid, so query origin grid
-				origHitSpan = origHit.component.getSafeHitSpan(origHit);
-				hitSpan = hit.component.getSafeHitSpan(hit);
-
-				if (origHitSpan && hitSpan) {
-					dropLocation = _this.computeEventDrop(origHitSpan, hitSpan, event);
-					isAllowed = dropLocation && _this.isEventLocationAllowed(dropLocation, event);
+				origFootprint = origHit.component.getSafeHitFootprint(origHit);
+				footprint = hit.component.getSafeHitFootprint(hit);
+
+				if (origFootprint && footprint) {
+					eventMutation = _this.computeEventDropMutation(origFootprint, footprint);
+					console.log('eventMutation', eventMutation);
+					eventInstanceGroup = view.calendar.buildMutatedEventInstanceGroup(event._id, eventMutation);
+					isAllowed = Boolean(eventMutation); // TODO && _this.isEventLocationAllowed(dropLocation, event);
 				}
 				else {
 					isAllowed = false;
 				}
 
 				if (!isAllowed) {
-					dropLocation = null;
+					eventMutation = null;
 					disableCursor();
 				}
 
 				// if a valid drop location, have the subclass render a visual indication
-				if (dropLocation && (dragHelperEls = view.renderDrag(dropLocation, seg))) {
+				if (
+					eventMutation &&
+					(dragHelperEls = view.renderDrag(
+						eventInstanceGroup.buildRenderRanges(
+							new UnzonedRange(_this.start, _this.end),
+							view.calendar
+						),
+						seg
+					))
+				) {
 
 					dragHelperEls.addClass('fc-dragging');
 					if (!dragListener.isTouch) {
@@ -391,13 +399,14 @@ Grid.mixin({
 				}
 
 				if (isOrig) {
-					dropLocation = null; // needs to have moved hits to be a valid drop
+					// needs to have moved hits to be a valid drop
+					eventMutation = null;
 				}
 			},
 			hitOut: function() { // called before mouse moves to a different hit OR moved out of all hits
 				view.unrenderDrag(); // unrender whatever was done in renderDrag
 				mouseFollower.show(); // show in case we are moving out of all hits
-				dropLocation = null;
+				eventMutation = null;
 			},
 			hitDone: function() { // Called after a hitOut OR before a dragEnd
 				enableCursor();
@@ -406,15 +415,16 @@ Grid.mixin({
 				delete seg.component; // prevent side effects
 
 				// do revert animation if hasn't changed. calls a callback when finished (whether animation or not)
-				mouseFollower.stop(!dropLocation, function() {
+				mouseFollower.stop(!eventMutation, function() {
 					if (isDragging) {
 						view.unrenderDrag();
 						_this.segDragStop(seg, ev);
 					}
 
-					if (dropLocation) {
-						// no need to re-show original, will rerender all anyways. esp important if eventRenderWait
-						view.reportSegDrop(seg, dropLocation, _this.largeUnit, el, ev);
+					if (eventMutation) {
+						console.log('DO eventMutation', eventMutation);
+						//// no need to re-show original, will rerender all anyways. esp important if eventRenderWait
+						//view.reportSegDrop(seg, dropLocation, _this.largeUnit, el, ev);
 					}
 					else {
 						view.showEvent(event);
@@ -470,50 +480,41 @@ Grid.mixin({
 	},
 
 
-	// Given the spans an event drag began, and the span event was dropped, calculates the new zoned start/end/allDay
-	// values for the event. Subclasses may override and set additional properties to be used by renderDrag.
-	// A falsy returned value indicates an invalid drop.
-	// DOES NOT consider overlap/constraint.
-	computeEventDrop: function(startSpan, endSpan, event) {
-		var calendar = this.view.calendar;
-		var dragStart = startSpan.start;
-		var dragEnd = endSpan.start;
-		var delta;
-		var dropLocation; // zoned event date properties
-
-		if (dragStart.hasTime() === dragEnd.hasTime()) {
-			delta = this.diffDates(dragEnd, dragStart);
-
-			// if an all-day event was in a timed area and it was dragged to a different time,
-			// guarantee an end and adjust start/end to have times
-			if (event.allDay && durationHasTime(delta)) {
-				dropLocation = {
-					start: event.start.clone(),
-					end: calendar.getEventEnd(event), // will be an ambig day
-					allDay: false // for normalizeEventTimes
-				};
-				calendar.normalizeEventTimes(dropLocation);
+	// DOES NOT consider overlap/constraint
+	computeEventDropMutation: function(startFootprint, endFootprint) {
+		var date0 = startFootprint.dateRange.getStart();
+		var date1 = endFootprint.dateRange.getStart();
+		var clearEnd = false;
+		var forceTimed = false;
+		var forceAllDay = false;
+		var dateDelta;
+		var dateMutation;
+		var eventMutation;
+
+		if (startFootprint.isAllDay !== endFootprint.isAllDay) {
+			clearEnd = true;
+
+			if (endFootprint.isAllDay) {
+				forceAllDay = true;
+				date0.stripTime();
 			}
-			// othewise, work off existing values
 			else {
-				dropLocation = pluckEventDateProps(event);
-			}
-
-			dropLocation.start.add(delta);
-			if (dropLocation.end) {
-				dropLocation.end.add(delta);
+				forceTimed = true;
 			}
 		}
-		else {
-			// if switching from day <-> timed, start should be reset to the dropped date, and the end cleared
-			dropLocation = {
-				start: dragEnd.clone(),
-				end: null, // end should be cleared
-				allDay: !dragEnd.hasTime()
-			};
-		}
 
-		return dropLocation;
+		dateDelta = this.diffDates(date1, date0);
+
+		dateMutation = new EventDateMutation();
+		dateMutation.clearEnd = clearEnd;
+		dateMutation.forceTimed = forceTimed;
+		dateMutation.forceAllDay = forceAllDay;
+		dateMutation.dateDelta = dateDelta;
+
+		eventMutation = new EventMutation();
+		eventMutation.dateMutation = dateMutation;
+
+		return eventMutation;
 	},
 
 
@@ -638,7 +639,7 @@ Grid.mixin({
 	// `seg` is the internal segment object that is being dragged. If dragging an external element, `seg` is null.
 	// A truthy returned value indicates this method has rendered a helper element.
 	// Must return elements used for any mock events.
-	renderDrag: function(dropLocation, seg) {
+	renderDrag: function(eventRanges, seg) {
 		// subclasses must implement
 	},
 
@@ -1010,7 +1011,7 @@ Grid.mixin({
 	},
 
 
-	/* Converting events -> eventRange -> eventFootprint -> eventSegs
+	/* Converting eventRange -> eventFootprint -> eventSegs
 	------------------------------------------------------------------------------------------------------------------*/
 
 
@@ -1021,35 +1022,17 @@ Grid.mixin({
 	},
 
 
-	eventRangesToSegs: function(eventRanges) {
-		var segs = [];
-		var i, eventRange;
-
-		for (i = 0; i < eventRanges.length; i++) {
-			segs.push.apply(segs,
-				this.eventRangeToSegs(eventRanges[i])
-			);
-		}
-
-		return segs;
-	},
-
-
-	// Given an event's range (unzoned start/end), and the event itself,
-	// slice into segments (using the segSliceFunc function if specified)
-	// eventRange - { start, end, isStart, isEnd }
-	eventRangeToSegs: function(eventRange) {
-		var eventFootprints = this.eventRangeToEventFootprints(eventRange);
-		var segs = [];
+	eventRangesToEventFootprints: function(eventRanges) {
+		var eventFootprints = [];
 		var i;
 
-		for (i = 0; i < eventFootprints.length; i++) {
-			segs.push.apply(segs, // append to
-				this.eventFootprintToSegs(eventFootprints[i])
+		for (i = 0; i < eventRanges.length; i++) {
+			eventFootprints.push.apply(eventFootprints,
+				this.eventRangeToEventFootprints(eventRanges[i])
 			);
 		}
 
-		return segs;
+		return eventFootprints;
 	},
 
 
@@ -1061,12 +1044,29 @@ Grid.mixin({
 		return [
 			new EventFootprint(
 				eventRange.eventInstance,
-				new ComponentFootprint(eventRange.dateRange)
+				new ComponentFootprint(
+					eventRange.dateRange,
+					eventRange.eventInstance.eventDateProfile.isAllDay()
+				)
 			)
 		];
 	},
 
 
+	eventFootprintsToSegs: function(eventFootprints) {
+		var segs = [];
+		var i;
+
+		for (i = 0; i < eventFootprints.length; i++) {
+			segs.push.apply(segs,
+				this.eventFootprintToSegs(eventFootprints[i])
+			);
+		}
+
+		return segs;
+	},
+
+
 	// Given an event's span (unzoned start/end and other misc data), and the event itself,
 	// slices into segments and attaches event-derived properties to them.
 	// eventSpan - { start, end, isStart, isEnd, otherthings... }

+ 11 - 16
src/common/Grid.js

@@ -174,26 +174,23 @@ var Grid = FC.Grid = ChronoComponent.extend({
 	// Given coordinates from the topleft of the document, return data about the date-related area underneath.
 	// Can return an object with arbitrary properties (although top/right/left/bottom are encouraged).
 	// Must have a `grid` property, a reference to this current grid. TODO: avoid this
-	// The returned object will be processed by getHitSpan and getHitEl.
+	// The returned object will be processed by getHitFootprint and getHitEl.
 	queryHit: function(leftOffset, topOffset) {
 	},
 
 
-	// like getHitSpan, but returns null if the resulting span's range is invalid
-	getSafeHitSpan: function(hit) {
-		var hitSpan = this.getHitSpan(hit);
+	getSafeHitFootprint: function(hit) {
+		var footprint = this.getHitFootprint(hit);
 
-		if (!isRangeWithinRange(hitSpan, this.view.activeRange)) {
+		if (!isRangeWithinRange(footprint.dateRange.getRange(), this.view.activeRange)) {
 			return null;
 		}
 
-		return hitSpan;
+		return footprint;
 	},
 
 
-	// Given position-level information about a date-related area within the grid,
-	// should return an object with at least a start/end date. Can provide other information as well.
-	getHitSpan: function(hit) {
+	getHitFootprint: function(hit) {
 	},
 
 
@@ -447,10 +444,8 @@ var Grid = FC.Grid = ChronoComponent.extend({
 
 	// Renders a mock event at the given event location, which contains zoned start/end properties.
 	// Returns all mock event elements.
-	renderEventLocationHelper: function(eventLocation, sourceSeg) {
-		var fakeEvent = this.fabricateHelperEvent(eventLocation, sourceSeg);
-
-		return this.renderHelper(fakeEvent, sourceSeg); // do the actual rendering
+	renderEventLocationHelper: function(eventRanges, sourceSeg) {
+		return this.renderHelper(eventRanges, sourceSeg); // do the actual rendering
 	},
 
 
@@ -480,7 +475,7 @@ var Grid = FC.Grid = ChronoComponent.extend({
 	// Renders a mock event. Given zoned event date properties.
 	// Must return all mock event elements.
 	// TODO: have this in ChronoComponent
-	renderHelper: function(eventLocation, sourceSeg) {
+	renderHelper: function(eventRanges, sourceSeg) {
 		// subclasses must implement
 	},
 
@@ -540,8 +535,8 @@ var Grid = FC.Grid = ChronoComponent.extend({
 
 
 	// Renders an emphasis on the given date range. Given a span (unzoned start/end and other misc data)
-	renderHighlight: function(span) {
-		this.renderFill('highlight', this.spanToSegs(span));
+	renderHighlight: function(componentFootprint) {
+		this.renderFill('highlight', this.componentFootprintToSegs(componentFootprint));
 	},
 
 

+ 24 - 12
src/common/TimeGrid.js

@@ -220,7 +220,7 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
 	},
 
 
-	getHitSpan: function(hit) {
+	getHitFootprint: function(hit) {
 		var start = this.getCellDate(0, hit.col); // row=0
 		var time = this.computeSnapTime(hit.snap); // pass in the snap-index
 		var end;
@@ -228,7 +228,10 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
 		start.time(time);
 		end = start.clone().add(this.snapDuration);
 
-		return { start: start, end: end };
+		return new ComponentFootprint(
+			new UnzonedRange(start, end),
+			false // all-day?
+		);
 	},
 
 
@@ -359,21 +362,22 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
 
 	// Renders a visual indication of an event being dragged over the specified date(s).
 	// A returned value of `true` signals that a mock "helper" event has been rendered.
-	renderDrag: function(eventLocation, seg) {
-		var eventSpans;
+	renderDrag: function(eventRanges, seg) {
+		var eventFootprints;
 		var i;
 
 		if (seg) { // if there is event information for this drag, render a helper event
 
 			// returns mock event elements
 			// signal that a helper has been rendered
-			return this.renderEventLocationHelper(eventLocation, seg);
+			return this.renderEventLocationHelper(eventRanges);
 		}
 		else { // otherwise, just render a highlight
-			eventSpans = this.eventToSpans(eventLocation);
 
-			for (i = 0; i < eventSpans.length; i++) {
-				this.renderHighlight(eventSpans[i]);
+			eventFootprints = this.eventRangesToEventFootprints(eventRanges);
+
+			for (i = 0; i < eventFootprints.length; i++) {
+				this.renderHighlight(eventFootprints[i].componentFootprint);
 			}
 		}
 	},
@@ -407,8 +411,14 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
 
 
 	// Renders a mock "helper" event. `sourceSeg` is the original segment object and might be null (an external drag)
-	renderHelper: function(event, sourceSeg) {
-		return this.renderHelperSegs(this.eventToSegs(event), sourceSeg); // returns mock event elements
+	renderHelper: function(eventRanges, sourceSeg) {
+		var eventFootprints = this.eventRangesToEventFootprints(eventRanges);
+		var segs = this.eventFootprintsToSegs(eventFootprints);
+
+		return this.renderHelperSegs( // returns mock event elements
+			segs,
+			sourceSeg
+		);
 	},
 
 
@@ -505,8 +515,10 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
 	------------------------------------------------------------------------------------------------------------------*/
 
 
-	renderHighlight: function(span) {
-		this.renderHighlightSegs(this.spanToSegs(span));
+	renderHighlight: function(componentFootprint) {
+		this.renderHighlightSegs(
+			this.componentFootprintToSegs(componentFootprint)
+		);
 	},
 
 

+ 6 - 0
src/models/UnzonedRange.js

@@ -34,12 +34,18 @@ var UnzonedRange = Class.extend({
 		return newRange;
 	},
 
+	// hopefully we'll remove these...
+
 	getStart: function() {
 		return FC.moment.utc(this.startMs).stripZone();
 	},
 
 	getEnd: function() {
 		return FC.moment.utc(this.endMs).stripZone();
+	},
+
+	getRange: function() {
+		return { start: this.getStart(), end: this.getEnd() };
 	}
 
 });