diff --git a/lib/timeline/Core.js b/lib/timeline/Core.js index 4c0886e0..7ea5a50f 100644 --- a/lib/timeline/Core.js +++ b/lib/timeline/Core.js @@ -457,23 +457,28 @@ Core.prototype.getVisibleItems = function() { * function is 'easeInOutQuad'. */ Core.prototype.fit = function(options) { - var range = this._getDataRange(); + var range = this.getDataRange(); // skip range set if there is no start and end date if (range.start === null && range.end === null) { return; } + // apply a margin of 1% left and right of the data + var interval = range.max - range.min; + var min = new Date(range.min.valueOf() - interval * 0.01); + var max = new Date(range.max.valueOf() + interval * 0.01); + var animation = (options && options.animation !== undefined) ? options.animation : true; - this.range.setRange(range.start, range.end, animation); + this.range.setRange(min, max, animation); }; /** - * Calculate the data range of the items and applies a 5% window around it. - * @returns {{start: Date | null, end: Date | null}} + * Calculate the data range of the items start and end dates + * @returns {{min: Date | null, max: Date | null}} * @protected */ -Core.prototype._getDataRange = function() { +Core.prototype.getDataRange = function() { // apply the data range as range var dataRange = this.getItemRange(); @@ -491,8 +496,8 @@ Core.prototype._getDataRange = function() { } return { - start: start, - end: end + start: null, + end: null } }; diff --git a/lib/timeline/Graph2d.js b/lib/timeline/Graph2d.js index 0ab6ab28..4cea8d8f 100644 --- a/lib/timeline/Graph2d.js +++ b/lib/timeline/Graph2d.js @@ -242,7 +242,7 @@ Graph2d.prototype.isGroupVisible = function(groupId) { * When no minimum is found, min==null * When no maximum is found, max==null */ -Graph2d.prototype.getItemRange = function() { +Graph2d.prototype.getDataRange = function() { var min = null; var max = null; diff --git a/lib/timeline/Timeline.js b/lib/timeline/Timeline.js index 288cde09..bc2f6232 100644 --- a/lib/timeline/Timeline.js +++ b/lib/timeline/Timeline.js @@ -218,8 +218,6 @@ Timeline.prototype.setItems = function(items) { this.setWindow(start, end, {animation: false}); } else { - // call fit twice as - this.fit({animation: false}); this.fit({animation: false}); } } @@ -345,12 +343,71 @@ Timeline.prototype.focus = function(id, options) { } }; +/** + * Set Timeline window such that it fits all items + * @param {Object} [options] Available options: + * `animation: boolean | {duration: number, easingFunction: string}` + * If true (default), the range is animated + * smoothly to the new window. An object can be + * provided to specify duration and easing function. + * Default duration is 500 ms, and default easing + * function is 'easeInOutQuad'. + */ +Timeline.prototype.fit = function (options) { + // then, make sure all items are drawn and have a width, and fit the timeline window exactly. + var min = null; + var max = null; + var minItem = null; + var maxItem = null; + + util.forEach(this.itemSet.items, function (item) { + item.show(); + item.repositionX(); + + if (item.left < min) { + min = item.left; + minItem = item; + } + + var right = item.left + item.width; + if (right > max) { + max = right; + maxItem = item; + } + }); + + if (minItem && maxItem) { + var start = util.convert(minItem.data.start, 'Date').valueOf(); + var end = maxItem.data.end != undefined + ? util.convert(maxItem.data.end, 'Date').valueOf() + : util.convert(maxItem.data.start, 'Date').valueOf(); + + var lhs = this._toScreen(start) - minItem.left - 10; + var rhs = maxItem.left + maxItem.width - this._toScreen(end) + 10; + + var interval = end - start; // ms + if (interval <= 0) {interval = 10;} + var delta = this.props.center.width - lhs - rhs; // px + + //console.log(start, end, lhs, rhs, delta) + + if (delta > 0) { + start -= lhs * interval / delta; // ms + end += rhs * interval / delta; // ms + + var animation = (options && options.animation !== undefined) ? options.animation : true; + this.range.setRange(start, end, animation); + } + } +}; + /** * Get the data range of the item set. * @returns {{min: Date, max: Date}} range A range with a start and end Date. * When no minimum is found, min==null * When no maximum is found, max==null */ +// TODO: remove this function Timeline.prototype.getItemRange = function() { var min = null; var max = null; @@ -378,11 +435,26 @@ Timeline.prototype.getItemRange = function() { var minItem = this.itemSet.items[minId]; var maxItem = this.itemSet.items[maxId]; if (minItem && minItem.width && maxItem && maxItem.width) { - var left = minItem.left; - var right = maxItem.left + maxItem.width; + //var left = minItem.left; + //var right = maxItem.left + maxItem.width; + // + //min = this._toTime(left - 10); + //max = this._toTime(right + 10); + + var lhs = minItem.widthLhs(); // px + var rhs = maxItem.widthRhs(); // px - min = this._toTime(left - 10); - max = this._toTime(right + 10); + var interval = max - min; // ms + if (interval <= 0) {interval = 10;} + var delta = this.props.center.width - lhs - rhs; // px + + + console.log(minId, maxId, minItem.left, this._toScreen(min), this._toScreen(minItem.data.start), lhs, rhs, interval, delta) + + if (delta > 0) { + min -= lhs * interval / delta; // ms + max += rhs * interval / delta; // ms + } } return { @@ -391,6 +463,35 @@ Timeline.prototype.getItemRange = function() { }; }; +/** + * Calculate the data range of the items start and end dates + * @returns {{min: Date | null, max: Date | null}} + * @protected + */ +Timeline.prototype.getDataRange = function() { + var min = null; + var max = null; + + var dataset = this.itemsData && this.itemsData.getDataSet(); + if (dataset) { + dataset.forEach(function (item) { + var start = util.convert(item.start, 'Date').valueOf(); + var end = util.convert(item.end != undefined ? item.end : item.start, 'Date').valueOf(); + if (min === null || start < min) { + min = start; + } + if (max === null || end > max) { + max = start; + } + }); + } + + return { + min: min, + max: max + } +}; + /** * Generate Timeline related information from an event * @param {Event} event diff --git a/lib/timeline/component/item/BoxItem.js b/lib/timeline/component/item/BoxItem.js index 65b720a3..fad4dd18 100644 --- a/lib/timeline/component/item/BoxItem.js +++ b/lib/timeline/component/item/BoxItem.js @@ -215,4 +215,20 @@ BoxItem.prototype.repositionY = function() { dot.style.top = (-this.props.dot.height / 2) + 'px'; }; +/** + * Return the width of the item left from its start date + * @return {number} + */ +BoxItem.prototype.widthLhs = function () { + return this.width / 2; +}; + +/** + * Return the width of the item right from its start date + * @return {number} + */ +BoxItem.prototype.widthRhs = function () { + return this.width / 2; +}; + module.exports = BoxItem; diff --git a/lib/timeline/component/item/Item.js b/lib/timeline/component/item/Item.js index 28d7dae5..686b2a47 100644 --- a/lib/timeline/component/item/Item.js +++ b/lib/timeline/component/item/Item.js @@ -273,4 +273,20 @@ Item.prototype._contentToString = function (content) { return content; }; +/** + * Return the width of the item left from its start date + * @return {number} + */ +Item.prototype.widthLhs = function () { + return 0; +}; + +/** + * Return the width of the item right from the max of its start and end date + * @return {number} + */ +Item.prototype.widthRhs = function () { + return 0; +}; + module.exports = Item; diff --git a/lib/timeline/component/item/PointItem.js b/lib/timeline/component/item/PointItem.js index ace98250..0ce8090a 100644 --- a/lib/timeline/component/item/PointItem.js +++ b/lib/timeline/component/item/PointItem.js @@ -177,4 +177,20 @@ PointItem.prototype.repositionY = function() { } }; +/** + * Return the width of the item left from its start date + * @return {number} + */ +PointItem.prototype.widthLhs = function () { + return this.props.dot.width / 2; +}; + +/** + * Return the width of the item right from its start date + * @return {number} + */ +PointItem.prototype.widthRhs = function () { + return this.width - this.props.dot.width / 2; +}; + module.exports = PointItem; diff --git a/test/timeline.html b/test/timeline.html index 5464de8f..1f8d2816 100644 --- a/test/timeline.html +++ b/test/timeline.html @@ -170,7 +170,7 @@ //min: moment('2013-01-01'), //max: moment('2013-12-31'), //zoomMin: 1000 * 60 * 60 * 24, // 1 day - zoomMax: 1000 * 60 * 60 * 24 * 30 * 6 // 6 months +// zoomMax: 1000 * 60 * 60 * 24 * 30 * 6 // 6 months }; console.timeEnd('create dataset');