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

break apart base Grid methods more

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

+ 3 - 2
src.json

@@ -21,16 +21,17 @@
     "common/GlobalEmitter.js",
     "common/MouseFollower.js",
     "common/ChronoComponent.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.business-hours.js",
-    "common/Grid.event-rendering.js",
     "common/Grid.event-interaction.js",
     "common/Grid.event-dragging.js",
     "common/Grid.event-resizing.js",
     "common/Grid.external-dropping.js",
-    "common/Grid.fill.js",
     "common/DayTableMixin.js",
     "common/DayGrid.js",
     "common/DayGrid.events.js",

+ 0 - 58
src/common/ChronoComponent.js

@@ -290,7 +290,6 @@ var ChronoComponent = Model.extend({
 	},
 
 
-
 	// Event Drag-n-Drop
 	// ---------------------------------------------------------------------------------------------------------------
 
@@ -362,63 +361,6 @@ var ChronoComponent = Model.extend({
 	// ---------------------------------------------------------------------------------------------------------------
 
 
-	// Renders foreground event segments onto the grid. May return a subset of segs that were rendered.
-	renderFgSegs: function(segs) {
-		// subclasses must implement
-	},
-
-
-	// Unrenders all currently rendered foreground segments
-	unrenderFgSegs: function() {
-		// subclasses must implement
-	},
-
-
-	// Renders and assigns an `el` property for each foreground event segment.
-	// Only returns segments that successfully rendered.
-	// A utility that subclasses may use.
-	renderFgSegEls: function(segs, disableResizing) {
-		var _this = this;
-		var hasEventRenderHandlers = this.hasPublicHandlers('eventRender');
-		var html = '';
-		var renderedSegs = [];
-		var i;
-
-		if (segs.length) { // don't build an empty html string
-
-			// build a large concatenation of event segment HTML
-			for (i = 0; i < segs.length; i++) {
-				html += this.fgSegHtml(segs[i], disableResizing);
-			}
-
-			// Grab individual elements from the combined HTML string. Use each as the default rendering.
-			// Then, compute the 'el' for each segment. An el might be null if the eventRender callback returned false.
-			$(html).each(function(i, node) {
-				var seg = segs[i];
-				var el = $(node);
-
-				if (hasEventRenderHandlers) { // optimization
-					el = _this.filterEventRenderEl(seg.footprint, el);
-				}
-
-				if (el) {
-					el.data('fc-seg', seg); // used by handlers
-					seg.el = el;
-					renderedSegs.push(seg);
-				}
-			});
-		}
-
-		return renderedSegs;
-	},
-
-
-	// Generates the HTML for the default rendering of a foreground event segment. Used by renderFgSegEls()
-	fgSegHtml: function(seg, disableResizing) {
-		// subclasses should implement
-	},
-
-
 	// Given an event and the default element used for rendering, returns the element that should actually be used.
 	// Basically runs events and elements through the eventRender hook.
 	filterEventRenderEl: function(eventFootprint, el) {

+ 13 - 162
src/common/Grid.event-rendering.js → src/common/EventRenderingUtilsMixin.js

@@ -1,5 +1,13 @@
 
-Grid.mixin({
+/*
+Caller must call:
+- initEventRenderingUtils
+
+This mixin can depend on ChronoComponent:
+- opt
+- _getView
+*/
+var EventRenderingUtilsMixin = {
 
 	// derived from options
 	// TODO: move initialization from Grid.js
@@ -9,7 +17,7 @@ Grid.mixin({
 
 
 	// Updates values that rely on options and also relate to range
-	initEventRenderingOptions: function() {
+	initEventRenderingUtils: function() {
 		var displayEventTime;
 		var displayEventEnd;
 
@@ -69,6 +77,7 @@ Grid.mixin({
 
 
 	_getEventTimeText: function(start, end, isAllDay, formatStr, displayEnd) {
+		var view = this._getView();
 
 		if (formatStr == null) {
 			formatStr = this.eventTimeFormat;
@@ -80,7 +89,7 @@ Grid.mixin({
 
 		if (this.displayEventTime && !isAllDay) {
 			if (displayEnd && end) {
-				return this.view.formatRange(
+				return view.formatRange(
 					{ start: start, end: end },
 					false, // allDay
 					formatStr
@@ -172,164 +181,6 @@ Grid.mixin({
 
 		return source.textColor ||
 			this.opt('eventTextColor');
-	},
-
-
-
-
-
-
-
-
-
-
-
-
-
-	segs: null, // the *event* segments currently rendered in the grid. TODO: rename to `eventSegs`
-
-
-	renderEventsPayload: function(eventsPayload) {
-		var id, eventInstanceGroup;
-		var eventRenderRanges;
-		var eventFootprints;
-		var eventSegs;
-		var bgSegs = [];
-		var fgSegs = [];
-
-		for (id in eventsPayload) {
-			eventInstanceGroup = eventsPayload[id];
-
-			eventRenderRanges = eventInstanceGroup.sliceRenderRanges(this.view.activeUnzonedRange);
-			eventFootprints = this.eventRangesToEventFootprints(eventRenderRanges);
-			eventSegs = this.eventFootprintsToSegs(eventFootprints);
-
-			if (eventInstanceGroup.getEventDef().hasBgRendering()) {
-				bgSegs.push.apply(bgSegs, // append
-					eventSegs
-				);
-			}
-			else {
-				fgSegs.push.apply(fgSegs, // append
-					eventSegs
-				);
-			}
-		}
-
-		this.segs = [].concat( // record all segs
-			this.renderBgSegs(bgSegs) || bgSegs,
-			this.renderFgSegs(fgSegs) || fgSegs
-		);
-	},
-
-
-	// Unrenders all events currently rendered on the grid
-	unrenderEvents: function() {
-		this.handleSegMouseout(); // trigger an eventMouseout if user's mouse is over an event
-		this.clearDragListeners();
-
-		this.unrenderFgSegs();
-		this.unrenderBgSegs();
-
-		this.segs = null;
-	},
-
-
-	// Retrieves all rendered segment objects currently rendered on the grid
-	getEventSegs: function() {
-		return this.segs || [];
-	},
-
-
-	// Background Segment Rendering
-	// ---------------------------------------------------------------------------------------------------------------
-	// TODO: move this to ChronoComponent, but without fill
-
-
-	// Renders the given background event segments onto the grid.
-	// Returns a subset of the segs that were actually rendered.
-	renderBgSegs: function(segs) {
-		return this.renderFill('bgEvent', segs);
-	},
-
-
-	// Unrenders all the currently rendered background event segments
-	unrenderBgSegs: function() {
-		this.unrenderFill('bgEvent');
-	},
-
-
-	// Renders a background event element, given the default rendering. Called by the fill system.
-	bgEventSegEl: function(seg, el) {
-		return this.filterEventRenderEl(seg.footprint, el);
-	},
-
-
-	// Generates an array of classNames to be used for the default rendering of a background event.
-	// NEEDED BY FILL SYSTEM, fillSegHtml :(
-	bgEventSegClasses: function(seg) {
-		return this.getBgEventFootprintClasses(seg.footprint);
-	},
-
-
-	// Generates a semicolon-separated CSS string to be used for the default rendering of a background event.
-	// NEEDED BY FILL SYSTEM,  fillSegHtml :(
-	bgEventSegCss: function(seg) {
-		return {
-			'background-color': this.getEventFootprintSkinCss(seg.footprint)['background-color']
-		};
-	},
-
-
-	/* Utils
-	------------------------------------------------------------------------------------------------------------------*/
-
-
-	// Generic utility for generating the HTML classNames for an event segment's element
-	getSegClasses: function(seg, isDraggable, isResizable) {
-		var view = this.view;
-		var classes = [
-			'fc-event',
-			seg.isStart ? 'fc-start' : 'fc-not-start',
-			seg.isEnd ? 'fc-end' : 'fc-not-end'
-		].concat(this.getEventFootprintClasses(seg.footprint));
-
-		if (isDraggable) {
-			classes.push('fc-draggable');
-		}
-		if (isResizable) {
-			classes.push('fc-resizable');
-		}
-
-		// event is currently selected? attach a className.
-		if (view.isEventDefSelected(seg.footprint.eventDef)) {
-			classes.push('fc-selected');
-		}
-
-		return classes;
-	},
-
-
-	sortEventSegs: function(segs) {
-		segs.sort(proxy(this, 'compareEventSegs'));
-	},
-
-
-	// A cmp function for determining which segments should take visual priority
-	compareEventSegs: function(seg1, seg2) {
-		var f1 = seg1.footprint.componentFootprint;
-		var r1 = f1.unzonedRange;
-		var f2 = seg2.footprint.componentFootprint;
-		var r2 = f2.unzonedRange;
-
-		return r1.startMs - r2.startMs || // earlier events go first
-			(r2.endMs - r2.startMs) - (r1.endMs - r1.startMs) || // tie? longer events go first
-			f2.isAllDay - f1.isAllDay || // tie? put all-day events first (booleans cast to 0/1)
-			compareByFieldSpecs(
-				seg1.footprint.eventDef,
-				seg2.footprint.eventDef,
-				this.view.eventOrderSpecs
-			);
 	}
 
-});
+};

+ 8 - 15
src/common/Grid.fill.js → src/common/FillSystemMixin.js

@@ -1,16 +1,15 @@
 
-Grid.mixin({
-
-	/* Fill System (highlight, background events, business hours)
-	--------------------------------------------------------------------------------------------------------------------
-	TODO: remove this system. like we did in TimeGrid
-	*/
-
+/*
+Caller must:
+- call initFillSystem
+- implement renderFill
+*/
+var FillSystemMixin = { // use for highlight, background events, business hours
 
 	elsByFill: null, // a hash of jQuery element sets used for rendering each fill. Keyed by fill name.
 
 
-	initFillInternals: function() {
+	initFillSystem: function() {
 		this.elsByFill = {};
 	},
 
@@ -96,12 +95,6 @@ Grid.mixin({
 			(classes.length ? ' class="' + classes.join(' ') + '"' : '') +
 			(css ? ' style="' + css + '"' : '') +
 			' />';
-	},
-
-
-	// Generates an array of classNames for rendering the highlight. Used by the fill system.
-	highlightSegClasses: function() {
-		return [ 'fc-highlight' ];
 	}
 
-});
+};

+ 17 - 76
src/common/Grid.js

@@ -10,7 +10,7 @@ Contains:
 - initializing event rendering-related options
 */
 
-var Grid = FC.Grid = ChronoComponent.extend({
+var Grid = FC.Grid = ChronoComponent.extend(SegChronoComponentMixin, {
 
 	// self-config, overridable by subclasses
 	hasDayInteractions: true, // can user click/select ranges of time?
@@ -28,11 +28,11 @@ var Grid = FC.Grid = ChronoComponent.extend({
 
 
 	constructor: function(view) {
-		this.view = view;
+		this.view = view; // do this first, for opt()
 
 		ChronoComponent.call(this);
 
-		this.initFillInternals();
+		this.initFillSystem(); // TODO: SegChronoComponentMixin should be responsible
 
 		this.dayClickListener = this.buildDayClickListener();
 		this.daySelectListener = this.buildDaySelectListener();
@@ -52,7 +52,10 @@ var Grid = FC.Grid = ChronoComponent.extend({
 	// Any date-related internal data should be generated.
 	setRange: function(unzonedRange) {
 		this.rangeUpdated();
-		this.initEventRenderingOptions();
+
+		// do after rangeUpdated because initEventRenderingUtils might depend on range-related values
+		// TODO: SegChronoComponentMixin should be responsible
+		this.initEventRenderingUtils();
 	},
 
 
@@ -61,6 +64,16 @@ var Grid = FC.Grid = ChronoComponent.extend({
 	},
 
 
+	/* Event Rendering
+	------------------------------------------------------------------------------------------------------------------*/
+
+
+	unrenderEvents: function() {
+		this.clearDragListeners(); // we wanted to add this action to event rendering teardown
+		SegChronoComponentMixin.unrenderEvents.apply(this, arguments);
+	},
+
+
 
 	/* Hit Area
 	------------------------------------------------------------------------------------------------------------------*/
@@ -240,22 +253,6 @@ var Grid = FC.Grid = ChronoComponent.extend({
 	},
 
 
-	/* Highlight
-	------------------------------------------------------------------------------------------------------------------*/
-
-
-	// Renders an emphasis on the given date range. Given a span (unzoned start/end and other misc data)
-	renderHighlight: function(componentFootprint) {
-		this.renderFill('highlight', this.componentFootprintToSegs(componentFootprint));
-	},
-
-
-	// Unrenders the emphasis on a date range
-	unrenderHighlight: function() {
-		this.unrenderFill('highlight');
-	},
-
-
 	/* Converting eventRange -> eventFootprint
 	------------------------------------------------------------------------------------------------------------------*/
 
@@ -290,62 +287,6 @@ var Grid = FC.Grid = ChronoComponent.extend({
 				eventRange.eventInstance // might not exist
 			)
 		];
-	},
-
-
-	/* Converting componentFootprint/eventFootprint -> segs
-	------------------------------------------------------------------------------------------------------------------*/
-
-
-	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... }
-	// constraintRange allow additional clipping. optional. eventually remove this.
-	eventFootprintToSegs: function(eventFootprint, constraintRange) {
-		var unzonedRange = eventFootprint.componentFootprint.unzonedRange;
-		var segs;
-		var i, seg;
-
-		if (constraintRange) {
-			unzonedRange = unzonedRange.intersect(constraintRange);
-		}
-
-		segs = this.componentFootprintToSegs(eventFootprint.componentFootprint);
-
-		for (i = 0; i < segs.length; i++) {
-			seg = segs[i];
-
-			if (!unzonedRange.isStart) {
-				seg.isStart = false;
-			}
-			if (!unzonedRange.isEnd) {
-				seg.isEnd = false;
-			}
-
-			seg.footprint = eventFootprint;
-			// TODO: rename to seg.eventFootprint
-		}
-
-		return segs;
-	},
-
-
-	componentFootprintToSegs: function(componentFootprint) {
-		// subclasses must implement
 	}
 
 });

+ 312 - 0
src/common/SegChronoComponentMixin.js

@@ -0,0 +1,312 @@
+
+/*
+Caller must:
+- call initSegChronoComponent
+- implement componentFootprintToSegs
+- implement renderFgSegs
+- implement unrenderFgSegs
+- implement renderFill (FillSystemMixin)
+
+This mixin can depend on ChronoComponent:
+- opt
+- _getView
+- filterEventRenderEl
+- eventRangesToEventFootprints
+- eventFootprintsToSegs
+*/
+var SegChronoComponentMixin = $.extend({}, EventRenderingUtilsMixin, FillSystemMixin, {
+
+	segs: null, // the *event* segments currently rendered in the grid. TODO: rename to `eventSegs`
+
+
+	//initSegChronoComponent: function() {
+	//	this.initEventRenderingUtils();
+	//	this.initFillSystem();
+	//},
+
+
+	renderEventsPayload: function(eventsPayload) {
+		var view = this._getView();
+		var id, eventInstanceGroup;
+		var eventRenderRanges;
+		var eventFootprints;
+		var eventSegs;
+		var bgSegs = [];
+		var fgSegs = [];
+
+		for (id in eventsPayload) {
+			eventInstanceGroup = eventsPayload[id];
+
+			eventRenderRanges = eventInstanceGroup.sliceRenderRanges(view.activeUnzonedRange);
+			eventFootprints = this.eventRangesToEventFootprints(eventRenderRanges);
+			eventSegs = this.eventFootprintsToSegs(eventFootprints);
+
+			if (eventInstanceGroup.getEventDef().hasBgRendering()) {
+				bgSegs.push.apply(bgSegs, // append
+					eventSegs
+				);
+			}
+			else {
+				fgSegs.push.apply(fgSegs, // append
+					eventSegs
+				);
+			}
+		}
+
+		this.segs = [].concat( // record all segs
+			this.renderBgSegs(bgSegs) || bgSegs,
+			this.renderFgSegs(fgSegs) || fgSegs
+		);
+	},
+
+
+	// Unrenders all events currently rendered on the grid
+	unrenderEvents: function() {
+		this.handleSegMouseout(); // trigger an eventMouseout if user's mouse is over an event
+
+		this.unrenderFgSegs();
+		this.unrenderBgSegs();
+
+		this.segs = null;
+	},
+
+
+	// Retrieves all rendered segment objects currently rendered on the grid
+	getEventSegs: function() {
+		return this.segs || [];
+	},
+
+
+	// Foreground Segment Rendering
+	// ---------------------------------------------------------------------------------------------------------------
+
+
+	// Renders foreground event segments onto the grid. May return a subset of segs that were rendered.
+	renderFgSegs: function(segs) {
+		// subclasses must implement
+	},
+
+
+	// Unrenders all currently rendered foreground segments
+	unrenderFgSegs: function() {
+		// subclasses must implement
+	},
+
+
+	// Renders and assigns an `el` property for each foreground event segment.
+	// Only returns segments that successfully rendered.
+	// A utility that subclasses may use.
+	renderFgSegEls: function(segs, disableResizing) {
+		var _this = this;
+		var hasEventRenderHandlers = this.hasPublicHandlers('eventRender');
+		var html = '';
+		var renderedSegs = [];
+		var i;
+
+		if (segs.length) { // don't build an empty html string
+
+			// build a large concatenation of event segment HTML
+			for (i = 0; i < segs.length; i++) {
+				html += this.fgSegHtml(segs[i], disableResizing);
+			}
+
+			// Grab individual elements from the combined HTML string. Use each as the default rendering.
+			// Then, compute the 'el' for each segment. An el might be null if the eventRender callback returned false.
+			$(html).each(function(i, node) {
+				var seg = segs[i];
+				var el = $(node);
+
+				if (hasEventRenderHandlers) { // optimization
+					el = _this.filterEventRenderEl(seg.footprint, el);
+				}
+
+				if (el) {
+					el.data('fc-seg', seg); // used by handlers
+					seg.el = el;
+					renderedSegs.push(seg);
+				}
+			});
+		}
+
+		return renderedSegs;
+	},
+
+
+	// Generates the HTML for the default rendering of a foreground event segment. Used by renderFgSegEls()
+	fgSegHtml: function(seg, disableResizing) {
+		// subclasses should implement
+	},
+
+
+	/* Background Segment Rendering
+	------------------------------------------------------------------------------------------------------------------*/
+
+
+	// Renders the given background event segments onto the grid.
+	// Returns a subset of the segs that were actually rendered.
+	renderBgSegs: function(segs) {
+		return this.renderFill('bgEvent', segs);
+	},
+
+
+	// Unrenders all the currently rendered background event segments
+	unrenderBgSegs: function() {
+		this.unrenderFill('bgEvent');
+	},
+
+
+	// Renders a background event element, given the default rendering. Called by the fill system.
+	// NEEDED BY FILL SYSTEM, renderFillSegEls :(
+	bgEventSegEl: function(seg, el) {
+		return this.filterEventRenderEl(seg.footprint, el);
+	},
+
+
+	// Generates an array of classNames to be used for the default rendering of a background event.
+	// NEEDED BY FILL SYSTEM, fillSegHtml :(
+	bgEventSegClasses: function(seg) {
+		return this.getBgEventFootprintClasses(seg.footprint);
+	},
+
+
+	// Generates a semicolon-separated CSS string to be used for the default rendering of a background event.
+	// NEEDED BY FILL SYSTEM,  fillSegHtml :(
+	bgEventSegCss: function(seg) {
+		return {
+			'background-color': this.getEventFootprintSkinCss(seg.footprint)['background-color']
+		};
+	},
+
+
+	/* Implement Highlight
+	------------------------------------------------------------------------------------------------------------------*/
+
+
+	// Renders an emphasis on the given date range. Given a span (unzoned start/end and other misc data)
+	renderHighlight: function(componentFootprint) {
+		this.renderFill('highlight', this.componentFootprintToSegs(componentFootprint));
+	},
+
+
+	// Unrenders the emphasis on a date range
+	unrenderHighlight: function() {
+		this.unrenderFill('highlight');
+	},
+
+
+	// Generates an array of classNames for rendering the highlight.
+	// USED BY THE FILL SYSTEM, fillSegHtml
+	highlightSegClasses: function() {
+		return [ 'fc-highlight' ];
+	},
+
+
+	/* Utils
+	------------------------------------------------------------------------------------------------------------------*/
+
+
+	// Generic utility for generating the HTML classNames for an event segment's element
+	getSegClasses: function(seg, isDraggable, isResizable) {
+		var view = this._getView();
+		var classes = [
+			'fc-event',
+			seg.isStart ? 'fc-start' : 'fc-not-start',
+			seg.isEnd ? 'fc-end' : 'fc-not-end'
+		].concat(this.getEventFootprintClasses(seg.footprint));
+
+		if (isDraggable) {
+			classes.push('fc-draggable');
+		}
+		if (isResizable) {
+			classes.push('fc-resizable');
+		}
+
+		// event is currently selected? attach a className.
+		if (view.isEventDefSelected(seg.footprint.eventDef)) {
+			classes.push('fc-selected');
+		}
+
+		return classes;
+	},
+
+
+	sortEventSegs: function(segs) {
+		segs.sort(proxy(this, 'compareEventSegs'));
+	},
+
+
+	// A cmp function for determining which segments should take visual priority
+	compareEventSegs: function(seg1, seg2) {
+		var view = this._getView(); // TODO: not optimal!
+		var f1 = seg1.footprint.componentFootprint;
+		var r1 = f1.unzonedRange;
+		var f2 = seg2.footprint.componentFootprint;
+		var r2 = f2.unzonedRange;
+
+		return r1.startMs - r2.startMs || // earlier events go first
+			(r2.endMs - r2.startMs) - (r1.endMs - r1.startMs) || // tie? longer events go first
+			f2.isAllDay - f1.isAllDay || // tie? put all-day events first (booleans cast to 0/1)
+			compareByFieldSpecs(
+				seg1.footprint.eventDef,
+				seg2.footprint.eventDef,
+				view.eventOrderSpecs
+			);
+	},
+
+
+	/* Converting componentFootprint/eventFootprint -> segs
+	------------------------------------------------------------------------------------------------------------------*/
+
+
+	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... }
+	// constraintRange allow additional clipping. optional. eventually remove this.
+	eventFootprintToSegs: function(eventFootprint, constraintRange) {
+		var unzonedRange = eventFootprint.componentFootprint.unzonedRange;
+		var segs;
+		var i, seg;
+
+		if (constraintRange) {
+			unzonedRange = unzonedRange.intersect(constraintRange);
+		}
+
+		segs = this.componentFootprintToSegs(eventFootprint.componentFootprint);
+
+		for (i = 0; i < segs.length; i++) {
+			seg = segs[i];
+
+			if (!unzonedRange.isStart) {
+				seg.isStart = false;
+			}
+			if (!unzonedRange.isEnd) {
+				seg.isEnd = false;
+			}
+
+			seg.footprint = eventFootprint;
+			// TODO: rename to seg.eventFootprint
+		}
+
+		return segs;
+	},
+
+
+	componentFootprintToSegs: function(componentFootprint) {
+		// subclasses must implement
+	}
+
+});