Explorar o código

fix some stuff

Adam Shaw %!s(int64=8) %!d(string=hai) anos
pai
achega
3a858f932f

+ 2 - 2
src/Calendar.business.js

@@ -14,14 +14,14 @@ Calendar.prototype.buildCurrentBusinessRanges = function(wholeDay) {
 	var eventPeriod = this.eventManager.currentPeriod;
 
 	if (eventPeriod) {
-		return new EventInstanceGroup(
+		return eventInstancesToEventRanges(
 			this.buildBusinessInstances(
 				wholeDay,
 				this.opt('businessHours'),
 				eventPeriod.start,
 				eventPeriod.end
 			)
-		).buildRanges();
+		);
 	}
 	else {
 		return [];

+ 2 - 6
src/Calendar.constraints.js

@@ -1,6 +1,5 @@
 
-Calendar.prototype.isEventInstanceGroupAllowed = function(eventInstanceGroup) {
-	var eventRangeGroup = eventInstanceGroup.buildRangeGroup();
+Calendar.prototype.isEventRangeGroupAllowed = function(eventRangeGroup) {
 	var eventDef = eventRangeGroup.getEventDef();
 	var eventFootprints = this.eventRangesToEventFootprints(eventRangeGroup.eventRanges);
 	var i;
@@ -220,14 +219,11 @@ function isOverlapEventInstancesAllowed(overlapEventFootprints, subjectEventInst
 Calendar.prototype.parseEventDefToEventRanges = function(eventInput) {
 	var eventPeriod = this.eventManager.currentPeriod;
 	var eventDef = EventDefParser.parse(eventInput, this.eventManager.stickySource);
-	var instanceGroup;
 
 	if (eventPeriod && eventDef) {
-		instanceGroup = new EventInstanceGroup(
+		return eventInstancesToEventRanges(
 			eventDef.buildInstances(eventPeriod.start, eventPeriod.end)
 		);
-
-		return instanceGroup.buildRanges();
 	}
 	else {
 		return [];

+ 1 - 1
src/Calendar.events-api.js

@@ -110,7 +110,7 @@ Calendar.mixin({
 		eventManager.freeze();
 
 		for (i = 0; i < eventIds.length; i++) {
-			eventManager.removeEventsById(eventIds[i]);
+			eventManager.removeEventDefsById(eventIds[i]);
 		}
 
 		eventManager.unfreeze();

+ 36 - 21
src/common/ChronoComponent.js

@@ -284,21 +284,29 @@ var ChronoComponent = Model.extend({
 
 
 	isEventStartEditable: function(event) {
-		return firstDefined(
-			event.startEditable,
-			(event.source || {}).startEditable,
-			this.opt('eventStartEditable'),
-			this.isEventGenerallyEditable(event)
-		);
+		var isEditable = event.startEditable;
+		if (isEditable == null) {
+			isEditable = event.source.startEditable;
+			if (isEditable == null) {
+				isEditable = this.opt('eventStartEditable');
+				if (isEditable == null) {
+					isEditable = this.isEventGenerallyEditable(event);
+				}
+			}
+		}
+		return isEditable;
 	},
 
 
 	isEventGenerallyEditable: function(event) {
-		return firstDefined(
-			event.editable,
-			(event.source || {}).editable,
-			this.opt('editable')
-		);
+		var isEditable = event.editable;
+		if (isEditable == null) {
+			isEditable = event.source.editable;
+			if (isEditable == null) {
+				isEditable = this.opt('editable');
+			}
+		}
+		return isEditable;
 	},
 
 
@@ -320,16 +328,23 @@ var ChronoComponent = Model.extend({
 
 	// Computes if the given event is allowed to be resized by the user at all
 	isEventResizable: function(event) {
-		var source = event.source || {};
-
-		return firstDefined(
-			event.durationEditable,
-			source.durationEditable,
-			this.opt('eventDurationEditable'),
-			event.editable,
-			source.editable,
-			this.opt('editable')
-		);
+		var isResizable = event.durationEditable;
+		if (isResizable == null) {
+			isResizable = event.source.durationEditable;
+			if (isResizable == null) {
+				isResizable = this.opt('eventDurationEditable');
+				if (isResizable == null) {
+					isResizable = event.editable;
+					if (isResizable == null) {
+						isResizable = event.source.editable;
+						if (isResizable == null) {
+							isResizable = this.opt('editable');
+						}
+					}
+				}
+			}
+		}
+		return isResizable;
 	},
 
 

+ 52 - 33
src/common/Grid.events.js

@@ -141,7 +141,6 @@ Grid.mixin({
 	// FOR RENDERING
 	buildBusinessHourRanges: function(wholeDay, businessHours) {
 		var calendar = this.view.calendar;
-		var instanceGroup;
 
 		if (businessHours == null) {
 			// fallback
@@ -149,14 +148,16 @@ Grid.mixin({
 			businessHours = calendar.opt('businessHours');
 		}
 
-		instanceGroup = new EventInstanceGroup(calendar.buildBusinessInstances(
-			wholeDay,
-			businessHours,
-			this.start,
-			this.end
-		));
-
-		return instanceGroup.buildRangeGroup().sliceRenderRanges(
+		return new EventRangeGroup(
+			eventInstancesToEventRanges(
+				calendar.buildBusinessInstances(
+					wholeDay,
+					businessHours,
+					this.start,
+					this.end
+				)
+			)
+		).sliceRenderRanges(
 			new UnzonedRange(this.start, this.end),
 			calendar
 		);
@@ -307,6 +308,8 @@ Grid.mixin({
 	buildSegDragListener: function(seg) {
 		var _this = this;
 		var view = this.view;
+		var calendar = view.calendar;
+		var eventManager = calendar.eventManager;
 		var el = seg.el;
 		var event = seg.event;
 		var isDragging;
@@ -350,7 +353,7 @@ Grid.mixin({
 				var isAllowed = true;
 				var origFootprint;
 				var footprint;
-				var eventInstanceGroup;
+				var mutatedEventRangeGroup;
 				var dragHelperEls;
 
 				// starting hit could be forced (DayGrid.limit)
@@ -366,8 +369,15 @@ Grid.mixin({
 					eventDefMutation = _this.computeEventDropMutation(origFootprint, footprint);
 
 					if (eventDefMutation) {
-						eventInstanceGroup = view.calendar.eventManager.buildMutatedEventInstanceGroup(event._id, eventDefMutation);
-						isAllowed = _this.isEventInstanceGroupAllowed(eventInstanceGroup);
+						mutatedEventRangeGroup = new EventRangeGroup(
+							eventInstancesToEventRanges(
+								eventManager.buildMutatedEventInstances(
+									eventManager.getEventDefByInternalId(event._id).id,
+									eventDefMutation
+								)
+							)
+						);
+						isAllowed = _this.isEventRangeGroupAllowed(mutatedEventRangeGroup);
 					}
 					else {
 						isAllowed = false;
@@ -386,14 +396,13 @@ Grid.mixin({
 				if (
 					eventDefMutation &&
 					(dragHelperEls = view.renderDrag(
-						eventInstanceGroup.buildRenderRanges(
+						mutatedEventRangeGroup.sliceRenderRanges(
 							new UnzonedRange(_this.start, _this.end),
-							view.calendar
+							calendar
 						),
 						seg
 					))
 				) {
-
 					dragHelperEls.addClass('fc-dragging');
 					if (!dragListener.isTouch) {
 						_this.applyDragOpacity(dragHelperEls);
@@ -573,16 +582,20 @@ Grid.mixin({
 			hitOver: function(hit) {
 				var isAllowed = true;
 				var hitFootprint = hit.component.getSafeHitFootprint(hit); // hit might not belong to this grid
-				var eventInstanceGroup;
+				var mutatedEventRangeGroup;
 
 				if (hitFootprint) {
 					singleEventDef = _this.computeExternalDrop(hitFootprint, meta);
 
 					if (singleEventDef) {
-						eventInstanceGroup = new EventInstanceGroup(singleEventDef.buildInstances());
+						mutatedEventRangeGroup = new EventRangeGroup(
+							eventInstancesToEventRanges(
+								singleEventDef.buildInstances()
+							)
+						);
 						isAllowed = meta.eventProps ? // isEvent?
-							_this.isEventInstanceGroupAllowed(eventInstanceGroup) :
-							_this.isExternalInstanceGroupAllowed(eventInstanceGroup);
+							_this.isEventRangeGroupAllowed(mutatedEventRangeGroup) :
+							_this.isExternalRangeGroupAllowed(mutatedEventRangeGroup);
 					}
 					else {
 						isAllowed = false;
@@ -599,11 +612,10 @@ Grid.mixin({
 
 				if (singleEventDef) {
 					_this.renderDrag( // called without a seg parameter
-						new EventInstanceGroup(singleEventDef.buildInstances())
-							.buildRenderRanges(
-								new UnzonedRange(_this.start, _this.end),
-								view.calendar
-							)
+						mutatedEventRangeGroup.sliceRenderRanges(
+							new UnzonedRange(_this.start, _this.end),
+							view.calendar
+						)
 					);
 				}
 			},
@@ -704,6 +716,7 @@ Grid.mixin({
 		var _this = this;
 		var view = this.view;
 		var calendar = view.calendar;
+		var eventManager = calendar.eventManager;
 		var el = seg.el;
 		var event = seg.event;
 		var isDragging;
@@ -725,7 +738,7 @@ Grid.mixin({
 				var isAllowed = true;
 				var origHitFootprint = _this.getSafeHitFootprint(origHit);
 				var hitFootprint = _this.getSafeHitFootprint(hit);
-				var eventInstanceGroup;
+				var mutatedEventRangeGroup;
 
 				if (origHitFootprint && hitFootprint) {
 					resizeMutation = isStart ?
@@ -733,8 +746,15 @@ Grid.mixin({
 						_this.computeEventEndResizeMutation(origHitFootprint, hitFootprint, event);
 
 					if (resizeMutation) {
-						eventInstanceGroup = calendar.eventManager.buildMutatedEventInstanceGroup(event._id, resizeMutation);
-						isAllowed = _this.isEventInstanceGroupAllowed(eventInstanceGroup);
+						mutatedEventRangeGroup = new EventRangeGroup(
+							eventInstancesToEventRanges(
+								eventManager.buildMutatedEventInstances(
+									eventManager.getEventDefByInternalId(event._id).id,
+									resizeMutation
+								)
+							)
+						);
+						isAllowed = _this.isEventRangeGroupAllowed(mutatedEventRangeGroup);
 					}
 					else {
 						isAllowed = false;
@@ -757,7 +777,7 @@ Grid.mixin({
 					view.hideEvent(event);
 
 					_this.renderEventResize(
-						eventInstanceGroup.buildRenderRanges(
+						mutatedEventRangeGroup.sliceRenderRanges(
 							new UnzonedRange(_this.start, _this.end),
 							calendar
 						),
@@ -1006,16 +1026,15 @@ Grid.mixin({
 	------------------------------------------------------------------------------------------------------------------*/
 
 
-	isEventInstanceGroupAllowed: function(eventInstanceGroup) {
-		return this.view.calendar.isEventInstanceGroupAllowed(eventInstanceGroup);
+	isEventRangeGroupAllowed: function(eventRangeGroup) {
+		return this.view.calendar.isEventRangeGroupAllowed(eventRangeGroup);
 	},
 
 
 	// when it's a completely anonymous external drag, no event.
-	isExternalInstanceGroupAllowed: function(eventInstanceGroup) {
+	isExternalRangeGroupAllowed: function(eventRangeGroup) {
 		var calendar = this.view.calendar;
-		var eventRanges = eventInstanceGroup.buildEventRanges(null, calendar); // TODO: fix signature
-		var eventFootprints = this.eventRangesToEventFootprints(eventRanges);
+		var eventFootprints = this.eventRangesToEventFootprints(eventRangeGroup.eventRanges);
 		var i;
 
 		for (i = 0; i < eventFootprints.length; i++) {

+ 12 - 12
src/common/View.js

@@ -710,12 +710,12 @@ var View = FC.View = ChronoComponent.extend({
 
 
 	reportEventDrop: function(legacyEvent, eventMutation, el, ev) {
-		var undoFunc = this.calendar.eventManager
-			.mutateEventsWithId(
-				EventManager.normalizeId(legacyEvent._id),
-				eventMutation,
-				this.calendar
-			);
+		var eventManager = this.calendar.eventManager;
+		var undoFunc = eventManager.mutateEventsWithId(
+			eventManager.getEventDefByInternalId(legacyEvent._id).id,
+			eventMutation,
+			this.calendar
+		);
 
 		this.triggerEventDrop(legacyEvent, eventMutation.dateDelta, undoFunc, el, ev);
 	},
@@ -767,12 +767,12 @@ var View = FC.View = ChronoComponent.extend({
 
 	// Must be called when an event in the view has been resized to a new length
 	reportEventResize: function(legacyEvent, eventMutation, el, ev) {
-		var undoFunc = this.calendar.eventManager
-			.mutateEventsWithId(
-				EventManager.normalizeId(legacyEvent._id),
-				eventMutation,
-				this.calendar
-			);
+		var eventManager = this.calendar.eventManager;
+		var undoFunc = eventManager.mutateEventsWithId(
+			eventManager.getEventDefByInternalId(legacyEvent._id).id,
+			eventMutation,
+			this.calendar
+		);
 
 		this.triggerEventResize(legacyEvent, eventMutation.endDelta, undoFunc, el, ev);
 	},

+ 57 - 65
src/models/EventManager.js

@@ -64,17 +64,25 @@ var EventManager = Class.extend(EmitterMixin, ListenerMixin, {
 
 
 	refetchSource: function(eventSource) {
-		if (this.currentPeriod) {
-			this.currentPeriod.purgeSource(eventSource, true); // isSilent=true
-			this.currentPeriod.requestSource(eventSource);
+		var currentPeriod = this.currentPeriod;
+
+		if (currentPeriod) {
+			currentPeriod.freeze();
+			currentPeriod.purgeSource(eventSource);
+			currentPeriod.requestSource(eventSource);
+			currentPeriod.thaw();
 		}
 	},
 
 
 	refetchAllSources: function() {
-		if (this.currentPeriod) {
-			this.currentPeriod.purgeAllSources(true); // isSilent=true
-			this.currentPeriod.requestSources(this.getSources());
+		var currentPeriod = this.currentPeriod;
+
+		if (currentPeriod) {
+			currentPeriod.freeze();
+			currentPeriod.purgeAllSources();
+			currentPeriod.requestSources(this.getSources());
+			currentPeriod.thaw();
 		}
 	},
 
@@ -183,56 +191,38 @@ var EventManager = Class.extend(EmitterMixin, ListenerMixin, {
 	// -----------------------------------------------------------------------------------------------------------------
 
 
-	addEventDef: function(eventDef, isSticky) {
-		if (isSticky) {
-			this.stickySource.addEventDef(eventDef);
-		}
-
+	getEventDefByInternalId: function(internalId) {
 		if (this.currentPeriod) {
-			this.currentPeriod.addEventDef(eventDef); // might release
+			return this.currentPeriod.getEventDefByInternalId(internalId);
 		}
 	},
 
 
-	removeEventsById: function(eventId) {
-		this.getSources().forEach(function(eventSource) {
-			eventSource.removeEventsById(eventId);
-		});
-
+	iterEventDefs: function(func) {
 		if (this.currentPeriod) {
-			this.currentPeriod.removeEventsById(eventId); // might release
+			this.currentPeriod.iterEventDefs(func);
 		}
 	},
 
 
-	getEventDefByInternalId: function(internalId) {
-		var foundEventDef = null;
-
-		// TODO: somehow break after first match
-		// TODO: somehow cache
-		this.iterEventDefs(function(eventDef) {
-			if (eventDef.internalId === internalId) {
-				foundEventDef = eventDef;
-			}
-		});
+	addEventDef: function(eventDef, isSticky) {
+		if (isSticky) {
+			this.stickySource.addEventDef(eventDef);
+		}
 
-		return foundEventDef;
+		if (this.currentPeriod) {
+			this.currentPeriod.addEventDef(eventDef); // might release
+		}
 	},
 
 
-	iterEventDefs: function(func) {
-		this.sources.each(function(source) {
-			if (source instanceof ArrayEventSource) {
-				source.iterEventDefs(func);
-			}
+	removeEventDefsById: function(eventId) {
+		this.getSources().forEach(function(eventSource) {
+			eventSource.removeEventDefsById(eventId);
 		});
 
 		if (this.currentPeriod) {
-			this.currentPeriod.iterEventDefs(function(eventDef) {
-				if (!(eventDef.source instanceof ArrayEventSource)) {
-					func(eventDef);
-				}
-			});
+			this.currentPeriod.removeEventDefsById(eventId); // might release
 		}
 	},
 
@@ -249,45 +239,47 @@ var EventManager = Class.extend(EmitterMixin, ListenerMixin, {
 
 
 	/*
-	Returns an undo function
+	Returns an undo function.
 	*/
 	mutateEventsWithId: function(eventDefId, eventDefMutation) {
-		var undoFuncs = [];
 		var currentPeriod = this.currentPeriod;
+		var eventDefs;
+		var undoFuncs = [];
 
-		// TODO: somehow use EventPeriod::getEventDefsById
-		this.iterEventDefs(function(eventDef) {
-			if (eventDef.id === eventDefId) {
-				if (eventDef instanceof SingleEventDef) {
-					undoFuncs.push(
-						eventDefMutation.mutateSingle(eventDef)
-					);
+		if (currentPeriod) {
+
+			currentPeriod.freeze();
+
+			eventDefs = currentPeriod.getEventDefsById(eventDefId);
+			eventDefs.forEach(function(eventDef) {
+				currentPeriod.removeEventDef(eventDef);
+				undoFuncs.push(eventDefMutation.mutateSingle(eventDef));
+				currentPeriod.addEventDef(eventDef);
+			});
+
+			currentPeriod.thaw();
+
+			return function() {
+				currentPeriod.freeze();
+
+				for (var i = 0; i < eventDefs.length; i++) {
+					currentPeriod.removeEventDef(eventDefs[i]);
+					undoFuncs[i]();
+					currentPeriod.addEventDef(eventDefs[i]);
 				}
-			}
-		});
 
-		if (currentPeriod && undoFuncs.length) {
-			// EventManager is responsible for triggering release
-			currentPeriod.tryRelease();
+				currentPeriod.thaw();
+			};
 		}
 
-		return function() {
-			for (var i = 0; i < undoFuncs.length; i++) {
-				undoFuncs[i]();
-			}
-
-			if (currentPeriod && undoFuncs.length) {
-				// EventManager is responsible for triggering release
-				currentPeriod.tryRelease();
-			}
-		};
+		return function() { };
 	},
 
 
 	/*
 	copies and then mutates
 	*/
-	buildMutatedEventInstanceGroup: function(eventDefId, eventDefMutation) {
+	buildMutatedEventInstances: function(eventDefId, eventDefMutation) {
 		var eventDefs = this.getEventDefsById(eventDefId);
 		var i;
 		var defCopy;
@@ -305,7 +297,7 @@ var EventManager = Class.extend(EmitterMixin, ListenerMixin, {
 			}
 		}
 
-		return new EventInstanceGroup(allInstances);
+		return allInstances;
 	},
 
 

+ 100 - 95
src/models/EventPeriod.js

@@ -12,6 +12,7 @@ var EventPeriod = Class.extend(EmitterMixin, {
 	stuntedReleaseCnt: 0,
 	releaseCnt: 0,
 
+	eventDefsByInternalId: null,
 	eventDefsById: null,
 	eventInstancesById: null,
 	eventRangesById: null,
@@ -22,6 +23,7 @@ var EventPeriod = Class.extend(EmitterMixin, {
 		this.end = end;
 		this.timezone = timezone;
 		this.requests = [];
+		this.eventDefsByInternalId = {};
 		this.eventDefsById = {};
 		this.eventInstancesById = {};
 		this.eventRangesById = {};
@@ -66,7 +68,7 @@ var EventPeriod = Class.extend(EmitterMixin, {
 	},
 
 
-	purgeSource: function(source, isSilent) {
+	purgeSource: function(source) {
 		var _this = this;
 
 		var removeCnt = removeMatching(this.requests, function(request) {
@@ -80,14 +82,14 @@ var EventPeriod = Class.extend(EmitterMixin, {
 		});
 
 		if (removeCnt) {
-			this.removeEventsBySource(source, isSilent); // might release
+			this.removeEventDefsBySource(source); // might release
 		}
 
 		return removeCnt;
 	},
 
 
-	purgeAllSources: function(isSilent) {
+	purgeAllSources: function() {
 		if (this.requests.length) {
 			this.requests.forEach(function(request) {
 				request.status = 'cancelled';
@@ -95,15 +97,41 @@ var EventPeriod = Class.extend(EmitterMixin, {
 
 			this.requests = [];
 			this.pendingCnt = 0;
-			this.removeAllEvents(isSilent); // might release
+			this.removeAllEventDefs(); // might release
 		}
 	},
 
 
-	// Event Def/Instance/Range ADDING
+	// Event Definitions
 	// -----------------------------------------------------------------------------------------------------------------
 
 
+	getEventDefByInternalId: function(eventDefInternalId) {
+		return this.eventDefsByInternalId[eventDefInternalId];
+	},
+
+
+	getEventDefsById: function(eventDefId) {
+		return this.eventDefsById[eventDefId] || [];
+	},
+
+
+	iterEventDefs: function(func) {
+		var eventDefsById = this.eventDefId;
+		var id;
+		var eventDefs;
+		var i;
+
+		for (id in eventDefsById) {
+			eventDefs = eventDefsById[id];
+
+			for (i = 0; i < eventDefs.length; i++) {
+				func(eventDefs[i]);
+			}
+		}
+	},
+
+
 	addEventDefs: function(eventDefs) {
 		for (var i = 0; i < eventDefs.length; i++) {
 			this.addEventDef(eventDefs[i]);
@@ -117,6 +145,8 @@ var EventPeriod = Class.extend(EmitterMixin, {
 		var eventInstances = eventDef.buildInstances(this.start, this.end);
 		var i;
 
+		this.eventDefsByInternalId[eventDef.internalId] = eventDef;
+
 		(eventDefsById[eventDefId] || (eventDefsById[eventDefId] = []))
 			.push(eventDef);
 
@@ -126,115 +156,46 @@ var EventPeriod = Class.extend(EmitterMixin, {
 	},
 
 
-	addEventInstance: function(eventInstance, eventDefId) {
-		var eventInstancesById = this.eventInstancesById;
-
-		(eventInstancesById[eventDefId] || (eventInstancesById[eventDefId] = []))
-			.push(eventInstance);
-
-		this.addEventRange(eventInstance.buildEventRange(), eventDefId);
-	},
-
-
-	addEventRange: function(eventRange, eventDefId) {
-		var eventRangesById = this.eventRangesById;
-
-		(eventRangesById[eventDefId] || (eventRangesById[eventDefId] = []))
-			.push(eventRange);
-
-		this.tryRelease();
-	},
-
-
-	// Event Def/Instance/Range REMOVING
-	// -----------------------------------------------------------------------------------------------------------------
-
-
-	removeEventsById: function(eventDefId) {
-		delete this.eventDefsById[eventDefId];
-		delete this.eventInstancesById[eventDefId];
-
-		if (eventDefId in this.eventRangesById) {
-			delete this.eventRangesById[eventDefId];
+	removeEventDefsById: function(eventDefId) {
+		var _this = this;
 
-			this.tryRelease();
-		}
+		this.getEventDefsById(eventDefId).forEach(function(eventDef) {
+			_this.removeEventDef(eventDef);
+		});
 	},
 
 
-	removeEventsBySource: function(source, isSilent) {
-		var eventDefsById = this.eventDefsById;
-		var eventInstancesById = this.eventInstancesById;
-		var eventRangesById = this.eventRangesById;
-		var id;
-		var removeCnt = 0;
-
-		function matchEventDef(eventDef) {
-			return eventDef.source === source;
-		}
-
-		function matchEventInstance(eventInstance) {
-			return eventInstance.def.source === source;
-		}
-
-		function matchEventRange(eventRange) {
-			return eventRange.eventInstance.def.source === source;
-		}
-
-		for (id in eventDefsById) {
-			removeMatching(eventDefsById[id], matchEventDef);
-		}
-
-		for (id in eventInstancesById) {
-			removeMatching(eventInstancesById[id], matchEventInstance);
-		}
-
-		for (id in eventRangesById) {
-			removeCnt += removeMatching(eventRangesById[id], matchEventRange);
-		}
+	removeEventDefsBySource: function(source) {
+		var _this = this;
 
-		if (removeCnt && !isSilent) {
-			this.tryRelease();
-		}
+		this.iterEventDefs(function(eventDef) {
+			if (eventDef.source === source) {
+				_this.removeEventDef(eventDef);
+			}
+		});
 	},
 
 
-	removeAllEvents: function(isSilent) {
-		var hasAny = !$.isEmptyObject(this.eventRangesById);
-
+	removeAllEventDefs: function() {
+		this.eventDefsByInternalId = {};
 		this.eventDefsById = {};
 		this.eventInstancesById = {};
 		this.eventRangesById = {};
-
-		if (hasAny && !isSilent) {
-			this.tryRelease();
-		}
+		this.tryRelease();
 	},
 
 
-	// Event Def/Instance/Range GETTING
-	// -----------------------------------------------------------------------------------------------------------------
+	removeEventDef: function(eventDef) {
+		delete this.eventDefsByInternalId[eventDef.internalId];
 
+		removeExact(this.eventDefsById[eventDef.id] || [], eventDef);
 
-	getEventDefsById: function(eventDefId) {
-		return this.eventDefsById[eventDefId] || [];
+		this.removeEventInstancesForDef(eventDef);
 	},
 
 
-	iterEventDefs: function(func) {
-		var eventDefsById = this.eventDefId;
-		var id;
-		var eventDefs;
-		var i;
-
-		for (id in eventDefsById) {
-			eventDefs = eventDefsById[id];
-
-			for (i = 0; i < eventDefs.length; i++) {
-				func(eventDefs[i]);
-			}
-		}
-	},
+	// Event Instances
+	// -----------------------------------------------------------------------------------------------------------------
 
 
 	getEventInstances: function() { // TODO: consider iterator
@@ -252,6 +213,29 @@ var EventPeriod = Class.extend(EmitterMixin, {
 	},
 
 
+	addEventInstance: function(eventInstance, eventDefId) {
+		var eventInstancesById = this.eventInstancesById;
+
+		(eventInstancesById[eventDefId] || (eventInstancesById[eventDefId] = []))
+			.push(eventInstance);
+
+		this.addEventRange(eventInstance.buildEventRange(), eventDefId);
+	},
+
+
+	removeEventInstancesForDef: function(eventDef) {
+		removeMatching(this.eventInstancesById[eventDef.id] || [], function(currentEventInstance) {
+			return currentEventInstance.def === eventDef;
+		});
+
+		this.removeEventRangesForDef(eventDef);
+	},
+
+
+	// Event Ranges
+	// -----------------------------------------------------------------------------------------------------------------
+
+
 	getEventRanges: function() { // TODO: consider iterator
 		var eventRangesById = this.eventRangesById;
 		var matchingRanges = [];
@@ -289,6 +273,27 @@ var EventPeriod = Class.extend(EmitterMixin, {
 	},
 
 
+	addEventRange: function(eventRange, eventDefId) {
+		var eventRangesById = this.eventRangesById;
+
+		(eventRangesById[eventDefId] || (eventRangesById[eventDefId] = []))
+			.push(eventRange);
+
+		this.tryRelease();
+	},
+
+
+	removeEventRangesForDef: function(eventDef) {
+		var removeCnt = removeMatching(this.eventRangesById[eventDef.id] || [], function(currentEventRange) {
+			return currentEventRange.eventInstance.def === eventDef;
+		});
+
+		if (removeCnt) {
+			this.tryRelease();
+		}
+	},
+
+
 	// Releasing and Freezing
 	// -----------------------------------------------------------------------------------------------------------------
 

+ 1 - 6
src/models/event-source/ArrayEventSource.js

@@ -30,15 +30,10 @@ var ArrayEventSource = EventSource.extend({
 	},
 
 
-	iterEventDefs: function(func) {
-		this.eventDefs.forEach(func);
-	},
-
-
 	/*
 	eventDefId already normalized to a string
 	*/
-	removeEventsById: function(eventDefId) {
+	removeEventDefsById: function(eventDefId) {
 		return removeMatching(this.eventDefs, function(eventDef) {
 			return eventDef.id === eventDefId;
 		});

+ 1 - 1
src/models/event-source/EventSource.js

@@ -31,7 +31,7 @@ var EventSource = Class.extend({
 	},
 
 
-	removeEventsById: function(eventDefId) {
+	removeEventDefsById: function(eventDefId) {
 		// optional for subclasses to implement
 	},
 

+ 2 - 4
src/models/event/EventDef.js

@@ -150,12 +150,10 @@ EventDef.parse = function(rawProps, source) {
 	}
 
 	if (rawProps.id != null) {
-		def.id = EventDef.normalizeId(
-			(def.rawId = rawProps.id)
-		);
+		def.id = EventDef.normalizeId((def.rawId = rawProps.id));
 	}
 	else {
-		def.id = def.rawId = EventDef.generateId();
+		def.id = EventDef.generateId();
 	}
 
 	def.internalId = String(EventDef.uuid++);

+ 16 - 6
src/models/event/EventInstance.js

@@ -29,18 +29,28 @@ var EventInstance = Class.extend({
 	toLegacy: function() {
 		var def = this.def;
 		var dateProfile = this.dateProfile;
-
-		return $.extend({}, def.miscProps, {
+		var obj = {
 			_id: def.internalId,
-			id: def.rawId,
-			title: def.title,
-			rendering: def.rendering,
 			start: dateProfile.start.clone(),
 			end: dateProfile.end ? dateProfile.end.clone() : null,
 			allDay: dateProfile.isAllDay(),
 			source: def.source,
 			className: def.className // should clone?
-		});
+		};
+
+		if (def.rawId != null) {
+			obj.id = def.rawId;
+		}
+
+		if (def.title != null) {
+			obj.title = def.title;
+		}
+
+		if (def.rendering != null) {
+			obj.rendering = def.rendering;
+		}
+
+		return obj;
 	}
 
 });

+ 5 - 25
src/models/event/EventInstanceGroup.js

@@ -1,26 +1,6 @@
 
-/*
-A group of related EventInstances. Assumed to all have the same ID.
-*/
-var EventInstanceGroup = Class.extend({
-
-	eventInstances: null, // EventInstance[]
-
-
-	constructor: function(eventInstances) {
-		this.eventInstances = eventInstances;
-	},
-
-
-	buildRanges: function() {
-		return this.eventInstances.map(function(instance) {
-			return instance.buildEventRange();
-		});
-	},
-
-
-	buildRangeGroup: function() {
-		return new EventRangeGroup(this.buildRanges());
-	}
-
-});
+function eventInstancesToEventRanges(eventInstances) {
+	return eventInstances.map(function(instance) {
+		return instance.buildEventRange();
+	});
+}

+ 2 - 2
src/util.js

@@ -932,7 +932,7 @@ function pluckProps(obj, propNames) {
 
 function removeMatching(array, testFunc) {
 	var removeCnt = 0;
-	var i;
+	var i = 0;
 
 	while (i < array.length) {
 		if (testFunc(array[i])) { // truthy value means *remove*
@@ -949,7 +949,7 @@ function removeMatching(array, testFunc) {
 
 function removeExact(array, exactVal) {
 	var removeCnt = 0;
-	var i;
+	var i = 0;
 
 	while (i < array.length) {
 		if (array[i] === exactVal) {