Browse Source

Fixed #1215: inconsistent types of properties `start` and `end` in callback functions `onMove`, `onMoving`, `onAdd`

webworkersNetwork
jos 9 years ago
parent
commit
bbbaeb48e0
4 changed files with 138 additions and 62 deletions
  1. +5
    -0
      HISTORY.md
  2. +68
    -25
      dist/vis.js
  3. +61
    -36
      lib/timeline/component/ItemSet.js
  4. +4
    -1
      test/timeline_groups.html

+ 5
- 0
HISTORY.md View File

@ -8,6 +8,11 @@ http://visjs.org
- Added German (de) locale. Thanks @Tooa. - Added German (de) locale. Thanks @Tooa.
### Timeline
- Fixed #1215: inconsistent types of properties `start` and `end` in callback
functions `onMove`, `onMoving`, `onAdd`.
## 2015-08-28, version 4.8.0 ## 2015-08-28, version 4.8.0

+ 68
- 25
dist/vis.js View File

@ -5,7 +5,7 @@
* A dynamic, browser-based visualization library. * A dynamic, browser-based visualization library.
* *
* @version 4.8.1-SNAPSHOT * @version 4.8.1-SNAPSHOT
* @date 2015-08-28
* @date 2015-09-04
* *
* @license * @license
* Copyright (C) 2011-2015 Almende B.V, http://almende.com * Copyright (C) 2011-2015 Almende B.V, http://almende.com
@ -16734,7 +16734,7 @@ return /******/ (function(modules) { // webpackBootstrap
item: dragLeftItem, item: dragLeftItem,
initialX: event.center.x, initialX: event.center.x,
dragLeft: true, dragLeft: true,
data: util.extend({}, item.data) // clone the items data
data: this._cloneItemData(item.data)
}; };
this.touchParams.itemProps = [props]; this.touchParams.itemProps = [props];
@ -16743,7 +16743,7 @@ return /******/ (function(modules) { // webpackBootstrap
item: dragRightItem, item: dragRightItem,
initialX: event.center.x, initialX: event.center.x,
dragRight: true, dragRight: true,
data: util.extend({}, item.data) // clone the items data
data: this._cloneItemData(item.data)
}; };
this.touchParams.itemProps = [props]; this.touchParams.itemProps = [props];
@ -16752,18 +16752,16 @@ return /******/ (function(modules) { // webpackBootstrap
var baseGroupIndex = this._getGroupIndex(item.data.group); var baseGroupIndex = this._getGroupIndex(item.data.group);
this.touchParams.itemProps = this.getSelection().map(function (id) {
this.touchParams.itemProps = this.getSelection().map((function (id) {
var item = me.items[id]; var item = me.items[id];
var groupIndex = me._getGroupIndex(item.data.group); var groupIndex = me._getGroupIndex(item.data.group);
var props = {
return {
item: item, item: item,
initialX: event.center.x, initialX: event.center.x,
groupOffset: baseGroupIndex - groupIndex, groupOffset: baseGroupIndex - groupIndex,
data: util.extend({}, item.data) // clone the items data
data: this._cloneItemData(item.data)
}; };
return props;
});
}).bind(this));
} }
event.stopPropagation(); event.stopPropagation();
@ -16805,14 +16803,14 @@ return /******/ (function(modules) { // webpackBootstrap
var newItem = new RangeItem(itemData, this.conversion, this.options); var newItem = new RangeItem(itemData, this.conversion, this.options);
newItem.id = id; // TODO: not so nice setting id afterwards newItem.id = id; // TODO: not so nice setting id afterwards
newItem.data = itemData;
newItem.data = this._cloneItemData(itemData);
this._addItem(newItem); this._addItem(newItem);
var props = { var props = {
item: newItem, item: newItem,
dragRight: true, dragRight: true,
initialX: event.center.x, initialX: event.center.x,
data: util.extend({}, itemData)
data: newItem.data
}; };
this.touchParams.itemProps = [props]; this.touchParams.itemProps = [props];
@ -16851,14 +16849,12 @@ return /******/ (function(modules) { // webpackBootstrap
} }
// move // move
this.touchParams.itemProps.forEach(function (props) {
var newProps = {};
this.touchParams.itemProps.forEach((function (props) {
var current = me.body.util.toTime(event.center.x - xOffset); var current = me.body.util.toTime(event.center.x - xOffset);
var initial = me.body.util.toTime(props.initialX - xOffset); var initial = me.body.util.toTime(props.initialX - xOffset);
var offset = current - initial;
var itemData = util.extend({}, props.item.data); // clone the data
var offset = current - initial; // ms
var itemData = this._cloneItemData(props.item.data); // clone the data
if (props.item.editable === false) { if (props.item.editable === false) {
return; return;
} }
@ -16871,6 +16867,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (itemData.start != undefined) { if (itemData.start != undefined) {
var initialStart = util.convert(props.data.start, 'Date'); var initialStart = util.convert(props.data.start, 'Date');
var start = new Date(initialStart.valueOf() + offset); var start = new Date(initialStart.valueOf() + offset);
// TODO: pass a Moment instead of a Date to snap(). (Breaking change)
itemData.start = snap ? snap(start, scale, step) : start; itemData.start = snap ? snap(start, scale, step) : start;
} }
} else if (props.dragRight) { } else if (props.dragRight) {
@ -16878,6 +16875,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (itemData.end != undefined) { if (itemData.end != undefined) {
var initialEnd = util.convert(props.data.end, 'Date'); var initialEnd = util.convert(props.data.end, 'Date');
var end = new Date(initialEnd.valueOf() + offset); var end = new Date(initialEnd.valueOf() + offset);
// TODO: pass a Moment instead of a Date to snap(). (Breaking change)
itemData.end = snap ? snap(end, scale, step) : end; itemData.end = snap ? snap(end, scale, step) : end;
} }
} else { } else {
@ -16890,9 +16888,11 @@ return /******/ (function(modules) { // webpackBootstrap
var initialEnd = util.convert(props.data.end, 'Date'); var initialEnd = util.convert(props.data.end, 'Date');
var duration = initialEnd.valueOf() - initialStart.valueOf(); var duration = initialEnd.valueOf() - initialStart.valueOf();
// TODO: pass a Moment instead of a Date to snap(). (Breaking change)
itemData.start = snap ? snap(start, scale, step) : start; itemData.start = snap ? snap(start, scale, step) : start;
itemData.end = new Date(itemData.start.valueOf() + duration); itemData.end = new Date(itemData.start.valueOf() + duration);
} else { } else {
// TODO: pass a Moment instead of a Date to snap(). (Breaking change)
itemData.start = snap ? snap(start, scale, step) : start; itemData.start = snap ? snap(start, scale, step) : start;
} }
} }
@ -16914,12 +16914,13 @@ return /******/ (function(modules) { // webpackBootstrap
} }
// confirm moving the item // confirm moving the item
itemData = this._cloneItemData(itemData); // convert start and end to the correct type
me.options.onMoving(itemData, function (itemData) { me.options.onMoving(itemData, function (itemData) {
if (itemData) { if (itemData) {
props.item.setData(itemData); props.item.setData(itemData);
} }
}); });
});
}).bind(this));
this.stackDirty = true; // force re-stacking of all items next redraw this.stackDirty = true; // force re-stacking of all items next redraw
this.body.emitter.emit('change'); this.body.emitter.emit('change');
@ -16959,7 +16960,7 @@ return /******/ (function(modules) { // webpackBootstrap
var itemProps = this.touchParams.itemProps; var itemProps = this.touchParams.itemProps;
this.touchParams.itemProps = null; this.touchParams.itemProps = null;
itemProps.forEach(function (props) {
itemProps.forEach((function (props) {
var id = props.item.id; var id = props.item.id;
var exists = me.itemsData.get(id, me.itemOptions) != null; var exists = me.itemsData.get(id, me.itemOptions) != null;
@ -16977,7 +16978,7 @@ return /******/ (function(modules) { // webpackBootstrap
}); });
} else { } else {
// update existing item // update existing item
var itemData = util.extend({}, props.item.data); // clone the data
var itemData = this._cloneItemData(props.item.data); // convert start and end to the correct type
me.options.onMove(itemData, function (itemData) { me.options.onMove(itemData, function (itemData) {
if (itemData) { if (itemData) {
// apply changes // apply changes
@ -16992,7 +16993,7 @@ return /******/ (function(modules) { // webpackBootstrap
} }
}); });
} }
});
}).bind(this));
} }
}; };
@ -17233,7 +17234,7 @@ return /******/ (function(modules) { // webpackBootstrap
var scale = this.body.util.getScale(); var scale = this.body.util.getScale();
var step = this.body.util.getStep(); var step = this.body.util.getStep();
var newItem = {
var newItemData = {
start: snap ? snap(start, scale, step) : start, start: snap ? snap(start, scale, step) : start,
content: 'new item' content: 'new item'
}; };
@ -17241,18 +17242,19 @@ return /******/ (function(modules) { // webpackBootstrap
// when default type is a range, add a default end date to the new item // when default type is a range, add a default end date to the new item
if (this.options.type === 'range') { if (this.options.type === 'range') {
var end = this.body.util.toTime(x + this.props.width / 5); var end = this.body.util.toTime(x + this.props.width / 5);
newItem.end = snap ? snap(end, scale, step) : end;
newItemData.end = snap ? snap(end, scale, step) : end;
} }
newItem[this.itemsData._fieldId] = util.randomUUID();
newItemData[this.itemsData._fieldId] = util.randomUUID();
var group = this.groupFromTarget(event); var group = this.groupFromTarget(event);
if (group) { if (group) {
newItem.group = group.groupId;
newItemData.group = group.groupId;
} }
// execute async handler to customize (or cancel) adding an item // execute async handler to customize (or cancel) adding an item
this.options.onAdd(newItem, function (item) {
newItemData = this._cloneItemData(newItemData); // convert start and end to the correct type
this.options.onAdd(newItemData, function (item) {
if (item) { if (item) {
me.itemsData.getDataSet().add(item); me.itemsData.getDataSet().add(item);
// TODO: need to trigger a redraw? // TODO: need to trigger a redraw?
@ -17419,6 +17421,29 @@ return /******/ (function(modules) { // webpackBootstrap
return null; return null;
}; };
/**
* Clone the data of an item, and "normalize" it: convert the start and end date
* to the type (Date, Moment, ...) configured in the DataSet. If not configured,
* start and end are converted to Date.
* @param {Object} itemData, typically `item.data`
* @return {Object} The cloned object
* @private
*/
ItemSet.prototype._cloneItemData = function (itemData) {
var clone = util.extend({}, itemData);
// convert start and end date to the type (Date, Moment, ...) configured in the DataSet
var type = this.itemsData.getDataSet()._options.type;
if (clone.start != undefined) {
clone.start = util.convert(clone.start, type && type.start || 'Date');
}
if (clone.end != undefined) {
clone.end = util.convert(clone.end, type && type.end || 'Date');
}
return clone;
};
module.exports = ItemSet; module.exports = ItemSet;
/***/ }, /***/ },
@ -43054,6 +43079,24 @@ return /******/ (function(modules) { // webpackBootstrap
exports['en_EN'] = exports['en']; exports['en_EN'] = exports['en'];
exports['en_US'] = exports['en']; exports['en_US'] = exports['en'];
// German
exports['de'] = {
edit: 'Editieren',
del: 'Lösche Auswahl',
back: 'Zurück',
addNode: 'Knoten hinzufügen',
addEdge: 'Kante hinzufügen',
editNode: 'Knoten editieren',
editEdge: 'Kante editieren',
addDescription: 'Klicke auf eine freie Stelle, um einen neuen Knoten zu plazieren.',
edgeDescription: 'Klicke auf einen Knoten und ziehe die Kante zu einem anderen Knoten, um diese zu verbinden.',
editEdgeDescription: 'Klicke auf die Verbindungspunkte und ziehe diese auf einen Knoten, um sie zu verbinden.',
createEdgeError: 'Es ist nicht möglich, Kanten mit Clustern zu verbinden.',
deleteClusterError: 'Cluster können nicht gelöscht werden.',
editClusterError: 'Cluster können nicht editiert werden.'
};
exports['de_DE'] = exports['de'];
// Spanish // Spanish
exports['es'] = { exports['es'] = {
edit: 'Editar', edit: 'Editar',

+ 61
- 36
lib/timeline/component/ItemSet.js View File

@ -1160,7 +1160,7 @@ ItemSet.prototype._getGroupIndex = function(groupId) {
if (groupId == this.groupIds[i]) if (groupId == this.groupIds[i])
return i; return i;
} }
}
};
/** /**
* Start dragging the selected events * Start dragging the selected events
@ -1174,8 +1174,8 @@ ItemSet.prototype._onDragStart = function (event) {
if (item && item.selected) { if (item && item.selected) {
if (!this.options.editable.updateTime &&
!this.options.editable.updateGroup &&
if (!this.options.editable.updateTime &&
!this.options.editable.updateGroup &&
!item.editable) { !item.editable) {
return; return;
} }
@ -1193,7 +1193,7 @@ ItemSet.prototype._onDragStart = function (event) {
item: dragLeftItem, item: dragLeftItem,
initialX: event.center.x, initialX: event.center.x,
dragLeft: true, dragLeft: true,
data: util.extend({}, item.data) // clone the items data
data: this._cloneItemData(item.data)
}; };
this.touchParams.itemProps = [props]; this.touchParams.itemProps = [props];
@ -1203,7 +1203,7 @@ ItemSet.prototype._onDragStart = function (event) {
item: dragRightItem, item: dragRightItem,
initialX: event.center.x, initialX: event.center.x,
dragRight: true, dragRight: true,
data: util.extend({}, item.data) // clone the items data
data: this._cloneItemData(item.data)
}; };
this.touchParams.itemProps = [props]; this.touchParams.itemProps = [props];
@ -1216,15 +1216,13 @@ ItemSet.prototype._onDragStart = function (event) {
this.touchParams.itemProps = this.getSelection().map(function (id) { this.touchParams.itemProps = this.getSelection().map(function (id) {
var item = me.items[id]; var item = me.items[id];
var groupIndex = me._getGroupIndex(item.data.group); var groupIndex = me._getGroupIndex(item.data.group);
var props = {
return {
item: item, item: item,
initialX: event.center.x, initialX: event.center.x,
groupOffset: baseGroupIndex-groupIndex, groupOffset: baseGroupIndex-groupIndex,
data: util.extend({}, item.data) // clone the items data
data: this._cloneItemData(item.data)
}; };
return props;
});
}.bind(this));
} }
event.stopPropagation(); event.stopPropagation();
@ -1267,14 +1265,14 @@ ItemSet.prototype._onDragStartAddItem = function (event) {
var newItem = new RangeItem(itemData, this.conversion, this.options); var newItem = new RangeItem(itemData, this.conversion, this.options);
newItem.id = id; // TODO: not so nice setting id afterwards newItem.id = id; // TODO: not so nice setting id afterwards
newItem.data = itemData;
newItem.data = this._cloneItemData(itemData);
this._addItem(newItem); this._addItem(newItem);
var props = { var props = {
item: newItem, item: newItem,
dragRight: true, dragRight: true,
initialX: event.center.x, initialX: event.center.x,
data: util.extend({}, itemData)
data: newItem.data
}; };
this.touchParams.itemProps = [props]; this.touchParams.itemProps = [props];
@ -1301,32 +1299,30 @@ ItemSet.prototype._onDrag = function (event) {
var updateGroupAllowed = me.options.editable.updateGroup; var updateGroupAllowed = me.options.editable.updateGroup;
var newGroupBase = null; var newGroupBase = null;
if (updateGroupAllowed && selectedItem) { if (updateGroupAllowed && selectedItem) {
if (selectedItem.data.group != undefined) {
// drag from one group to another
var group = me.groupFromTarget(event);
if (group) {
//we know the offset for all items, so the new group for all items
//will be relative to this one.
newGroupBase = this._getGroupIndex(group.groupId);
}
if (selectedItem.data.group != undefined) {
// drag from one group to another
var group = me.groupFromTarget(event);
if (group) {
//we know the offset for all items, so the new group for all items
//will be relative to this one.
newGroupBase = this._getGroupIndex(group.groupId);
} }
}
} }
// move // move
this.touchParams.itemProps.forEach(function (props) { this.touchParams.itemProps.forEach(function (props) {
var newProps = {};
var current = me.body.util.toTime(event.center.x - xOffset); var current = me.body.util.toTime(event.center.x - xOffset);
var initial = me.body.util.toTime(props.initialX - xOffset); var initial = me.body.util.toTime(props.initialX - xOffset);
var offset = current - initial;
var itemData = util.extend({}, props.item.data); // clone the data
var offset = current - initial; // ms
var itemData = this._cloneItemData(props.item.data); // clone the data
if (props.item.editable === false) { if (props.item.editable === false) {
return; return;
} }
var updateTimeAllowed = me.options.editable.updateTime ||
props.item.editable === true;
var updateTimeAllowed = me.options.editable.updateTime ||
props.item.editable === true;
if (updateTimeAllowed) { if (updateTimeAllowed) {
if (props.dragLeft) { if (props.dragLeft) {
@ -1334,6 +1330,7 @@ ItemSet.prototype._onDrag = function (event) {
if (itemData.start != undefined) { if (itemData.start != undefined) {
var initialStart = util.convert(props.data.start, 'Date'); var initialStart = util.convert(props.data.start, 'Date');
var start = new Date(initialStart.valueOf() + offset); var start = new Date(initialStart.valueOf() + offset);
// TODO: pass a Moment instead of a Date to snap(). (Breaking change)
itemData.start = snap ? snap(start, scale, step) : start; itemData.start = snap ? snap(start, scale, step) : start;
} }
} }
@ -1342,6 +1339,7 @@ ItemSet.prototype._onDrag = function (event) {
if (itemData.end != undefined) { if (itemData.end != undefined) {
var initialEnd = util.convert(props.data.end, 'Date'); var initialEnd = util.convert(props.data.end, 'Date');
var end = new Date(initialEnd.valueOf() + offset); var end = new Date(initialEnd.valueOf() + offset);
// TODO: pass a Moment instead of a Date to snap(). (Breaking change)
itemData.end = snap ? snap(end, scale, step) : end; itemData.end = snap ? snap(end, scale, step) : end;
} }
} }
@ -1355,18 +1353,20 @@ ItemSet.prototype._onDrag = function (event) {
var initialEnd = util.convert(props.data.end, 'Date'); var initialEnd = util.convert(props.data.end, 'Date');
var duration = initialEnd.valueOf() - initialStart.valueOf(); var duration = initialEnd.valueOf() - initialStart.valueOf();
// TODO: pass a Moment instead of a Date to snap(). (Breaking change)
itemData.start = snap ? snap(start, scale, step) : start; itemData.start = snap ? snap(start, scale, step) : start;
itemData.end = new Date(itemData.start.valueOf() + duration); itemData.end = new Date(itemData.start.valueOf() + duration);
} }
else { else {
// TODO: pass a Moment instead of a Date to snap(). (Breaking change)
itemData.start = snap ? snap(start, scale, step) : start; itemData.start = snap ? snap(start, scale, step) : start;
} }
} }
} }
} }
var updateGroupAllowed = me.options.editable.updateGroup ||
props.item.editable === true;
var updateGroupAllowed = me.options.editable.updateGroup ||
props.item.editable === true;
if (updateGroupAllowed && (!props.dragLeft && !props.dragRight) && newGroupBase!=null) { if (updateGroupAllowed && (!props.dragLeft && !props.dragRight) && newGroupBase!=null) {
if (itemData.group != undefined) { if (itemData.group != undefined) {
@ -1381,12 +1381,13 @@ ItemSet.prototype._onDrag = function (event) {
} }
// confirm moving the item // confirm moving the item
itemData = this._cloneItemData(itemData); // convert start and end to the correct type
me.options.onMoving(itemData, function (itemData) { me.options.onMoving(itemData, function (itemData) {
if (itemData) { if (itemData) {
props.item.setData(itemData); props.item.setData(itemData);
} }
}); });
});
}.bind(this));
this.stackDirty = true; // force re-stacking of all items next redraw this.stackDirty = true; // force re-stacking of all items next redraw
this.body.emitter.emit('change'); this.body.emitter.emit('change');
@ -1445,7 +1446,7 @@ ItemSet.prototype._onDragEnd = function (event) {
} }
else { else {
// update existing item // update existing item
var itemData = util.extend({}, props.item.data); // clone the data
var itemData = this._cloneItemData(props.item.data); // convert start and end to the correct type
me.options.onMove(itemData, function (itemData) { me.options.onMove(itemData, function (itemData) {
if (itemData) { if (itemData) {
// apply changes // apply changes
@ -1461,7 +1462,7 @@ ItemSet.prototype._onDragEnd = function (event) {
} }
}); });
} }
});
}.bind(this));
} }
}; };
@ -1708,7 +1709,7 @@ ItemSet.prototype._onAddItem = function (event) {
var scale = this.body.util.getScale(); var scale = this.body.util.getScale();
var step = this.body.util.getStep(); var step = this.body.util.getStep();
var newItem = {
var newItemData = {
start: snap ? snap(start, scale, step) : start, start: snap ? snap(start, scale, step) : start,
content: 'new item' content: 'new item'
}; };
@ -1716,18 +1717,19 @@ ItemSet.prototype._onAddItem = function (event) {
// when default type is a range, add a default end date to the new item // when default type is a range, add a default end date to the new item
if (this.options.type === 'range') { if (this.options.type === 'range') {
var end = this.body.util.toTime(x + this.props.width / 5); var end = this.body.util.toTime(x + this.props.width / 5);
newItem.end = snap ? snap(end, scale, step) : end;
newItemData.end = snap ? snap(end, scale, step) : end;
} }
newItem[this.itemsData._fieldId] = util.randomUUID();
newItemData[this.itemsData._fieldId] = util.randomUUID();
var group = this.groupFromTarget(event); var group = this.groupFromTarget(event);
if (group) { if (group) {
newItem.group = group.groupId;
newItemData.group = group.groupId;
} }
// execute async handler to customize (or cancel) adding an item // execute async handler to customize (or cancel) adding an item
this.options.onAdd(newItem, function (item) {
newItemData = this._cloneItemData(newItemData); // convert start and end to the correct type
this.options.onAdd(newItemData, function (item) {
if (item) { if (item) {
me.itemsData.getDataSet().add(item); me.itemsData.getDataSet().add(item);
// TODO: need to trigger a redraw? // TODO: need to trigger a redraw?
@ -1901,4 +1903,27 @@ ItemSet.itemSetFromTarget = function(event) {
return null; return null;
}; };
/**
* Clone the data of an item, and "normalize" it: convert the start and end date
* to the type (Date, Moment, ...) configured in the DataSet. If not configured,
* start and end are converted to Date.
* @param {Object} itemData, typically `item.data`
* @return {Object} The cloned object
* @private
*/
ItemSet.prototype._cloneItemData = function (itemData) {
var clone = util.extend({}, itemData);
// convert start and end date to the type (Date, Moment, ...) configured in the DataSet
var type = this.itemsData.getDataSet()._options.type;
if (clone.start != undefined) {
clone.start = util.convert(clone.start, type && type.start || 'Date');
}
if (clone.end != undefined) {
clone.end = util.convert(clone.end , type && type.end || 'Date');
}
return clone;
};
module.exports = ItemSet; module.exports = ItemSet;

+ 4
- 1
test/timeline_groups.html View File

@ -71,7 +71,9 @@
} }
// create a dataset with items // create a dataset with items
var items = new vis.DataSet();
var items = new vis.DataSet({
type: {start: 'Moment', end: 'Moment'}
});
for (var i = 0; i < itemCount; i++) { for (var i = 0; i < itemCount; i++) {
var start = now.clone().add(Math.random() * 200, 'hours'); var start = now.clone().add(Math.random() * 200, 'hours');
var end = Math.random() > 0.5 ? start.clone().add(24, 'hours') : undefined; var end = Math.random() > 0.5 ? start.clone().add(24, 'hours') : undefined;
@ -117,6 +119,7 @@
}, },
onMove: function (item, callback) { onMove: function (item, callback) {
console.log('onMove', item)
if (confirm('Do you really want to move the item to\n' + if (confirm('Do you really want to move the item to\n' +
'start: ' + item.start + '\n' + 'start: ' + item.start + '\n' +
'end: ' + item.end + '?')) { 'end: ' + item.end + '?')) {

Loading…
Cancel
Save