Browse Source

Implemented automatic start and end based on the data range

css_transitions
josdejong 11 years ago
parent
commit
1b941b9ec4
7 changed files with 229 additions and 44 deletions
  1. +26
    -0
      src/component/itemset.js
  2. +48
    -3
      src/dataset.js
  3. +4
    -4
      src/range.js
  4. +32
    -10
      src/visualization/timeline.js
  5. +4
    -5
      test/timeline.html
  6. +111
    -18
      vis.js
  7. +4
    -4
      vis.min.js

+ 26
- 0
src/component/itemset.js View File

@ -313,6 +313,32 @@ ItemSet.prototype.setData = function(data) {
this._onAdd(ids);
};
/**
* 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
*/
ItemSet.prototype.getDataRange = function () {
// calculate min from start filed
var data = this.data;
var min = data.min('start');
min = min ? min.start.valueOf() : null;
// calculate max of both start and end fields
var maxStart = data.max('start');
var maxEnd = data.max('end');
maxStart = maxStart ? maxStart.start.valueOf() : null;
maxEnd = maxEnd ? maxEnd.end.valueOf() : null;
var max = Math.max(maxStart, maxEnd);
return {
min: new Date(min),
max: new Date(max)
};
};
/**
* Handle updated items
* @param {Number[]} ids

+ 48
- 3
src/dataset.js View File

@ -366,12 +366,57 @@ DataSet.prototype.remove = function (id, senderId) {
* all but this sender's event subscribers.
*/
DataSet.prototype.clear = function (senderId) {
var items = Object.keys(this.data);
var ids = Object.keys(this.data);
this.data = [];
this.data = {};
this.internalIds = {};
this._trigger('remove', {items: items}, senderId);
this._trigger('remove', {items: ids}, senderId);
};
/**
* Find the item with maximum value of a specified field
* @param {String} field
* @return {Object} item Item containing max value, or null if no items
*/
DataSet.prototype.max = function (field) {
var data = this.data,
ids = Object.keys(data);
var max = null;
var maxField = null;
ids.forEach(function (id) {
var item = data[id];
var itemField = item[field];
if (itemField != null && (!max || itemField > maxField)) {
max = item;
maxField = itemField;
}
});
return max;
};
/**
* Find the item with minimum value of a specified field
* @param {String} field
*/
DataSet.prototype.min = function (field) {
var data = this.data,
ids = Object.keys(data);
var min = null;
var minField = null;
ids.forEach(function (id) {
var item = data[id];
var itemField = item[field];
if (itemField != null && (!min || itemField < minField)) {
min = item;
minField = itemField;
}
});
return min;
};
/**

+ 4
- 4
src/range.js View File

@ -139,15 +139,15 @@ Range.prototype.setRange = function(start, end) {
* @private
*/
Range.prototype._applyRange = function(start, end) {
var newStart = util.cast(start, 'Number');
var newEnd = util.cast(end, 'Number');
var newStart = (start != null) ? util.cast(start, 'Number') : this.start;
var newEnd = (end != null) ? util.cast(end, 'Number') : this.end;
var diff;
// check for valid number
if (newStart == null || isNaN(newStart)) {
if (isNaN(newStart)) {
throw new Error('Invalid start "' + start + '"');
}
if (newEnd == null || isNaN(newEnd)) {
if (isNaN(newEnd)) {
throw new Error('Invalid end "' + end + '"');
}

+ 32
- 10
src/visualization/timeline.js View File

@ -31,13 +31,10 @@ function Timeline (container, data, options) {
this.controller.add(this.main);
// range
var now = moment().minutes(0).seconds(0).milliseconds(0);
var start = options.start && options.start.valueOf() || now.clone().add('days', -3).valueOf();
var end = options.end && options.end.valueOf() || moment(start).clone().add('days', 7).valueOf();
// TODO: if start and end are not provided, calculate range from the dataset
var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
this.range = new Range({
start: start,
end: end
start: now.clone().add('days', -3).valueOf(),
end: now.clone().add('days', 4).valueOf()
});
// TODO: reckon with options moveable and zoomable
this.range.subscribe(this.main, 'move', 'horizontal');
@ -62,11 +59,11 @@ function Timeline (container, data, options) {
// items panel
this.itemset = new ItemSet(this.main, [this.timeaxis], {
orientation: this.options.orientation,
range: this.range,
data: data
orientation: this.options.orientation
});
this.itemset.setRange(this.range);
// set data
if (data) {
this.setData(data);
}
@ -114,5 +111,30 @@ Timeline.prototype.setOptions = function (options) {
* @param {DataSet | Array | DataTable} data
*/
Timeline.prototype.setData = function(data) {
this.itemset.setData(data);
var dataset = this.itemset.data;
if (!dataset) {
// first load of data
this.itemset.setData(data);
// apply the data range as range
var dataRange = this.itemset.getDataRange();
// add 5% on both sides
var min = dataRange.min;
var max = dataRange.max;
if (min != null && max != null) {
var interval = (max.valueOf() - min.valueOf());
min = new Date(min.valueOf() - interval * 0.05);
max = new Date(max.valueOf() + interval * 0.05);
}
// apply range if there is a min or max available
if (min != null || max != null) {
this.range.setRange(min, max);
}
}
else {
// updated data
this.itemset.setData(data);
}
};

+ 4
- 5
test/timeline.html View File

@ -64,19 +64,18 @@
{id: 1, content: 'item 1<br>start', start: now.clone().add('days', 4).toDate()},
{id: 2, content: 'item 2', start: now.clone().add('days', -2).toDate() },
{id: 3, content: 'item 3', start: now.clone().add('days', 2).toDate()},
{id: 4, content: 'item 4', start: now.clone().add('days', 0).toDate(), end: now.clone().add('days', 3).toDate()},
{id: 4, content: 'item 4', start: now.clone().add('days', 0).toDate(), end: now.clone().add('days', 7).toDate()},
{id: 5, content: 'item 5', start: now.clone().add('days', 9).toDate(), type:'point'},
{id: 6, content: 'item 6', start: now.clone().add('days', 11).toDate()}
]);
var container = document.getElementById('visualization');
var options = {
start: now.clone().add('days', -3).valueOf(),
end: now.clone().add('days', 7).valueOf(),
//start: now.clone().add('days', -7).valueOf(),
//end: now.clone().add('days', 7).valueOf(),
min: moment('2013-01-01').valueOf(),
max: moment('2013-12-31').valueOf(),
// zoomMin: 1000 * 60 * 60, // 1 hour
zoomMin: 1000 * 60 * 60 * 24, // 1 hour
zoomMin: 1000 * 60 * 60 * 24, // 1 day
zoomMax: 1000 * 60 * 60 * 24 * 30 * 6 // 6 months
};

+ 111
- 18
vis.js View File

@ -5,7 +5,7 @@
* A dynamic, browser-based visualization library.
*
* @version 0.0.5
* @date 2013-04-18
* @date 2013-04-23
*
* @license
* Copyright (C) 2011-2013 Almende B.V, http://almende.com
@ -1723,12 +1723,57 @@ DataSet.prototype.remove = function (id, senderId) {
* all but this sender's event subscribers.
*/
DataSet.prototype.clear = function (senderId) {
var items = Object.keys(this.data);
var ids = Object.keys(this.data);
this.data = [];
this.data = {};
this.internalIds = {};
this._trigger('remove', {items: items}, senderId);
this._trigger('remove', {items: ids}, senderId);
};
/**
* Find the item with maximum value of a specified field
* @param {String} field
* @return {Object} item Item containing max value, or null if no items
*/
DataSet.prototype.max = function (field) {
var data = this.data,
ids = Object.keys(data);
var max = null;
var maxField = null;
ids.forEach(function (id) {
var item = data[id];
var itemField = item[field];
if (itemField != null && (!max || itemField > maxField)) {
max = item;
maxField = itemField;
}
});
return max;
};
/**
* Find the item with minimum value of a specified field
* @param {String} field
*/
DataSet.prototype.min = function (field) {
var data = this.data,
ids = Object.keys(data);
var min = null;
var minField = null;
ids.forEach(function (id) {
var item = data[id];
var itemField = item[field];
if (itemField != null && (!min || itemField < minField)) {
min = item;
minField = itemField;
}
});
return min;
};
/**
@ -2157,15 +2202,15 @@ Range.prototype.setRange = function(start, end) {
* @private
*/
Range.prototype._applyRange = function(start, end) {
var newStart = util.cast(start, 'Number');
var newEnd = util.cast(end, 'Number');
var newStart = (start != null) ? util.cast(start, 'Number') : this.start;
var newEnd = (end != null) ? util.cast(end, 'Number') : this.end;
var diff;
// check for valid number
if (newStart == null || isNaN(newStart)) {
if (isNaN(newStart)) {
throw new Error('Invalid start "' + start + '"');
}
if (newEnd == null || isNaN(newEnd)) {
if (isNaN(newEnd)) {
throw new Error('Invalid end "' + end + '"');
}
@ -3966,6 +4011,32 @@ ItemSet.prototype.setData = function(data) {
this._onAdd(ids);
};
/**
* 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
*/
ItemSet.prototype.getDataRange = function () {
// calculate min from start filed
var data = this.data;
var min = data.min('start');
min = min ? min.start.valueOf() : null;
// calculate max of both start and end fields
var maxStart = data.max('start');
var maxEnd = data.max('end');
maxStart = maxStart ? maxStart.start.valueOf() : null;
maxEnd = maxEnd ? maxEnd.end.valueOf() : null;
var max = Math.max(maxStart, maxEnd);
return {
min: new Date(min),
max: new Date(max)
};
};
/**
* Handle updated items
* @param {Number[]} ids
@ -4838,13 +4909,10 @@ function Timeline (container, data, options) {
this.controller.add(this.main);
// range
var now = moment().minutes(0).seconds(0).milliseconds(0);
var start = options.start && options.start.valueOf() || now.clone().add('days', -3).valueOf();
var end = options.end && options.end.valueOf() || moment(start).clone().add('days', 7).valueOf();
// TODO: if start and end are not provided, calculate range from the dataset
var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
this.range = new Range({
start: start,
end: end
start: now.clone().add('days', -3).valueOf(),
end: now.clone().add('days', 4).valueOf()
});
// TODO: reckon with options moveable and zoomable
this.range.subscribe(this.main, 'move', 'horizontal');
@ -4869,11 +4937,11 @@ function Timeline (container, data, options) {
// items panel
this.itemset = new ItemSet(this.main, [this.timeaxis], {
orientation: this.options.orientation,
range: this.range,
data: data
orientation: this.options.orientation
});
this.itemset.setRange(this.range);
// set data
if (data) {
this.setData(data);
}
@ -4921,7 +4989,32 @@ Timeline.prototype.setOptions = function (options) {
* @param {DataSet | Array | DataTable} data
*/
Timeline.prototype.setData = function(data) {
this.itemset.setData(data);
var dataset = this.itemset.data;
if (!dataset) {
// first load of data
this.itemset.setData(data);
// apply the data range as range
var dataRange = this.itemset.getDataRange();
// add 5% on both sides
var min = dataRange.min;
var max = dataRange.max;
if (min != null && max != null) {
var interval = (max.valueOf() - min.valueOf());
min = new Date(min.valueOf() - interval * 0.05);
max = new Date(max.valueOf() + interval * 0.05);
}
// apply range if there is a min or max available
if (min != null || max != null) {
this.range.setRange(min, max);
}
}
else {
// updated data
this.itemset.setData(data);
}
};
// moment.js

+ 4
- 4
vis.min.js
File diff suppressed because it is too large
View File


Loading…
Cancel
Save