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

date range generation now uses UnzonedRanges, but with hacks

Adam Shaw 8 лет назад
Родитель
Сommit
d1ea235f41
7 измененных файлов с 121 добавлено и 161 удалено
  1. 2 2
      src/Calendar.js
  2. 10 6
      src/basic/BasicView.js
  3. 6 4
      src/basic/MonthView.js
  4. 73 78
      src/common/View.date-range.js
  5. 5 6
      src/common/View.js
  6. 25 1
      src/models/UnzonedRange.js
  7. 0 64
      src/util.js

+ 2 - 2
src/Calendar.js

@@ -247,7 +247,7 @@ var Calendar = FC.Calendar = Class.extend(EmitterMixin, {
 
 
 	// will return `null` if invalid range
-	parseRange: function(rangeInput) {
+	parseUnzonedRange: function(rangeInput) {
 		var start = null;
 		var end = null;
 
@@ -267,7 +267,7 @@ var Calendar = FC.Calendar = Class.extend(EmitterMixin, {
 			return null;
 		}
 
-		return { start: start, end: end };
+		return new UnzonedRange(start, end);
 	},
 
 

+ 10 - 6
src/basic/BasicView.js

@@ -43,20 +43,24 @@ var BasicView = FC.BasicView = View.extend({
 
 
 	// Computes the date range that will be rendered.
-	buildRenderRange: function(currentRange, currentRangeUnit) {
-		var renderRange = View.prototype.buildRenderRange.apply(this, arguments);
+	buildRenderRange: function(currentUnzonedRange, currentRangeUnit) {
+		var renderUnzonedRange = View.prototype.buildRenderRange.apply(this, arguments); // an UnzonedRange
+		var start = renderUnzonedRange.getStart();
+		var end = renderUnzonedRange.getEnd();
 
 		// year and month views should be aligned with weeks. this is already done for week
 		if (/^(year|month)$/.test(currentRangeUnit)) {
-			renderRange.start.startOf('week');
+			start.startOf('week');
 
 			// make end-of-week if not already
-			if (renderRange.end.weekday()) {
-				renderRange.end.add(1, 'week').startOf('week'); // exclusively move backwards
+			if (end.weekday()) {
+				end.add(1, 'week').startOf('week'); // exclusively move backwards
 			}
 		}
 
-		return this.trimHiddenDays(renderRange);
+		renderUnzonedRange = new UnzonedRange(start, end);
+
+		return this.trimHiddenDays(renderUnzonedRange);
 	},
 
 

+ 6 - 4
src/basic/MonthView.js

@@ -7,18 +7,20 @@ var MonthView = FC.MonthView = BasicView.extend({
 
 	// Computes the date range that will be rendered.
 	buildRenderRange: function() {
-		var renderRange = BasicView.prototype.buildRenderRange.apply(this, arguments);
+		var renderUnzonedRange = BasicView.prototype.buildRenderRange.apply(this, arguments);
+		var start = renderUnzonedRange.getStart();
+		var end = renderUnzonedRange.getEnd();
 		var rowCnt;
 
 		// ensure 6 weeks
 		if (this.isFixedWeeks()) {
 			rowCnt = Math.ceil( // could be partial weeks due to hiddenDays
-				renderRange.end.diff(renderRange.start, 'weeks', true) // dontRound=true
+				end.diff(start, 'weeks', true) // dontRound=true
 			);
-			renderRange.end.add(6 - rowCnt, 'weeks');
+			end.add(6 - rowCnt, 'weeks');
 		}
 
-		return renderRange;
+		return new UnzonedRange(start, end);
 	},
 
 

+ 73 - 78
src/common/View.date-range.js

@@ -38,21 +38,25 @@ View.mixin({
 
 
 	setDateProfileForRendering: function(dateProfile) {
-		this.currentUnzonedRange = new UnzonedRange(dateProfile.currentRange.start, dateProfile.currentRange.end);
+		this.currentUnzonedRange = dateProfile.currentUnzonedRange;
 		this.currentRangeUnit = dateProfile.currentRangeUnit;
 		this.isRangeAllDay = dateProfile.isRangeAllDay;
-		this.renderUnzonedRange = new UnzonedRange(dateProfile.renderRange.start, dateProfile.renderRange.end);
-		this.activeUnzonedRange = new UnzonedRange(dateProfile.activeRange.start, dateProfile.activeRange.end);
-		this.validUnzonedRange = new UnzonedRange(dateProfile.validRange.start, dateProfile.validRange.end);
+		this.renderUnzonedRange = dateProfile.renderUnzonedRange;
+		this.activeUnzonedRange = dateProfile.activeUnzonedRange;
+		this.validUnzonedRange = dateProfile.validUnzonedRange;
 		this.dateIncrement = dateProfile.dateIncrement;
 		this.minTime = dateProfile.minTime;
 		this.maxTime = dateProfile.maxTime;
 
-		// DEPRECATED, but we need to keep it updated
-		this.start = dateProfile.activeRange.start;
-		this.end = dateProfile.activeRange.end;
-		this.intervalStart = dateProfile.currentRange.start;
-		this.intervalEnd = dateProfile.currentRange.end;
+		// DEPRECATED, but we need to keep it updated...
+
+		var zonedActiveRange = dateProfile.activeUnzonedRange.getZonedRange(this.calendar, this.isRangeAllDay);
+		this.start = zonedActiveRange.start;
+		this.end = zonedActiveRange.end;
+
+		var zonedCurrentRange = dateProfile.currentUnzonedRange.getZonedRange(this.calendar, this.isRangeAllDay);
+		this.intervalStart = zonedCurrentRange.start;
+		this.intervalEnd = zonedCurrentRange.end;
 	},
 
 
@@ -76,44 +80,44 @@ View.mixin({
 	// Optional direction param indicates whether the date is being incremented/decremented
 	// from its previous value. decremented = -1, incremented = 1 (default).
 	buildDateProfile: function(date, direction, forceToValid) {
-		var validRange = this.buildValidRange();
+		var validUnzonedRange = this.buildValidRange();
 		var minTime = null;
 		var maxTime = null;
 		var currentInfo;
-		var renderRange;
-		var activeRange;
+		var renderUnzonedRange;
+		var activeUnzonedRange;
 		var isValid;
 
 		if (forceToValid) {
-			date = constrainDate(date, validRange);
+			date = validUnzonedRange.constrainDate(date);
 		}
 
 		currentInfo = this.buildCurrentRangeInfo(date, direction);
-		renderRange = this.buildRenderRange(currentInfo.range, currentInfo.unit);
-		activeRange = cloneRange(renderRange);
+		renderUnzonedRange = this.buildRenderRange(currentInfo.unzonedRange, currentInfo.unit);
+		activeUnzonedRange = renderUnzonedRange.clone();
 
 		if (!this.opt('showNonCurrentDates')) {
-			activeRange = constrainRange(activeRange, currentInfo.range);
+			activeUnzonedRange = activeUnzonedRange.constrainTo(currentInfo.unzonedRange);
 		}
 
 		minTime = moment.duration(this.opt('minTime'));
 		maxTime = moment.duration(this.opt('maxTime'));
-		this.adjustActiveRange(activeRange, minTime, maxTime);
+		activeUnzonedRange = this.adjustActiveRange(activeUnzonedRange, minTime, maxTime);
 
-		activeRange = constrainRange(activeRange, validRange);
-		date = constrainDate(date, activeRange);
+		activeUnzonedRange = activeUnzonedRange.constrainTo(validUnzonedRange);
+		date = activeUnzonedRange.constrainDate(date);
 
 		// it's invalid if the originally requested date is not contained,
 		// or if the range is completely outside of the valid range.
-		isValid = doRangesIntersect(currentInfo.range, validRange);
+		isValid = currentInfo.unzonedRange.intersectsWith(validUnzonedRange);
 
 		return {
-			validRange: validRange,
-			currentRange: currentInfo.range,
+			validUnzonedRange: validUnzonedRange,
+			currentUnzonedRange: currentInfo.unzonedRange,
 			currentRangeUnit: currentInfo.unit,
 			isRangeAllDay: /^(year|month|week|day)$/.test(currentInfo.unit),
-			activeRange: activeRange,
-			renderRange: renderRange,
+			activeUnzonedRange: activeUnzonedRange,
+			renderUnzonedRange: renderUnzonedRange,
 			minTime: minTime,
 			maxTime: maxTime,
 			isValid: isValid,
@@ -127,7 +131,8 @@ View.mixin({
 	// Builds an object with optional start/end properties.
 	// Indicates the minimum/maximum dates to display.
 	buildValidRange: function() {
-		return this.getRangeOption('validRange', this.calendar.getNow()) || {};
+		return this.getUnzonedRangeOption('validRange', this.calendar.getNow()) ||
+			new UnzonedRange(); // completely open-ended
 	},
 
 
@@ -135,33 +140,32 @@ View.mixin({
 	// highlighted as being the current month for example.
 	// See buildDateProfile for a description of `direction`.
 	// Guaranteed to have `range` and `unit` properties. `duration` is optional.
+	// TODO: accept a MS-time instead of a moment `date`?
 	buildCurrentRangeInfo: function(date, direction) {
 		var duration = null;
 		var unit = null;
-		var range = null;
+		var unzonedRange = null;
 		var dayCount;
 
 		if (this.viewSpec.duration) {
 			duration = this.viewSpec.duration;
 			unit = this.viewSpec.durationUnit;
-			range = this.buildRangeFromDuration(date, direction, duration, unit);
+			unzonedRange = this.buildRangeFromDuration(date, direction, duration, unit);
 		}
 		else if ((dayCount = this.opt('dayCount'))) {
 			unit = 'day';
-			range = this.buildRangeFromDayCount(date, direction, dayCount);
+			unzonedRange = this.buildRangeFromDayCount(date, direction, dayCount);
 		}
-		else if ((range = this.buildCustomVisibleRange(date))) {
-			unit = computeGreatestUnit(range.start, range.end);
+		else if ((unzonedRange = this.buildCustomVisibleRange(date))) {
+			unit = computeGreatestUnit(unzonedRange.start, unzonedRange.end);
 		}
 		else {
 			duration = this.getFallbackDuration();
 			unit = computeGreatestUnit(duration);
-			range = this.buildRangeFromDuration(date, direction, duration, unit);
+			unzonedRange = this.buildRangeFromDuration(date, direction, duration, unit);
 		}
 
-		this.normalizeCurrentRange(range, unit); // modifies in-place
-
-		return { duration: duration, unit: unit, range: range };
+		return { duration: duration, unit: unit, unzonedRange: unzonedRange };
 	},
 
 
@@ -170,56 +174,42 @@ View.mixin({
 	},
 
 
-	// If the range has day units or larger, remove times. Otherwise, ensure times.
-	normalizeCurrentRange: function(range, unit) {
-
-		if (/^(year|month|week|day)$/.test(unit)) { // whole-days?
-			range.start.stripTime();
-			range.end.stripTime();
-		}
-		else { // needs to have a time?
-			if (!range.start.hasTime()) {
-				range.start.time(0); // give 00:00 time
-			}
-			if (!range.end.hasTime()) {
-				range.end.time(0); // give 00:00 time
-			}
-		}
-	},
-
-
-	// Mutates the given activeRange to have time values (un-ambiguate)
-	// if the minTime or maxTime causes the range to expand.
-	// TODO: eventually activeRange should *always* have times.
-	adjustActiveRange: function(range, minTime, maxTime) {
+	// Returns a new activeUnzonedRange to have time values (un-ambiguate)
+	// minTime or maxTime causes the range to expand.
+	adjustActiveRange: function(unzonedRange, minTime, maxTime) {
 		var hasSpecialTimes = false;
+		var start = unzonedRange.getStart();
+		var end = unzonedRange.getEnd();
 
 		if (this.usesMinMaxTime) {
 
 			if (minTime < 0) {
-				range.start.time(0).add(minTime);
+				start.time(0).add(minTime);
 				hasSpecialTimes = true;
 			}
 
 			if (maxTime > 24 * 60 * 60 * 1000) { // beyond 24 hours?
-				range.end.time(maxTime - (24 * 60 * 60 * 1000));
+				end.time(maxTime - (24 * 60 * 60 * 1000));
 				hasSpecialTimes = true;
 			}
 
 			if (hasSpecialTimes) {
-				if (!range.start.hasTime()) {
-					range.start.time(0);
+				if (!start.hasTime()) {
+					start.time(0);
 				}
-				if (!range.end.hasTime()) {
-					range.end.time(0);
+				if (!end.hasTime()) {
+					end.time(0);
 				}
 			}
 		}
+
+		return new UnzonedRange(start, end);
 	},
 
 
 	// Builds the "current" range when it is specified as an explicit duration.
 	// `unit` is the already-computed computeGreatestUnit value of duration.
+	// TODO: accept a MS-time instead of a moment `date`?
 	buildRangeFromDuration: function(date, direction, duration, unit) {
 		var alignment = this.opt('dateAlignment');
 		var start = date.clone();
@@ -258,11 +248,12 @@ View.mixin({
 		start.startOf(alignment);
 		end = start.clone().add(duration);
 
-		return { start: start, end: end };
+		return new UnzonedRange(start, end);
 	},
 
 
 	// Builds the "current" range when a dayCount is specified.
+	// TODO: accept a MS-time instead of a moment `date`?
 	buildRangeFromDayCount: function(date, direction, dayCount) {
 		var customAlignment = this.opt('dateAlignment');
 		var runningCount = 0;
@@ -284,31 +275,32 @@ View.mixin({
 			}
 		} while (runningCount < dayCount);
 
-		return { start: start, end: end };
+		return new UnzonedRange(start, end);
 	},
 
 
 	// Builds a normalized range object for the "visible" range,
-	// which is a way to define the currentUnzonedRange and activeRange at the same time.
+	// which is a way to define the currentUnzonedRange and activeUnzonedRange at the same time.
+	// TODO: accept a MS-time instead of a moment `date`?
 	buildCustomVisibleRange: function(date) {
-		var visibleRange = this.getRangeOption(
+		var visibleUnzonedRange = this.getUnzonedRangeOption(
 			'visibleRange',
 			this.calendar.moment(date) // correct zone. also generates new obj that avoids mutations
 		);
 
-		if (visibleRange && (!visibleRange.start || !visibleRange.end)) {
+		if (visibleUnzonedRange && (visibleUnzonedRange.startMs === null || visibleUnzonedRange.endMs === null)) {
 			return null;
 		}
 
-		return visibleRange;
+		return visibleUnzonedRange;
 	},
 
 
 	// Computes the range that will represent the element/cells for *rendering*,
 	// but which may have voided days/times.
-	buildRenderRange: function(currentRange, currentRangeUnit) {
-		// cut off days in the currentRange that are hidden
-		return this.trimHiddenDays(currentRange);
+	buildRenderRange: function(currentUnzonedRange, currentRangeUnit) {
+		// cut off days in the currentUnzonedRange that are hidden
+		return this.trimHiddenDays(currentUnzonedRange);
 	},
 
 
@@ -334,11 +326,14 @@ View.mixin({
 
 
 	// Remove days from the beginning and end of the range that are computed as hidden.
-	trimHiddenDays: function(inputRange) {
-		return {
-			start: this.skipHiddenDays(inputRange.start),
-			end: this.skipHiddenDays(inputRange.end, -1, true) // exclusively move backwards
-		};
+	trimHiddenDays: function(inputUnzonedRange) {
+		var start = inputUnzonedRange.getStart();
+		var end = inputUnzonedRange.getEnd();
+
+		start = this.skipHiddenDays(start);
+		end = this.skipHiddenDays(end, -1, true);
+
+		return new UnzonedRange(start, end);
 	},
 
 
@@ -364,7 +359,7 @@ View.mixin({
 	// Arguments after name will be forwarded to a hypothetical function value
 	// WARNING: passed-in arguments will be given to generator functions as-is and can cause side-effects.
 	// Always clone your objects if you fear mutation.
-	getRangeOption: function(name) {
+	getUnzonedRangeOption: function(name) {
 		var val = this.opt(name);
 
 		if (typeof val === 'function') {
@@ -375,7 +370,7 @@ View.mixin({
 		}
 
 		if (val) {
-			return this.calendar.parseRange(val);
+			return this.calendar.parseUnzonedRange(val);
 		}
 	},
 

+ 5 - 6
src/common/View.js

@@ -157,7 +157,7 @@ var View = FC.View = ChronoComponent.extend({
 
 		return this.formatRange(
 			{
-				// in case currentRange has a time, make sure timezone is correct.
+				// in case currentUnzonedRange has a time, make sure timezone is correct.
 				// TODO: make a utility for zoning an unzoned range.
 				start: this.calendar.applyTimezone(range.start),
 				end: this.calendar.applyTimezone(range.end)
@@ -216,7 +216,7 @@ var View = FC.View = ChronoComponent.extend({
 
 		if (
 			!currentDateProfile ||
-			!isRangesEqual(currentDateProfile.activeRange, newDateProfile.activeRange)
+			!currentDateProfile.activeUnzonedRange.equals(newDateProfile.activeUnzonedRange)
 		) {
 			this.set('dateProfile', newDateProfile);
 		}
@@ -257,10 +257,9 @@ var View = FC.View = ChronoComponent.extend({
 
 
 	fetchInitialEvents: function(dateProfile) {
-		return this.calendar.requestEvents(
-			dateProfile.activeRange.start,
-			dateProfile.activeRange.end
-		);
+		var zonedRange = dateProfile.activeUnzonedRange.getZonedRange(this.calendar, this.isRangeAllDay);
+
+		return this.calendar.requestEvents(zonedRange.start, zonedRange.end);
 	},
 
 

+ 25 - 1
src/models/UnzonedRange.js

@@ -81,11 +81,35 @@ var UnzonedRange = FC.UnzonedRange = Class.extend({
 	},
 
 
-	equals: function(otherRange) { // needed?
+	equals: function(otherRange) {
 		return this.startMs === otherRange.startMs && this.endMs === otherRange.endMs;
 	},
 
 
+	clone: function() {
+		return new UnzonedRange(this.startMs, this.endMs);
+	},
+
+
+	// If the given date is not within the given range, move it inside.
+	// (If it's past the end, make it one millisecond before the end).
+	// expects a UTC or ambig-time/timezone moment, or MS-time.
+	// returns a UTC moment.
+	constrainDate: function(date) {
+		var ms = date.valueOf();
+
+		if (this.startMs !== null && ms < this.startMs) {
+			ms = this.startMs;
+		}
+
+		if (this.endMs !== null && ms >= this.endMs) {
+			ms = this.endMs - 1;
+		}
+
+		return FC.moment.utc(ms);
+	},
+
+
 	// hopefully we'll remove these...
 
 	getStart: function() {

+ 0 - 64
src/util.js

@@ -684,76 +684,12 @@ function multiplyDuration(dur, n) {
 }
 
 
-function cloneRange(range) {
-	return {
-		start: range.start.clone(),
-		end: range.end.clone()
-	};
-}
-
-
-// Trims the beginning and end of inner range to be completely within outerRange.
-// Returns a new range object.
-function constrainRange(innerRange, outerRange) {
-	innerRange = cloneRange(innerRange);
-
-	if (outerRange.start) {
-		// needs to be inclusively before outerRange's end
-		innerRange.start = constrainDate(innerRange.start, outerRange);
-	}
-
-	if (outerRange.end) {
-		innerRange.end = minMoment(innerRange.end, outerRange.end);
-	}
-
-	return innerRange;
-}
-
-
-// If the given date is not within the given range, move it inside.
-// (If it's past the end, make it one millisecond before the end).
-// Always returns a new moment.
-function constrainDate(date, range) {
-	date = date.clone();
-
-	if (range.start) {
-		date = maxMoment(date, range.start);
-	}
-
-	if (range.end && date >= range.end) {
-		date = range.end.clone().subtract(1);
-	}
-
-	return date;
-}
-
-
-// TODO: deal with repeat code in intersectRanges
-// constraintRange can have unspecified start/end, an open-ended range.
-function doRangesIntersect(subjectRange, constraintRange) {
-	return (!constraintRange.start || subjectRange.end >= constraintRange.start) &&
-		(!constraintRange.end || subjectRange.start < constraintRange.end);
-}
-
-
 function isRangesEqual(range0, range1) {
 	return ((range0.start && range1.start && range0.start.isSame(range1.start)) || (!range0.start && !range1.start)) &&
 		((range0.end && range1.end && range0.end.isSame(range1.end)) || (!range0.end && !range1.end));
 }
 
 
-// Returns the moment that's earlier in time. Always a copy.
-function minMoment(mom1, mom2) {
-	return (mom1.isBefore(mom2) ? mom1 : mom2).clone();
-}
-
-
-// Returns the moment that's later in time. Always a copy.
-function maxMoment(mom1, mom2) {
-	return (mom1.isAfter(mom2) ? mom1 : mom2).clone();
-}
-
-
 // Returns a boolean about whether the given duration has any time parts (hours/minutes/seconds/ms)
 function durationHasTime(dur) {
 	return Boolean(dur.hours() || dur.minutes() || dur.seconds() || dur.milliseconds());