From e3998b89d93296581f080c1eb02091170e41ea8e Mon Sep 17 00:00:00 2001 From: josdejong Date: Tue, 18 Feb 2014 11:50:21 +0100 Subject: [PATCH] Implemented handlers onAdd, onUpdate, onMove, onRemove --- examples/timeline/06_event_listeners.html | 4 +- examples/timeline/08_validate_changes.html | 91 +++++++++++++++++ examples/timeline/index.html | 1 + src/timeline/Timeline.js | 113 ++++++++++++++------- src/timeline/component/ItemSet.js | 43 ++++++-- src/timeline/component/RootPanel.js | 2 +- src/timeline/component/item/ItemRange.js | 2 +- 7 files changed, 208 insertions(+), 48 deletions(-) create mode 100644 examples/timeline/08_validate_changes.html diff --git a/examples/timeline/06_event_listeners.html b/examples/timeline/06_event_listeners.html index e488e930..02cb8e45 100644 --- a/examples/timeline/06_event_listeners.html +++ b/examples/timeline/06_event_listeners.html @@ -34,7 +34,9 @@ ]); var container = document.getElementById('visualization'); - var options = {}; + var options = { + editable: true + }; var timeline = new vis.Timeline(container, items, options); timeline.on('rangechange', function (properties) { diff --git a/examples/timeline/08_validate_changes.html b/examples/timeline/08_validate_changes.html new file mode 100644 index 00000000..2964f10c --- /dev/null +++ b/examples/timeline/08_validate_changes.html @@ -0,0 +1,91 @@ + + + + Timeline | Validate changes + + + + + + + +
+

+
+ + + + \ No newline at end of file diff --git a/examples/timeline/index.html b/examples/timeline/index.html index b1f89374..d7a447bf 100644 --- a/examples/timeline/index.html +++ b/examples/timeline/index.html @@ -19,6 +19,7 @@

05_groups.html

06_event_listeners.html

07_custom_time_bar.html

+

08_validate_changes.html

diff --git a/src/timeline/Timeline.js b/src/timeline/Timeline.js index ee42f0df..d19b54b0 100644 --- a/src/timeline/Timeline.js +++ b/src/timeline/Timeline.js @@ -11,7 +11,7 @@ function Timeline (container, items, options) { this.options = { orientation: 'bottom', autoResize: true, - editable: true, + editable: false, selectable: true, snap: null, // will be specified after timeaxis is created @@ -25,7 +25,20 @@ function Timeline (container, items, options) { showMinorLabels: true, showMajorLabels: true, showCurrentTime: false, - showCustomTime: false + showCustomTime: false, + + onAdd: function (item, callback) { + callback(item); + }, + onUpdate: function (item, callback) { + callback(item); + }, + onMoved: function (item, callback) { + callback(item); + }, + onRemove: function (item, callback) { + callback(item); + } }; // controller @@ -57,6 +70,9 @@ function Timeline (container, items, options) { // multi select when holding mouse/touch, or on ctrl+click this.controller.on('hold', this._onMultiSelectItem.bind(this)); + // add item on doubletap + this.controller.on('doubletap', this._onAddItem.bind(this)); + // item panel var itemOptions = Object.create(this.options); itemOptions.left = function () { @@ -408,9 +424,7 @@ Timeline.prototype.getSelection = function getSelection() { */ // TODO: move this function to ItemSet Timeline.prototype._onSelectItem = function (event) { - if (!this.options.selectable) { - return; - } + if (!this.options.selectable) return; var item = ItemSet.itemFromTarget(event); @@ -424,6 +438,63 @@ Timeline.prototype._onSelectItem = function (event) { event.stopPropagation(); }; +/** + * Handle creation and updates of an item on double tap + * @param event + * @private + */ +Timeline.prototype._onAddItem = function (event) { + if (!this.options.selectable) return; + + var me = this, + item = ItemSet.itemFromTarget(event); + + if (item) { + // update item + + // execute async handler to update the item (or cancel it) + var itemData = me.itemsData.get(item.id); // get a clone of the data from the dataset + this.options.onUpdate(itemData, function (itemData) { + if (itemData) { + me.itemsData.update(itemData); + } + }); + } + else { + // add item + var xAbs = vis.util.getAbsoluteLeft(this.rootPanel.frame); + var x = event.gesture.center.pageX - xAbs; + var newItem = { + start: this.timeaxis.snap(this._toTime(x)), + content: 'new item' + }; + + var id = util.randomUUID(); + newItem[this.itemsData.fieldId] = id; + + var group = GroupSet.groupFromTarget(event); + if (group) { + newItem.group = group.groupId; + } + + // execute async handler to customize (or cancel) adding an item + this.options.onAdd(newItem, function (item) { + if (item) { + me.itemsData.add(newItem); + + // select the created item after it is repainted + me.controller.once('repaint', function () { + me.setSelection([id]); + + me.controller.emit('select', { + items: me.getSelection() + }); + }.bind(me)); + } + }); + } +}; + /** * Handle selecting/deselecting multiple items when holding an item * @param {Event} event @@ -431,9 +502,7 @@ Timeline.prototype._onSelectItem = function (event) { */ // TODO: move this function to ItemSet Timeline.prototype._onMultiSelectItem = function (event) { - if (!this.options.selectable) { - return; - } + if (!this.options.selectable) return; var selection, item = ItemSet.itemFromTarget(event); @@ -460,33 +529,7 @@ Timeline.prototype._onMultiSelectItem = function (event) { } else { // create a new item - var xAbs = vis.util.getAbsoluteLeft(this.rootPanel.frame); - var x = event.gesture.center.pageX - xAbs; - var newItem = { - start: this.timeaxis.snap(this._toTime(x)), - content: 'new item' - }; - - var id = util.randomUUID(); - newItem[this.itemsData.fieldId] = id; - - var group = GroupSet.groupFromTarget(event); - if (group) { - newItem.group = group.groupId; - } - - // TODO: implement an async handler to customize adding an item - - this.itemsData.add(newItem); - - // select the created item after it is repainted - this.controller.once('repaint', function () { - this.setSelection([id]); - - this.controller.emit('select', { - items: this.getSelection() - }); - }.bind(this)); + this._onAddItem(event); } }; diff --git a/src/timeline/component/ItemSet.js b/src/timeline/component/ItemSet.js index 4713d9a1..ec7c9d1f 100644 --- a/src/timeline/component/ItemSet.js +++ b/src/timeline/component/ItemSet.js @@ -580,8 +580,17 @@ ItemSet.prototype.getItems = function getItems() { * @param {String | Number} id */ ItemSet.prototype.removeItem = function removeItem (id) { - var dataset = this._myDataSet(); - dataset.remove(id); + var item = this.itemsData.get(id), + dataset = this._myDataSet(); + + if (item) { + // confirm deletion + this.options.onRemove(item, function (item) { + if (item) { + dataset.remove(item); + } + }); + } }; /** @@ -751,6 +760,8 @@ ItemSet.prototype._onDrag = function (event) { } }); + // TODO: implement onMoving handler + // TODO: implement dragging from one group to another this.requestReflow(); @@ -767,25 +778,37 @@ ItemSet.prototype._onDrag = function (event) { ItemSet.prototype._onDragEnd = function (event) { if (this.touchParams.itemProps) { // prepare a change set for the changed items - var changes = []; + var changes = [], + me = this; + this.touchParams.itemProps.forEach(function (props) { - var change = { - id: props.item.id - }; + var id = props.item.id, + item = me.itemsData.get(id); var changed = false; if ('start' in props.item.data) { changed = (props.start != props.item.data.start.valueOf()); - change.start = props.item.data.start; + item.start = util.convert(props.item.data.start, me.itemsData.convert['start']); } if ('end' in props.item.data) { changed = changed || (props.end != props.item.data.end.valueOf()); - change.end = props.item.data.end; + item.end = util.convert(props.item.data.end, me.itemsData.convert['end']); } - // only add changes when start or end is actually changed + // only apply changes when start or end is actually changed if (changed) { - changes.push(change); + me.options.onMove(item, function (item) { + if (item) { + // apply changes + changes.push(item); + } + else { + // restore original values + if ('start' in props) props.item.data.start = props.start; + if ('end' in props) props.item.data.end = props.end; + me.requestReflow(); + } + }); } }); this.touchParams.itemProps = null; diff --git a/src/timeline/component/RootPanel.js b/src/timeline/component/RootPanel.js index e4c6166d..708548f3 100644 --- a/src/timeline/component/RootPanel.js +++ b/src/timeline/component/RootPanel.js @@ -17,7 +17,7 @@ function RootPanel(container, options) { // create listeners for all interesting events, these events will be emitted // via the controller var events = [ - 'touch', 'pinch', 'tap', 'hold', + 'touch', 'pinch', 'tap', 'doubletap', 'hold', 'dragstart', 'drag', 'dragend', 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is for Firefox ]; diff --git a/src/timeline/component/item/ItemRange.js b/src/timeline/component/item/ItemRange.js index 76cbf3df..4c279515 100644 --- a/src/timeline/component/item/ItemRange.js +++ b/src/timeline/component/item/ItemRange.js @@ -72,7 +72,7 @@ ItemRange.prototype.repaint = function repaint() { this._repaintDragRight(); // update class - var className = (this.data.className? ' ' + this.data.className : '') + + var className = (this.data.className ? (' ' + this.data.className) : '') + (this.selected ? ' selected' : ''); if (this.className != className) { this.className = className;