You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by jw...@apache.org on 2017/01/21 02:42:09 UTC

nifi git commit: NIFI-3301: Provenance UI Cursor Styling - Addressing inconsistent cursor styling and drag behavior in lineage graph.

Repository: nifi
Updated Branches:
  refs/heads/master dc934cbb8 -> 79ca30be4


NIFI-3301: Provenance UI Cursor Styling
- Addressing inconsistent cursor styling and drag behavior in lineage graph.

This closes #1430.

Signed-off-by: James Wing <jv...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/79ca30be
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/79ca30be
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/79ca30be

Branch: refs/heads/master
Commit: 79ca30be4a84cd6c1d218e1a00af7946405b6f30
Parents: dc934cb
Author: Matt Gilman <ma...@gmail.com>
Authored: Fri Jan 20 16:46:12 2017 -0500
Committer: James Wing <jv...@gmail.com>
Committed: Fri Jan 20 18:40:36 2017 -0800

----------------------------------------------------------------------
 .../src/main/webapp/css/provenance.css          |  17 +-
 .../js/nf/provenance/nf-provenance-lineage.js   | 537 +++++++++----------
 2 files changed, 283 insertions(+), 271 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/79ca30be/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/provenance.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/provenance.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/provenance.css
index a1a7ed5..e105348 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/provenance.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/provenance.css
@@ -473,12 +473,19 @@ path.link.selected {
     stroke: #ba554a;
 }
 
+g.event {
+    cursor: pointer;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
 g.event circle.selected {
     fill: #ba554a;
 }
 
 text.event-type {
-    cursor: pointer;
     font-family: Roboto;
     font-size: 11px;
     font-style: normal;
@@ -492,6 +499,14 @@ text.event-type.expand-parents, text.event-type.expand-children {
     font-size: 13px;
 }
 
+g.flowfile {
+    cursor: default;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
 g.flowfile circle.context, g.event circle.context {
     stroke: #004849;
     stroke-width: 1.5px;

http://git-wip-us.apache.org/repos/asf/nifi/blob/79ca30be/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js
index ddada58..d9586e9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js
@@ -553,6 +553,9 @@
                     // hide the context menu if necessary
                     d3.selectAll('circle.context').classed('context', false);
                     $('#provenance-lineage-context-menu').hide().empty();
+
+                    // prevents browser from using text cursor
+                    d3.event.preventDefault();
                 })
                 .on('contextmenu', function () {
                     var contextMenu = $('#provenance-lineage-context-menu');
@@ -672,7 +675,11 @@
 
             // renders flowfile nodes
             var renderFlowFile = function (flowfiles) {
-                flowfiles.classed('flowfile', true);
+                flowfiles
+                    .classed('flowfile', true)
+                    .on('mousedown', function (d) {
+                        d3.event.stopPropagation();
+                    });
 
                 // node
                 flowfiles.append('circle')
@@ -682,16 +689,6 @@
                         'stroke': '#000',
                         'stroke-width': 1.0
                     })
-                    .on('mousedown', function (d) {
-                        // empty context menu if necessary
-                        $('#provenance-lineage-context-menu').hide().empty();
-
-                        // prevents the drag event when something other than the
-                        // left button is clicked
-                        if (d3.event.button !== 0) {
-                            d3.event.stopPropagation();
-                        }
-                    }, true)
                     .on('mouseover', function (d) {
                         links.filter(function (linkDatum) {
                             return d.id === linkDatum.flowFileUuid;
@@ -725,16 +722,6 @@
                             return 'translate(0,15)';
                         }
                     })
-                    .on('mousedown', function (d) {
-                        // empty context menu if necessary
-                        $('#provenance-lineage-context-menu').hide().empty();
-
-                        // prevents the drag event when something other than the
-                        // left button is clicked
-                        if (d3.event.button !== 0) {
-                            d3.event.stopPropagation();
-                        }
-                    }, true)
                     .on('mouseover', function (d) {
                         links.filter(function (linkDatum) {
                             return d.id === linkDatum.flowFileUuid;
@@ -753,287 +740,297 @@
                             });
                     })
                     .text(function (d) {
-                        return '\ue808'
+                        return '\ue808';
                     });
             };
 
-            // renders event nodes
-            var renderEvent = function (events, provenanceTableCtrl) {
-                events
-                    .classed('event', true)
-                    .append('circle')
-                    .classed('selected', function (d) {
-                        return d.id === eventId;
-                    })
-                    .attr({
-                        'r': 8,
-                        'fill': '#aabbc3',
-                        'stroke': '#000',
-                        'stroke-width': 1.0,
-                        'id': function (d) {
-                            return 'event-node-' + d.id;
-                        }
-                    })
-                    .on('contextmenu', function (d) {
-                        // select the current node for a visible cue
-                        d3.select(this).classed('context', true);
+            var showContextMenu = function (d, provenanceTableCtrl) {
+                // empty an previous contents - in case they right click on the
+                // node twice without closing the previous context menu
+                $('#provenance-lineage-context-menu').hide().empty();
 
-                        // empty an previous contents - in case they right click on the
-                        // node twice without closing the previous context menu
-                        $('#provenance-lineage-context-menu').hide().empty();
+                var menuItems = [{
+                    'class': 'lineage-view-event',
+                    'text': 'View details',
+                    'click': function () {
+                        provenanceTableCtrl.showEventDetails(d.id, clusterNodeId);
+                    }
+                }];
+
+                // if this is a spawn event show appropriate actions
+                if (d.eventType === 'SPAWN' || d.eventType === 'CLONE' || d.eventType === 'FORK' || d.eventType === 'JOIN' || d.eventType === 'REPLAY') {
+                    // starts the lineage expansion process
+                    var expandLineage = function (lineageRequest) {
+                        var lineageProgress = $('#lineage-percent-complete');
+
+                        // add support to cancel outstanding requests - when the button is pressed we
+                        // could be in one of two stages, 1) waiting to GET the status or 2)
+                        // in the process of GETting the status. Handle both cases by cancelling
+                        // the setTimeout (1) and by setting a flag to indicate that a request has
+                        // been request so we can ignore the results (2).
+
+                        var cancelled = false;
+                        var lineage = null;
+                        var lineageTimer = null;
+
+                        // update the progress bar value
+                        provenanceTableCtrl.updateProgress(lineageProgress, 0);
+
+                        // show the 'searching...' dialog
+                        $('#lineage-query-dialog').modal('setButtonModel', [{
+                            buttonText: 'Cancel',
+                            color: {
+                                base: '#E3E8EB',
+                                hover: '#C7D2D7',
+                                text: '#004849'
+                            },
+                            handler: {
+                                click: function () {
+                                    cancelled = true;
+
+                                    // we are waiting for the next poll attempt
+                                    if (lineageTimer !== null) {
+                                        // cancel it
+                                        clearTimeout(lineageTimer);
+
+                                        // cancel the provenance
+                                        closeDialog();
+                                    }
+                                }
+                            }
+                        }]).modal('show');
 
-                        var menuItems = [{
-                            'class': 'lineage-view-event',
-                            'text': 'View details',
-                            'click': function () {
-                                provenanceTableCtrl.showEventDetails(d.id, clusterNodeId);
+
+                        // closes the searching dialog and cancels the query on the server
+                        var closeDialog = function () {
+                            // cancel the provenance results since we've successfully processed the results
+                            if (common.isDefinedAndNotNull(lineage)) {
+                                cancelLineage(lineage);
                             }
-                        }];
-
-                        // if this is a spawn event show appropriate actions
-                        if (d.eventType === 'SPAWN' || d.eventType === 'CLONE' || d.eventType === 'FORK' || d.eventType === 'JOIN' || d.eventType === 'REPLAY') {
-                            // starts the lineage expansion process
-                            var expandLineage = function (lineageRequest) {
-                                var lineageProgress = $('#lineage-percent-complete');
-
-                                // add support to cancel outstanding requests - when the button is pressed we
-                                // could be in one of two stages, 1) waiting to GET the status or 2)
-                                // in the process of GETting the status. Handle both cases by cancelling
-                                // the setTimeout (1) and by setting a flag to indicate that a request has
-                                // been request so we can ignore the results (2).
-
-                                var cancelled = false;
-                                var lineage = null;
-                                var lineageTimer = null;
-
-                                // update the progress bar value
-                                provenanceTableCtrl.updateProgress(lineageProgress, 0);
-
-                                // show the 'searching...' dialog
-                                $('#lineage-query-dialog').modal('setButtonModel', [{
-                                    buttonText: 'Cancel',
-                                    color: {
-                                        base: '#E3E8EB',
-                                        hover: '#C7D2D7',
-                                        text: '#004849'
-                                    },
-                                    handler: {
-                                        click: function () {
-                                            cancelled = true;
-
-                                            // we are waiting for the next poll attempt
-                                            if (lineageTimer !== null) {
-                                                // cancel it
-                                                clearTimeout(lineageTimer);
-
-                                                // cancel the provenance
-                                                closeDialog();
-                                            }
-                                        }
-                                    }
-                                }]).modal('show');
 
+                            // close the dialog
+                            $('#lineage-query-dialog').modal('hide');
+                        };
 
-                                // closes the searching dialog and cancels the query on the server
-                                var closeDialog = function () {
-                                    // cancel the provenance results since we've successfully processed the results
-                                    if (common.isDefinedAndNotNull(lineage)) {
-                                        cancelLineage(lineage);
-                                    }
+                        // polls for the event lineage
+                        var pollLineage = function () {
+                            getLineage(lineage).done(function (response) {
+                                lineage = response.lineage;
 
-                                    // close the dialog
-                                    $('#lineage-query-dialog').modal('hide');
-                                };
+                                // process the lineage
+                                processLineage();
+                            }).fail(closeDialog);
+                        };
 
-                                // polls for the event lineage
-                                var pollLineage = function () {
-                                    getLineage(lineage).done(function (response) {
-                                        lineage = response.lineage;
+                        // processes the event lineage
+                        var processLineage = function () {
+                            // if the request was cancelled just ignore the current response
+                            if (cancelled === true) {
+                                closeDialog();
+                                return;
+                            }
 
-                                        // process the lineage
-                                        processLineage();
-                                    }).fail(closeDialog);
-                                };
+                            // close the dialog if the results contain an error
+                            if (!common.isEmpty(lineage.results.errors)) {
+                                var errors = lineage.results.errors;
+                                dialog.showOkDialog({
+                                    headerText: 'Process Lineage',
+                                    dialogContent: common.formatUnorderedList(errors)
+                                });
 
-                                // processes the event lineage
-                                var processLineage = function () {
-                                    // if the request was cancelled just ignore the current response
-                                    if (cancelled === true) {
-                                        closeDialog();
-                                        return;
-                                    }
+                                closeDialog();
+                                return;
+                            }
 
-                                    // close the dialog if the results contain an error
-                                    if (!common.isEmpty(lineage.results.errors)) {
-                                        var errors = lineage.results.errors;
-                                        dialog.showOkDialog({
-                                            headerText: 'Process Lineage',
-                                            dialogContent: common.formatUnorderedList(errors)
-                                        });
+                            // update the precent complete
+                            provenanceTableCtrl.updateProgress(lineageProgress, lineage.percentCompleted);
+
+                            // process the results if they are finished
+                            if (lineage.finished === true) {
+                                var results = lineage.results;
+
+                                // ensure the events haven't aged off
+                                if (results.nodes.length > 0) {
+                                    // update the lineage graph
+                                    renderEventLineage(results);
+                                } else {
+                                    // inform the user that no results were found
+                                    dialog.showOkDialog({
+                                        headerText: 'Lineage Results',
+                                        dialogContent: 'The lineage search has completed successfully but there no results were found. The events may have aged off.'
+                                    });
+                                }
 
-                                        closeDialog();
-                                        return;
-                                    }
+                                // close the searching.. dialog
+                                closeDialog();
+                            } else {
+                                lineageTimer = setTimeout(function () {
+                                    // clear the timer since we've been invoked
+                                    lineageTimer = null;
 
-                                    // update the precent complete
-                                    provenanceTableCtrl.updateProgress(lineageProgress, lineage.percentCompleted);
-
-                                    // process the results if they are finished
-                                    if (lineage.finished === true) {
-                                        var results = lineage.results;
-
-                                        // ensure the events haven't aged off
-                                        if (results.nodes.length > 0) {
-                                            // update the lineage graph
-                                            renderEventLineage(results);
-                                        } else {
-                                            // inform the user that no results were found
-                                            dialog.showOkDialog({
-                                                headerText: 'Lineage Results',
-                                                dialogContent: 'The lineage search has completed successfully but there no results were found. The events may have aged off.'
-                                            });
-                                        }
+                                    // for the lineage
+                                    pollLineage();
+                                }, 2000);
+                            }
+                        };
 
-                                        // close the searching.. dialog
-                                        closeDialog();
-                                    } else {
-                                        lineageTimer = setTimeout(function () {
-                                            // clear the timer since we've been invoked
-                                            lineageTimer = null;
-
-                                            //  for the lineage
-                                            pollLineage();
-                                        }, 2000);
-                                    }
-                                };
+                        // once the query is submitted wait until its finished
+                        submitLineage(lineageRequest).done(function (response) {
+                            lineage = response.lineage;
 
-                                // once the query is submitted wait until its finished
-                                submitLineage(lineageRequest).done(function (response) {
-                                    lineage = response.lineage;
+                            // process the lineage, if its not done computing wait 1 second before checking again
+                            processLineage(1);
+                        }).fail(closeDialog);
+                    };
 
-                                    // process the lineage, if its not done computing wait 1 second before checking again
-                                    processLineage(1);
-                                }).fail(closeDialog);
-                            };
+                    // handles updating the lineage graph
+                    var renderEventLineage = function (lineageResults) {
+                        addLineage(lineageResults.nodes, lineageResults.links, provenanceTableCtrl);
+                    };
 
-                            // handles updating the lineage graph
-                            var renderEventLineage = function (lineageResults) {
-                                addLineage(lineageResults.nodes, lineageResults.links, provenanceTableCtrl);
+                    // collapses the lineage for the specified event in the specified direction
+                    var collapseLineage = function (eventId, provenanceTableCtrl) {
+                        // get the event in question and collapse in the appropriate direction
+                        provenanceTableCtrl.getEventDetails(eventId, clusterNodeId).done(function (response) {
+                            var provenanceEvent = response.provenanceEvent;
+                            var eventUuid = provenanceEvent.flowFileUuid;
+                            var eventUuids = d3.set(provenanceEvent.childUuids);
+
+                            // determines if the specified event should be removable based on if the collapsing is fanning in/out
+                            var allowEventRemoval = function (fanIn, node) {
+                                if (fanIn) {
+                                    return node.id !== eventId;
+                                } else {
+                                    return node.flowFileUuid !== eventUuid && $.inArray(eventUuid, node.parentUuids) === -1;
+                                }
                             };
 
-                            // collapses the lineage for the specified event in the specified direction
-                            var collapseLineage = function (eventId, provenanceTableCtrl) {
-                                // get the event in question and collapse in the appropriate direction
-                                provenanceTableCtrl.getEventDetails(eventId, clusterNodeId).done(function (response) {
-                                    var provenanceEvent = response.provenanceEvent;
-                                    var eventUuid = provenanceEvent.flowFileUuid;
-                                    var eventUuids = d3.set(provenanceEvent.childUuids);
-
-                                    // determines if the specified event should be removable based on if the collapsing is fanning in/out
-                                    var allowEventRemoval = function (fanIn, node) {
-                                        if (fanIn) {
-                                            return node.id !== eventId;
-                                        } else {
-                                            return node.flowFileUuid !== eventUuid && $.inArray(eventUuid, node.parentUuids) === -1;
-                                        }
-                                    };
-
-                                    // determines if the specified link should be removable based on if the collapsing is fanning in/out
-                                    var allowLinkRemoval = function (fanIn, link) {
-                                        if (fanIn) {
-                                            return true;
-                                        } else {
-                                            return link.flowFileUuid !== eventUuid;
-                                        }
-                                    };
-
-                                    // the event is fan in if the flowfile uuid is in the children
-                                    var fanIn = $.inArray(eventUuid, provenanceEvent.childUuids) >= 0;
-
-                                    // collapses the specified uuids
-                                    var collapse = function (uuids) {
-                                        var newUuids = false;
-
-                                        // consider each node for being collapsed
-                                        $.each(nodeLookup.values(), function (_, node) {
-                                            // if this node is in the uuids remove it unless its the original event or is part of this and another lineage
-                                            if (uuids.has(node.flowFileUuid) && allowEventRemoval(fanIn, node)) {
-                                                // remove it from the look lookup
-                                                nodeLookup.remove(node.id);
-
-                                                // include all related outgoing flow file uuids
-                                                $.each(node.outgoing, function (_, outgoing) {
-                                                    if (!uuids.has(outgoing.flowFileUuid)) {
-                                                        uuids.add(outgoing.flowFileUuid);
-                                                        newUuids = true;
-                                                    }
-                                                });
-                                            }
-                                        });
+                            // determines if the specified link should be removable based on if the collapsing is fanning in/out
+                            var allowLinkRemoval = function (fanIn, link) {
+                                if (fanIn) {
+                                    return true;
+                                } else {
+                                    return link.flowFileUuid !== eventUuid;
+                                }
+                            };
 
-                                        // update the link data
-                                        $.each(linkLookup.values(), function (_, link) {
-                                            // if this link is in the uuids remove it
-                                            if (uuids.has(link.flowFileUuid) && allowLinkRemoval(fanIn, link)) {
-                                                // remove it from the link lookup
-                                                linkLookup.remove(link.id);
-
-                                                // add a related uuid that needs to be collapse
-                                                var next = link.target;
-                                                if (!uuids.has(next.flowFileUuid)) {
-                                                    uuids.add(next.flowFileUuid);
-                                                    newUuids = true;
-                                                }
+                            // the event is fan in if the flowfile uuid is in the children
+                            var fanIn = $.inArray(eventUuid, provenanceEvent.childUuids) >= 0;
+
+                            // collapses the specified uuids
+                            var collapse = function (uuids) {
+                                var newUuids = false;
+
+                                // consider each node for being collapsed
+                                $.each(nodeLookup.values(), function (_, node) {
+                                    // if this node is in the uuids remove it unless its the original event or is part of this and another lineage
+                                    if (uuids.has(node.flowFileUuid) && allowEventRemoval(fanIn, node)) {
+                                        // remove it from the look lookup
+                                        nodeLookup.remove(node.id);
+
+                                        // include all related outgoing flow file uuids
+                                        $.each(node.outgoing, function (_, outgoing) {
+                                            if (!uuids.has(outgoing.flowFileUuid)) {
+                                                uuids.add(outgoing.flowFileUuid);
+                                                newUuids = true;
                                             }
                                         });
+                                    }
+                                });
 
-                                        // collapse any related uuids
-                                        if (newUuids) {
-                                            collapse(uuids);
+                                // update the link data
+                                $.each(linkLookup.values(), function (_, link) {
+                                    // if this link is in the uuids remove it
+                                    if (uuids.has(link.flowFileUuid) && allowLinkRemoval(fanIn, link)) {
+                                        // remove it from the link lookup
+                                        linkLookup.remove(link.id);
+
+                                        // add a related uuid that needs to be collapse
+                                        var next = link.target;
+                                        if (!uuids.has(next.flowFileUuid)) {
+                                            uuids.add(next.flowFileUuid);
+                                            newUuids = true;
                                         }
-                                    };
-
-                                    // collapse the specified uuids
-                                    collapse(eventUuids);
-
-                                    // update the layout
-                                    refresh(provenanceTableCtrl);
+                                    }
                                 });
-                            };
 
-                            // add menu items
-                            menuItems.push({
-                                'class': 'lineage-view-parents',
-                                'text': 'Find parents',
-                                'click': function () {
-                                    expandLineage({
-                                        lineageRequestType: 'PARENTS',
-                                        eventId: d.id,
-                                        clusterNodeId: clusterNodeId
-                                    });
-                                }
-                            }, {
-                                'class': 'lineage-view-children',
-                                'text': 'Expand',
-                                'click': function () {
-                                    expandLineage({
-                                        lineageRequestType: 'CHILDREN',
-                                        eventId: d.id,
-                                        clusterNodeId: clusterNodeId
-                                    });
-                                }
-                            }, {
-                                'class': 'lineage-collapse-children',
-                                'text': 'Collapse',
-                                'click': function () {
-                                    // collapse the children lineage
-                                    collapseLineage(d.id, provenanceTableCtrl);
+                                // collapse any related uuids
+                                if (newUuids) {
+                                    collapse(uuids);
                                 }
+                            };
+
+                            // collapse the specified uuids
+                            collapse(eventUuids);
+
+                            // update the layout
+                            refresh(provenanceTableCtrl);
+                        });
+                    };
+
+                    // add menu items
+                    menuItems.push({
+                        'class': 'lineage-view-parents',
+                        'text': 'Find parents',
+                        'click': function () {
+                            expandLineage({
+                                lineageRequestType: 'PARENTS',
+                                eventId: d.id,
+                                clusterNodeId: clusterNodeId
                             });
                         }
+                    }, {
+                        'class': 'lineage-view-children',
+                        'text': 'Expand',
+                        'click': function () {
+                            expandLineage({
+                                lineageRequestType: 'CHILDREN',
+                                eventId: d.id,
+                                clusterNodeId: clusterNodeId
+                            });
+                        }
+                    }, {
+                        'class': 'lineage-collapse-children',
+                        'text': 'Collapse',
+                        'click': function () {
+                            // collapse the children lineage
+                            collapseLineage(d.id, provenanceTableCtrl);
+                        }
+                    });
+                }
+
+                // show the context menu for an event
+                addContextMenuItems(menuItems);
+            };
+
+            // renders event nodes
+            var renderEvent = function (events, provenanceTableCtrl) {
+                events
+                    .on('contextmenu', function (d) {
+                        // select the current node for a visible cue
+                        d3.select('#event-node-' + d.id).classed('context', true);
+
+                        // show the context menu
+                        showContextMenu(d, provenanceTableCtrl);
+                    })
+                    .on('mousedown', function (d) {
+                        d3.event.stopPropagation();
+                    });
 
-                        // show the context menu for an event
-                        addContextMenuItems(menuItems);
+                events
+                    .classed('event', true)
+                    .append('circle')
+                    .classed('selected', function (d) {
+                        return d.id === eventId;
+                    })
+                    .attr({
+                        'r': 8,
+                        'fill': '#aabbc3',
+                        'stroke': '#000',
+                        'stroke-width': 1.0,
+                        'id': function (d) {
+                            return 'event-node-' + d.id;
+                        }
                     });
 
                 events