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

[05/12] nifi git commit: [NIFI-3359] Modularize all of nifi-web-ui except canvas directory - Removing shell.jsp from summary.jsp. - This closes #1428

http://git-wip-us.apache.org/repos/asf/nifi/blob/dc934cbb/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 d20737c..ddada58 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
@@ -15,1383 +15,1416 @@
  * limitations under the License.
  */
 
-/* global nf, d3 */
-
-nf.ng.ProvenanceLineage = function () {
+/* global nf, top, define, module, require, exports */
+
+(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        define(['jquery',
+                'd3',
+                'nf.Common',
+                'nf.Dialog',
+                'nf.ErrorHandler'],
+            function ($, d3, common, dialog, errorHandler) {
+                return (nf.ng.ProvenanceLineage = factory($, d3, common, dialog, errorHandler));
+            });
+    } else if (typeof exports === 'object' && typeof module === 'object') {
+        module.exports = (nf.ng.ProvenanceLineage =
+            factory(require('jquery'),
+                require('d3'),
+                require('nf.Common'),
+                require('nf.Dialog'),
+                require('nf.ErrorHandler')));
+    } else {
+        nf.ng.ProvenanceLineage = factory(root.$,
+            root.d3,
+            root.nf.Common,
+            root.nf.Dialog,
+            root.nf.ErrorHandler);
+    }
+}(this, function ($, d3, common, dialog, errorHandler) {
     'use strict';
 
-    /**
-     * Configuration object used to hold a number of configuration items.
-     */
-    var config = {
-        sliderTickCount: 75,
-        urls: {
-            lineage: '../nifi-api/provenance/lineage'
-        }
-    };
+    var mySelf = function () {
+        'use strict';
 
-    /**
-     * Initializes the lineage query dialog.
-     */
-    var initLineageQueryDialog = function () {
-        // initialize the dialog
-        $('#lineage-query-dialog').modal({
-            scrollableContentStyle: 'scrollable',
-            headerText: 'Computing FlowFile lineage...'
-        });
-    };
+        /**
+         * Configuration object used to hold a number of configuration items.
+         */
+        var config = {
+            sliderTickCount: 75,
+            urls: {
+                lineage: '../nifi-api/provenance/lineage'
+
+            }
+        };
+
+        /**
+         * Initializes the lineage query dialog.
+         */
+        var initLineageQueryDialog = function () {
+            // initialize the dialog
+            $('#lineage-query-dialog').modal({
+                scrollableContentStyle: 'scrollable',
+                headerText: 'Computing FlowFile lineage...'
+            });
+        };
 
-    var downloadSvgFile = function(svgString){
-        var link = document.getElementById("image-download-link");
-        var downloadSupported = typeof link.download != 'undefined';
-        var fileName ='lineage.svg';
+        var downloadSvgFile = function (svgString) {
+            var link = document.getElementById("image-download-link");
+            var downloadSupported = typeof link.download != 'undefined';
+            var fileName = 'lineage.svg';
 
-        if (downloadSupported) {
-            var DOMURL = self.URL || self.webkitURL || self;
-            var svg = new Blob([svgString], {type: "image/svg+xml;charset=utf-8"});
+            if (downloadSupported) {
+                var DOMURL = self.URL || self.webkitURL || self;
+                var svg = new Blob([svgString], {type: "image/svg+xml;charset=utf-8"});
 
-            if (window.navigator.msSaveOrOpenBlob) {
-                window.navigator.msSaveOrOpenBlob(svg, fileName);
+                if (window.navigator.msSaveOrOpenBlob) {
+                    window.navigator.msSaveOrOpenBlob(svg, fileName);
+                } else {
+                    var url = DOMURL.createObjectURL(svg);
+                    link.href = url;
+                    link.download = fileName;
+                    link.click();
+                }
             } else {
-                var url = DOMURL.createObjectURL(svg);
-                link.href = url;
-                link.download = fileName;
-                link.click();
+                window.open('data:image/svg+xml;charset=utf-8,' + encodeURI(svgString));
             }
-        } else {
-            window.open('data:image/svg+xml;charset=utf-8,' + encodeURI(svgString));
-        }
-    };
+        };
 
-    /**
-     * Appends the items to the context menu.
-     *
-     * items = [{class: ..., text: ..., click: function() {...}}, ...]
-     *
-     * @param {array} items
-     */
-    var addContextMenuItems = function (items) {
-        var contextMenu = $('#provenance-lineage-context-menu');
-
-        $.each(items, function (_, item) {
-            if (typeof item.click === 'function') {
-                var menuItem = $('<div class="context-menu-item"></div>').on('click', item.click).on('mouseenter', function () {
-                    $(this).addClass('hover');
-                }).on('mouseleave', function () {
-                    $(this).removeClass('hover');
-                }).appendTo(contextMenu);
-
-                // add the img and the text
-                $('<div class="context-menu-item-img"></div>').addClass(item['class']).appendTo(menuItem);
-                $('<div class="context-menu-item-text"></div>').text(item['text']).appendTo(menuItem);
-                $('<div class="clear"></div>').appendTo(menuItem);
-            }
-        });
-    };
+        /**
+         * Appends the items to the context menu.
+         *
+         * items = [{class: ..., text: ..., click: function() {...}}, ...]
+         *
+         * @param {array} items
+         */
+        var addContextMenuItems = function (items) {
+            var contextMenu = $('#provenance-lineage-context-menu');
+
+            $.each(items, function (_, item) {
+                if (typeof item.click === 'function') {
+                    var menuItem = $('<div class="context-menu-item"></div>').on('click', item.click).on('mouseenter', function () {
+                        $(this).addClass('hover');
+                    }).on('mouseleave', function () {
+                        $(this).removeClass('hover');
+                    }).appendTo(contextMenu);
+
+                    // add the img and the text
+                    $('<div class="context-menu-item-img"></div>').addClass(item['class']).appendTo(menuItem);
+                    $('<div class="context-menu-item-text"></div>').text(item['text']).appendTo(menuItem);
+                    $('<div class="clear"></div>').appendTo(menuItem);
+                }
+            });
+        };
+
+        /**
+         * Submits the specified lineage request.
+         *
+         * @param {type} lineageRequest
+         * @returns {deferred}
+         */
+        var submitLineage = function (lineageRequest) {
+            var lineageEntity = {
+                'lineage': {
+                    'request': lineageRequest
+                }
+            };
+
+            return $.ajax({
+                type: 'POST',
+                url: config.urls.lineage,
+                data: JSON.stringify(lineageEntity),
+                dataType: 'json',
+                contentType: 'application/json'
+            }).fail(errorHandler.handleAjaxError);
+        };
 
-    /**
-     * Submits the specified lineage request.
-     *
-     * @param {type} lineageRequest
-     * @returns {deferred}
-     */
-    var submitLineage = function (lineageRequest) {
-        var lineageEntity = {
-            'lineage': {
-                'request': lineageRequest
+        /**
+         * Gets the specified lineage.
+         *
+         * @param {type} lineage
+         * @returns {deferred}
+         */
+        var getLineage = function (lineage) {
+            var url = lineage.uri;
+            if (common.isDefinedAndNotNull(lineage.request.clusterNodeId)) {
+                url += '?' + $.param({
+                        clusterNodeId: lineage.request.clusterNodeId
+                    });
             }
+
+            return $.ajax({
+                type: 'GET',
+                url: url,
+                dataType: 'json'
+            }).fail(errorHandler.handleAjaxError);
         };
 
-        return $.ajax({
-            type: 'POST',
-            url: config.urls.lineage,
-            data: JSON.stringify(lineageEntity),
-            dataType: 'json',
-            contentType: 'application/json'
-        }).fail(nf.Common.handleAjaxError);
-    };
+        /**
+         * Cancels the specified lineage.
+         *
+         * @param {type} lineage
+         * @returns {deferred}
+         */
+        var cancelLineage = function (lineage) {
+            var url = lineage.uri;
+            if (common.isDefinedAndNotNull(lineage.request.clusterNodeId)) {
+                url += '?' + $.param({
+                        clusterNodeId: lineage.request.clusterNodeId
+                    });
+            }
 
-    /**
-     * Gets the specified lineage.
-     *
-     * @param {type} lineage
-     * @returns {deferred}
-     */
-    var getLineage = function (lineage) {
-        var url = lineage.uri;
-        if (nf.Common.isDefinedAndNotNull(lineage.request.clusterNodeId)) {
-            url += '?' + $.param({
-                    clusterNodeId: lineage.request.clusterNodeId
-                });
-        }
+            return $.ajax({
+                type: 'DELETE',
+                url: url,
+                dataType: 'json'
+            }).fail(errorHandler.handleAjaxError);
+        };
 
-        return $.ajax({
-            type: 'GET',
-            url: url,
-            dataType: 'json'
-        }).fail(nf.Common.handleAjaxError);
-    };
+        var DEFAULT_NODE_SPACING = 100;
+        var DEFAULT_LEVEL_DIFFERENCE = 120;
 
-    /**
-     * Cancels the specified lineage.
-     *
-     * @param {type} lineage
-     * @returns {deferred}
-     */
-    var cancelLineage = function (lineage) {
-        var url = lineage.uri;
-        if (nf.Common.isDefinedAndNotNull(lineage.request.clusterNodeId)) {
-            url += '?' + $.param({
-                    clusterNodeId: lineage.request.clusterNodeId
-                });
-        }
+        /**
+         * Renders the lineage in the specified results.
+         *
+         * @param {object} lineageResults
+         * @param {integer} eventId
+         * @param {string} clusterNodeId    The id of the node in the cluster where this event/flowfile originated
+         */
+        var renderLineage = function (lineageResults, eventId, clusterNodeId, provenanceTableCtrl) {
+            // get the container
+            var lineageContainer = $('#provenance-lineage');
+            var width = lineageContainer.width();
+            var height = lineageContainer.height();
+
+            // record the min/max event time
+            var minMillis;
+            var minTimestamp;
+            var maxMillis;
+
+            // data lookups
+            var nodeLookup = d3.map();
+            var linkLookup = d3.map();
+
+            var locateDescendants = function (nodeIds, descendants, depth) {
+                $.each(nodeIds, function (_, nodeId) {
+                    var node = nodeLookup.get(nodeId);
 
-        return $.ajax({
-            type: 'DELETE',
-            url: url,
-            dataType: 'json'
-        }).fail(nf.Common.handleAjaxError);
-    };
+                    var children = [];
+                    $.each(node.outgoing, function (_, link) {
+                        children.push(link.target.id);
+                        descendants.add(link.target.id);
+                    });
 
-    var DEFAULT_NODE_SPACING = 100;
-    var DEFAULT_LEVEL_DIFFERENCE = 120;
-
-    /**
-     * Renders the lineage in the specified results.
-     *
-     * @param {object} lineageResults
-     * @param {integer} eventId
-     * @param {string} clusterNodeId    The id of the node in the cluster where this event/flowfile originated
-     */
-    var renderLineage = function (lineageResults, eventId, clusterNodeId, provenanceTableCtrl) {
-        // get the container
-        var lineageContainer = $('#provenance-lineage');
-        var width = lineageContainer.width();
-        var height = lineageContainer.height();
-
-        // record the min/max event time
-        var minMillis;
-        var minTimestamp;
-        var maxMillis;
-
-        // data lookups
-        var nodeLookup = d3.map();
-        var linkLookup = d3.map();
-
-        var locateDescendants = function (nodeIds, descendants, depth) {
-            $.each(nodeIds, function (_, nodeId) {
-                var node = nodeLookup.get(nodeId);
-
-                var children = [];
-                $.each(node.outgoing, function (_, link) {
-                    children.push(link.target.id);
-                    descendants.add(link.target.id);
+                    if (common.isUndefined(depth)) {
+                        locateDescendants(children, descendants);
+                    } else if (depth > 1) {
+                        locateDescendants(children, descendants, depth - 1);
+                    }
                 });
+            };
 
-                if (nf.Common.isUndefined(depth)) {
-                    locateDescendants(children, descendants);
-                } else if (depth > 1) {
-                    locateDescendants(children, descendants, depth - 1);
-                }
-            });
-        };
+            var positionNodes = function (nodeIds, depth, parents, levelDifference) {
+                var immediateSet = d3.set(nodeIds);
+                var childSet = d3.set();
+                var descendantSet = d3.set();
 
-        var positionNodes = function (nodeIds, depth, parents, levelDifference) {
-            var immediateSet = d3.set(nodeIds);
-            var childSet = d3.set();
-            var descendantSet = d3.set();
+                // locate children
+                locateDescendants(nodeIds, childSet, 1);
 
-            // locate children
-            locateDescendants(nodeIds, childSet, 1);
+                // locate all descendants (including children)
+                locateDescendants(nodeIds, descendantSet);
 
-            // locate all descendants (including children)
-            locateDescendants(nodeIds, descendantSet);
+                // push off processing a node until its deepest point
+                // by removing any descendants from the immediate nodes.
+                // in this case, a link is panning multiple levels
+                descendantSet.forEach(function (d) {
+                    immediateSet.remove(d);
+                });
 
-            // push off processing a node until its deepest point
-            // by removing any descendants from the immediate nodes.
-            // in this case, a link is panning multiple levels
-            descendantSet.forEach(function (d) {
-                immediateSet.remove(d);
-            });
+                // convert the children to an array to ensure consistent
+                // order when performing index of checks below
+                var children = childSet.values().sort(d3.descending);
 
-            // convert the children to an array to ensure consistent
-            // order when performing index of checks below
-            var children = childSet.values().sort(d3.descending);
+                // convert the immediate to allow for sorting below
+                var immediate = immediateSet.values();
 
-            // convert the immediate to allow for sorting below
-            var immediate = immediateSet.values();
+                // attempt to identify fan in/out cases
+                var nodesWithTwoParents = 0;
+                $.each(immediate, function (_, nodeId) {
+                    var node = nodeLookup.get(nodeId);
 
-            // attempt to identify fan in/out cases
-            var nodesWithTwoParents = 0;
-            $.each(immediate, function (_, nodeId) {
-                var node = nodeLookup.get(nodeId);
+                    // identify fanning cases
+                    if (node.incoming.length > 3) {
+                        levelDifference = DEFAULT_LEVEL_DIFFERENCE;
+                    } else if (node.incoming.length >= 2) {
+                        nodesWithTwoParents++;
+                    }
+                });
 
-                // identify fanning cases
-                if (node.incoming.length > 3) {
+                // increate the level difference if more than two nodes have two or more parents
+                if (nodesWithTwoParents > 2) {
                     levelDifference = DEFAULT_LEVEL_DIFFERENCE;
-                } else if (node.incoming.length >= 2) {
-                    nodesWithTwoParents++;
                 }
-            });
 
-            // increate the level difference if more than two nodes have two or more parents
-            if (nodesWithTwoParents > 2) {
-                levelDifference = DEFAULT_LEVEL_DIFFERENCE;
-            }
-
-            // attempt to sort the nodes to provide an optimum layout
-            if (parents.length === 1) {
-                immediate = immediate.sort(function (one, two) {
-                    var oneNode = nodeLookup.get(one);
-                    var twoNode = nodeLookup.get(two);
-
-                    // try to order by children
-                    if (oneNode.outgoing.length > 0 && twoNode.outgoing.length > 0) {
-                        var oneIndex = children.indexOf(oneNode.outgoing[0].target.id);
-                        var twoIndex = children.indexOf(twoNode.outgoing[0].target.id);
-                        if (oneIndex !== twoIndex) {
-                            return oneIndex - twoIndex;
+                // attempt to sort the nodes to provide an optimum layout
+                if (parents.length === 1) {
+                    immediate = immediate.sort(function (one, two) {
+                        var oneNode = nodeLookup.get(one);
+                        var twoNode = nodeLookup.get(two);
+
+                        // try to order by children
+                        if (oneNode.outgoing.length > 0 && twoNode.outgoing.length > 0) {
+                            var oneIndex = children.indexOf(oneNode.outgoing[0].target.id);
+                            var twoIndex = children.indexOf(twoNode.outgoing[0].target.id);
+                            if (oneIndex !== twoIndex) {
+                                return oneIndex - twoIndex;
+                            }
                         }
-                    }
 
-                    // try to order by parents
-                    if (oneNode.incoming.length > 0 && twoNode.incoming.length > 0) {
-                        var oneIndex = oneNode.incoming[0].source.index;
-                        var twoIndex = twoNode.incoming[0].source.index;
-                        if (oneIndex !== twoIndex) {
-                            return oneIndex - twoIndex;
+                        // try to order by parents
+                        if (oneNode.incoming.length > 0 && twoNode.incoming.length > 0) {
+                            var oneIndex = oneNode.incoming[0].source.index;
+                            var twoIndex = twoNode.incoming[0].source.index;
+                            if (oneIndex !== twoIndex) {
+                                return oneIndex - twoIndex;
+                            }
                         }
-                    }
 
-                    // type of node
-                    if (oneNode.type !== twoNode.type) {
-                        return oneNode.type > twoNode.type ? 1 : -1;
-                    }
-
-                    // type of event
-                    if (oneNode.eventType !== twoNode.eventType) {
-                        return oneNode.eventType > twoNode.eventType ? 1 : -1;
-                    }
+                        // type of node
+                        if (oneNode.type !== twoNode.type) {
+                            return oneNode.type > twoNode.type ? 1 : -1;
+                        }
 
-                    // timestamp
-                    return oneNode.millis - twoNode.millis;
-                });
-            } else if (parents.length > 1) {
-                immediate = immediate.sort(function (one, two) {
-                    var oneNode = nodeLookup.get(one);
-                    var twoNode = nodeLookup.get(two);
-
-                    // try to order by parents
-                    if (oneNode.incoming.length > 0 && twoNode.incoming.length > 0) {
-                        var oneIndex = oneNode.incoming[0].source.index;
-                        var twoIndex = twoNode.incoming[0].source.index;
-                        if (oneIndex !== twoIndex) {
-                            return oneIndex - twoIndex;
+                        // type of event
+                        if (oneNode.eventType !== twoNode.eventType) {
+                            return oneNode.eventType > twoNode.eventType ? 1 : -1;
                         }
-                    }
 
-                    // try to order by children
-                    if (oneNode.outgoing.length > 0 && twoNode.outgoing.length > 0) {
-                        var oneIndex = children.indexOf(oneNode.outgoing[0].target.id);
-                        var twoIndex = children.indexOf(twoNode.outgoing[0].target.id);
-                        if (oneIndex !== twoIndex) {
-                            return oneIndex - twoIndex;
+                        // timestamp
+                        return oneNode.millis - twoNode.millis;
+                    });
+                } else if (parents.length > 1) {
+                    immediate = immediate.sort(function (one, two) {
+                        var oneNode = nodeLookup.get(one);
+                        var twoNode = nodeLookup.get(two);
+
+                        // try to order by parents
+                        if (oneNode.incoming.length > 0 && twoNode.incoming.length > 0) {
+                            var oneIndex = oneNode.incoming[0].source.index;
+                            var twoIndex = twoNode.incoming[0].source.index;
+                            if (oneIndex !== twoIndex) {
+                                return oneIndex - twoIndex;
+                            }
                         }
-                    }
 
-                    // node type
-                    if (oneNode.type !== twoNode.type) {
-                        return oneNode.type > twoNode.type ? 1 : -1;
-                    }
+                        // try to order by children
+                        if (oneNode.outgoing.length > 0 && twoNode.outgoing.length > 0) {
+                            var oneIndex = children.indexOf(oneNode.outgoing[0].target.id);
+                            var twoIndex = children.indexOf(twoNode.outgoing[0].target.id);
+                            if (oneIndex !== twoIndex) {
+                                return oneIndex - twoIndex;
+                            }
+                        }
 
-                    // event type
-                    if (oneNode.eventType !== twoNode.eventType) {
-                        return oneNode.eventType > twoNode.eventType ? 1 : -1;
-                    }
+                        // node type
+                        if (oneNode.type !== twoNode.type) {
+                            return oneNode.type > twoNode.type ? 1 : -1;
+                        }
 
-                    // timestamp
-                    return oneNode.millis - twoNode.millis;
-                });
-            }
+                        // event type
+                        if (oneNode.eventType !== twoNode.eventType) {
+                            return oneNode.eventType > twoNode.eventType ? 1 : -1;
+                        }
 
-            var originX = width / 2;
-            if (parents.length > 0) {
-                originX = d3.mean(parents, function (parentId) {
-                    var parent = nodeLookup.get(parentId);
-                    return parent.x;
-                });
-            }
+                        // timestamp
+                        return oneNode.millis - twoNode.millis;
+                    });
+                }
 
-            var depthWidth = (immediate.length - 1) * DEFAULT_NODE_SPACING;
-            $.each(immediate, function (i, nodeId) {
-                var node = nodeLookup.get(nodeId);
+                var originX = width / 2;
+                if (parents.length > 0) {
+                    originX = d3.mean(parents, function (parentId) {
+                        var parent = nodeLookup.get(parentId);
+                        return parent.x;
+                    });
+                }
 
-                // set the y position based on the depth
-                node.y = levelDifference + depth - 25;
+                var depthWidth = (immediate.length - 1) * DEFAULT_NODE_SPACING;
+                $.each(immediate, function (i, nodeId) {
+                    var node = nodeLookup.get(nodeId);
 
-                // ensure the children won't position on top of one another
-                // based on the number of parent nodes
-                if (immediate.length <= parents.length) {
-                    if (node.incoming.length === 1) {
-                        var parent = node.incoming[0].source;
-                        if (parent.outgoing.length === 1) {
-                            node.x = parent.x;
+                    // set the y position based on the depth
+                    node.y = levelDifference + depth - 25;
+
+                    // ensure the children won't position on top of one another
+                    // based on the number of parent nodes
+                    if (immediate.length <= parents.length) {
+                        if (node.incoming.length === 1) {
+                            var parent = node.incoming[0].source;
+                            if (parent.outgoing.length === 1) {
+                                node.x = parent.x;
+                                return;
+                            }
+                        } else if (node.incoming.length > 1) {
+                            var nodesOnPreviousLevel = $.grep(node.incoming, function (link) {
+                                return (node.y - link.source.y) <= DEFAULT_LEVEL_DIFFERENCE;
+                            });
+                            node.x = d3.mean(nodesOnPreviousLevel, function (link) {
+                                return link.source.x;
+                            });
                             return;
                         }
-                    } else if (node.incoming.length > 1) {
-                        var nodesOnPreviousLevel = $.grep(node.incoming, function (link) {
-                            return (node.y - link.source.y) <= DEFAULT_LEVEL_DIFFERENCE;
-                        });
-                        node.x = d3.mean(nodesOnPreviousLevel, function (link) {
-                            return link.source.x;
-                        });
-                        return;
                     }
-                }
 
-                // evenly space the nodes under the origin
-                node.x = (i * DEFAULT_NODE_SPACING) + originX - (depthWidth / 2);
-            });
+                    // evenly space the nodes under the origin
+                    node.x = (i * DEFAULT_NODE_SPACING) + originX - (depthWidth / 2);
+                });
 
-            // sort the immediate nodes after positioning by the x coordinate
-            // so they can be shifted accordingly if necessary
-            var sortedImmediate = immediate.slice().sort(function (one, two) {
-                var nodeOne = nodeLookup.get(one);
-                var nodeTwo = nodeLookup.get(two);
-                return nodeOne.x - nodeTwo.x;
-            });
+                // sort the immediate nodes after positioning by the x coordinate
+                // so they can be shifted accordingly if necessary
+                var sortedImmediate = immediate.slice().sort(function (one, two) {
+                    var nodeOne = nodeLookup.get(one);
+                    var nodeTwo = nodeLookup.get(two);
+                    return nodeOne.x - nodeTwo.x;
+                });
 
-            // adjust the x positioning if necessary to avoid positioning on top
-            // of one another, only need to consider the x coordinate since the
-            // y coordinate will be the same for each node on this row
-            for (var i = 0; i < sortedImmediate.length - 1; i++) {
-                var first = nodeLookup.get(sortedImmediate[i]);
-                var second = nodeLookup.get(sortedImmediate[i + 1]);
-                var difference = second.x - first.x;
+                // adjust the x positioning if necessary to avoid positioning on top
+                // of one another, only need to consider the x coordinate since the
+                // y coordinate will be the same for each node on this row
+                for (var i = 0; i < sortedImmediate.length - 1; i++) {
+                    var first = nodeLookup.get(sortedImmediate[i]);
+                    var second = nodeLookup.get(sortedImmediate[i + 1]);
+                    var difference = second.x - first.x;
 
-                if (difference < DEFAULT_NODE_SPACING) {
-                    second.x += (DEFAULT_NODE_SPACING - difference);
+                    if (difference < DEFAULT_NODE_SPACING) {
+                        second.x += (DEFAULT_NODE_SPACING - difference);
+                    }
                 }
-            }
 
-            // if there are children to position
-            if (children.length > 0) {
-                var childLevelDifference = DEFAULT_LEVEL_DIFFERENCE / 3;
+                // if there are children to position
+                if (children.length > 0) {
+                    var childLevelDifference = DEFAULT_LEVEL_DIFFERENCE / 3;
 
-                // resort the immediate values after each node has been positioned
-                immediate = immediate.sort(function (one, two) {
-                    var oneNode = nodeLookup.get(one);
-                    var twoNode = nodeLookup.get(two);
-                    return oneNode.x - twoNode.x;
-                });
+                    // resort the immediate values after each node has been positioned
+                    immediate = immediate.sort(function (one, two) {
+                        var oneNode = nodeLookup.get(one);
+                        var twoNode = nodeLookup.get(two);
+                        return oneNode.x - twoNode.x;
+                    });
 
-                // mark each nodes index so subsequent recursive calls can position children accordingly
-                var nodesWithTwoChildren = 0;
-                $.each(immediate, function (i, nodeId) {
-                    var node = nodeLookup.get(nodeId);
-                    node.index = i;
+                    // mark each nodes index so subsequent recursive calls can position children accordingly
+                    var nodesWithTwoChildren = 0;
+                    $.each(immediate, function (i, nodeId) {
+                        var node = nodeLookup.get(nodeId);
+                        node.index = i;
+
+                        // precompute the next level difference since we have easy access to going here
+                        if (node.outgoing.length > 3) {
+                            childLevelDifference = DEFAULT_LEVEL_DIFFERENCE;
+                        } else if (node.outgoing.length >= 2) {
+                            nodesWithTwoChildren++;
+                        }
+                    });
 
-                    // precompute the next level difference since we have easy access to going here
-                    if (node.outgoing.length > 3) {
+                    // if there are at least two immediate nodes with two or more children, increase the level difference
+                    if (nodesWithTwoChildren > 2) {
                         childLevelDifference = DEFAULT_LEVEL_DIFFERENCE;
-                    } else if (node.outgoing.length >= 2) {
-                        nodesWithTwoChildren++;
                     }
-                });
 
-                // if there are at least two immediate nodes with two or more children, increase the level difference
-                if (nodesWithTwoChildren > 2) {
-                    childLevelDifference = DEFAULT_LEVEL_DIFFERENCE;
+                    // position the children
+                    positionNodes(children, levelDifference + depth, immediate, childLevelDifference);
                 }
+            };
 
-                // position the children
-                positionNodes(children, levelDifference + depth, immediate, childLevelDifference);
-            }
-        };
+            var addLineage = function (nodes, links, provenanceTableCtrl) {
+                // add the new nodes
+                $.each(nodes, function (_, node) {
+                    if (nodeLookup.has(node.id)) {
+                        return;
+                    }
 
-        var addLineage = function (nodes, links, provenanceTableCtrl) {
-            // add the new nodes
-            $.each(nodes, function (_, node) {
-                if (nodeLookup.has(node.id)) {
-                    return;
-                }
+                    // add values to the node to support rendering
+                    $.extend(node, {
+                        x: 0,
+                        y: 0,
+                        visible: true
+                    });
 
-                // add values to the node to support rendering
-                $.extend(node, {
-                    x: 0,
-                    y: 0,
-                    visible: true
+                    // store the node in a lookup
+                    nodeLookup.set(node.id, node);
                 });
 
-                // store the node in a lookup
-                nodeLookup.set(node.id, node);
-            });
-
-            // add the new links
-            $.each(links, function (_, link) {
-                // create the link object
-                var linkObj = {
-                    id: link.sourceId + '-' + link.targetId,
-                    source: nodeLookup.get(link.sourceId),
-                    target: nodeLookup.get(link.targetId),
-                    flowFileUuid: link.flowFileUuid,
-                    millis: link.millis,
-                    visible: true
-                };
-
-                linkLookup.set(linkObj.id, linkObj);
-            });
-
-            refresh(provenanceTableCtrl);
-        };
+                // add the new links
+                $.each(links, function (_, link) {
+                    // create the link object
+                    var linkObj = {
+                        id: link.sourceId + '-' + link.targetId,
+                        source: nodeLookup.get(link.sourceId),
+                        target: nodeLookup.get(link.targetId),
+                        flowFileUuid: link.flowFileUuid,
+                        millis: link.millis,
+                        visible: true
+                    };
+
+                    linkLookup.set(linkObj.id, linkObj);
+                });
 
-        var refresh = function (provenanceTableCtrl) {
-            // consider all nodes as starting points
-            var startNodes = d3.set(nodeLookup.keys());
+                refresh(provenanceTableCtrl);
+            };
 
-            // go through the nodes to reset their outgoing links
-            nodeLookup.forEach(function (id, node) {
-                node.outgoing = [];
-                node.incoming = [];
+            var refresh = function (provenanceTableCtrl) {
+                // consider all nodes as starting points
+                var startNodes = d3.set(nodeLookup.keys());
 
-                // ensure this event has an event time
-                if (nf.Common.isUndefined(minMillis) || minMillis > node.millis) {
-                    minMillis = node.millis;
-                    minTimestamp = node.timestamp;
-                }
-                if (nf.Common.isUndefined(maxMillis) || maxMillis < node.millis) {
-                    maxMillis = node.millis;
-                }
-            });
+                // go through the nodes to reset their outgoing links
+                nodeLookup.forEach(function (id, node) {
+                    node.outgoing = [];
+                    node.incoming = [];
 
-            // go through the links in order to compute the new layout
-            linkLookup.forEach(function (id, link) {
-                // updating the nodes connections
-                link.source.outgoing.push(link);
-                link.target.incoming.push(link);
+                    // ensure this event has an event time
+                    if (common.isUndefined(minMillis) || minMillis > node.millis) {
+                        minMillis = node.millis;
+                        minTimestamp = node.timestamp;
+                    }
+                    if (common.isUndefined(maxMillis) || maxMillis < node.millis) {
+                        maxMillis = node.millis;
+                    }
+                });
 
-                // remove the target from being a potential starting node
-                startNodes.remove(link.target.id);
-            });
+                // go through the links in order to compute the new layout
+                linkLookup.forEach(function (id, link) {
+                    // updating the nodes connections
+                    link.source.outgoing.push(link);
+                    link.target.incoming.push(link);
 
-            // position the nodes
-            positionNodes(startNodes.values(), 1, [], 50);
+                    // remove the target from being a potential starting node
+                    startNodes.remove(link.target.id);
+                });
 
-            // update the slider min/max/step values
-            var step = (maxMillis - minMillis) / config.sliderTickCount;
-            slider.slider('option', 'min', minMillis).slider('option', 'max', maxMillis).slider('option', 'step', step).slider('value', maxMillis);
+                // position the nodes
+                positionNodes(startNodes.values(), 1, [], 50);
 
-            // populate the event timeline
-            $('#event-time').text(formatEventTime(maxMillis, provenanceTableCtrl));
+                // update the slider min/max/step values
+                var step = (maxMillis - minMillis) / config.sliderTickCount;
+                slider.slider('option', 'min', minMillis).slider('option', 'max', maxMillis).slider('option', 'step', step).slider('value', maxMillis);
 
-            // update the layout
-            update(provenanceTableCtrl);
-        };
+                // populate the event timeline
+                $('#event-time').text(formatEventTime(maxMillis, provenanceTableCtrl));
 
-        // formats the specified millis
-        var formatEventTime = function (millis, provenanceTableCtrl) {
-            // get the current user time to properly convert the server time
-            var now = new Date();
+                // update the layout
+                update(provenanceTableCtrl);
+            };
 
-            // conver the user offset to millis
-            var userTimeOffset = now.getTimezoneOffset() * 60 * 1000;
+            // formats the specified millis
+            var formatEventTime = function (millis, provenanceTableCtrl) {
+                // get the current user time to properly convert the server time
+                var now = new Date();
 
-            // create the proper date by adjusting by the offsets
-            var date = new Date(millis + userTimeOffset + provenanceTableCtrl.serverTimeOffset);
-            return nf.Common.formatDateTime(date);
-        };
+                // conver the user offset to millis
+                var userTimeOffset = now.getTimezoneOffset() * 60 * 1000;
 
-        // handle context menu clicks...
-        $('#provenance-lineage-context-menu').on('click', function () {
-            $(this).hide().empty();
-        });
-
-        // handle zoom behavior
-        var lineageZoom = d3.behavior.zoom()
-            .scaleExtent([0.2, 8])
-            .on('zoom', function () {
-                d3.select('g.lineage').attr('transform', function () {
-                    return 'translate(' + d3.event.translate + ') scale(' + d3.event.scale + ')';
-                });
-            });
-
-        // build the svg img
-        var svg = d3.select('#provenance-lineage-container').append('svg:svg')
-            .attr('width', '100%')
-            .attr('height', '100%')
-            .call(lineageZoom)
-            .on('dblclick.zoom', null)
-            .on('mousedown', function (d) {
-                // hide the context menu if necessary
-                d3.selectAll('circle.context').classed('context', false);
-                $('#provenance-lineage-context-menu').hide().empty();
-            })
-            .on('contextmenu', function () {
-                var contextMenu = $('#provenance-lineage-context-menu');
-
-                // if there is something to show in the context menu
-                if (!contextMenu.is(':empty')) {
-                    var position = d3.mouse(this);
-
-                    // show the context menu
-                    contextMenu.css({
-                        'left': position[0] + 'px',
-                        'top': position[1] + 'px'
-                    }).show();
-                }
+                // create the proper date by adjusting by the offsets
+                var date = new Date(millis + userTimeOffset + provenanceTableCtrl.serverTimeOffset);
+                return common.formatDateTime(date);
+            };
 
-                // prevent the native default context menu
-                d3.event.preventDefault();
+            // handle context menu clicks...
+            $('#provenance-lineage-context-menu').on('click', function () {
+                $(this).hide().empty();
             });
 
-        svg.append('rect')
-            .attr({
-                'width': '100%',
-                'height': '100%',
-                'fill': '#f9fafb'
-            });
+            // handle zoom behavior
+            var lineageZoom = d3.behavior.zoom()
+                .scaleExtent([0.2, 8])
+                .on('zoom', function () {
+                    d3.select('g.lineage').attr('transform', function () {
+                        return 'translate(' + d3.event.translate + ') scale(' + d3.event.scale + ')';
+                    });
+                });
 
-        svg.append('defs').selectAll('marker')
-            .data(['FLOWFILE', 'FLOWFILE-SELECTED', 'EVENT', 'EVENT-SELECTED'])
-            .enter().append('marker')
-            .attr({
-                'id': function (d) {
-                    return d;
-                },
-                'viewBox': '0 -3 6 6',
-                'refX': function (d) {
-                    if (d.indexOf('FLOWFILE') >= 0) {
-                        return 16;
-                    } else {
-                        return 11;
-                    }
-                },
-                'refY': 0,
-                'markerWidth': 6,
-                'markerHeight': 6,
-                'orient': 'auto',
-                'fill': function (d) {
-                    if (d.indexOf('SELECTED') >= 0) {
-                        return '#ba554a';
-                    } else {
-                        return '#000000';
+            // build the svg img
+            var svg = d3.select('#provenance-lineage-container').append('svg:svg')
+                .attr('width', '100%')
+                .attr('height', '100%')
+                .call(lineageZoom)
+                .on('dblclick.zoom', null)
+                .on('mousedown', function (d) {
+                    // hide the context menu if necessary
+                    d3.selectAll('circle.context').classed('context', false);
+                    $('#provenance-lineage-context-menu').hide().empty();
+                })
+                .on('contextmenu', function () {
+                    var contextMenu = $('#provenance-lineage-context-menu');
+
+                    // if there is something to show in the context menu
+                    if (!contextMenu.is(':empty')) {
+                        var position = d3.mouse(this);
+
+                        // show the context menu
+                        contextMenu.css({
+                            'left': position[0] + 'px',
+                            'top': position[1] + 'px'
+                        }).show();
                     }
-                }
-            })
-            .append('path')
-            .attr('d', 'M0,-3 L6,0 L0,3');
-
-        // group everything together
-        var lineageContainer = svg.append('g')
-            .attr({
-                'transform': 'translate(0, 0) scale(1)',
-                'pointer-events': 'all',
-                'class': 'lineage'
-            });
-
-        // select the nodes and links
-        var nodes = lineageContainer.selectAll('g.node');
-        var links = lineageContainer.selectAll('path.link');
-
-        var previousMillis = maxMillis;
-        var slide = function (event, ui) {
-            if (previousMillis > ui.value) {
-                // the slider is descending
 
-                // determine the nodes to hide
-                var nodesToHide = nodes.filter(function (d) {
-                    return d.millis > ui.value && d.millis <= previousMillis;
+                    // prevent the native default context menu
+                    d3.event.preventDefault();
                 });
-                var linksToHide = links.filter(function (d) {
-                    return d.millis > ui.value && d.millis <= previousMillis;
+
+            svg.append('rect')
+                .attr({
+                    'width': '100%',
+                    'height': '100%',
+                    'fill': '#f9fafb'
                 });
 
-                // hide applicable nodes and lines
-                linksToHide.transition().duration(400).style('opacity', 0);
-                nodesToHide.transition().delay(200).duration(400).style('opacity', 0);
-            } else {
-                // the slider is ascending
+            svg.append('defs').selectAll('marker')
+                .data(['FLOWFILE', 'FLOWFILE-SELECTED', 'EVENT', 'EVENT-SELECTED'])
+                .enter().append('marker')
+                .attr({
+                    'id': function (d) {
+                        return d;
+                    },
+                    'viewBox': '0 -3 6 6',
+                    'refX': function (d) {
+                        if (d.indexOf('FLOWFILE') >= 0) {
+                            return 16;
+                        } else {
+                            return 11;
+                        }
+                    },
+                    'refY': 0,
+                    'markerWidth': 6,
+                    'markerHeight': 6,
+                    'orient': 'auto',
+                    'fill': function (d) {
+                        if (d.indexOf('SELECTED') >= 0) {
+                            return '#ba554a';
+                        } else {
+                            return '#000000';
+                        }
+                    }
+                })
+                .append('path')
+                .attr('d', 'M0,-3 L6,0 L0,3');
 
-                // determine the nodes to show
-                var nodesToShow = nodes.filter(function (d) {
-                    return d.millis <= ui.value && d.millis > previousMillis;
-                });
-                var linksToShow = links.filter(function (d) {
-                    return d.millis <= ui.value && d.millis > previousMillis;
+            // group everything together
+            var lineageContainer = svg.append('g')
+                .attr({
+                    'transform': 'translate(0, 0) scale(1)',
+                    'pointer-events': 'all',
+                    'class': 'lineage'
                 });
 
-                // show applicable nodes and lines
-                nodesToShow.transition().duration(400).style('opacity', 1);
-                linksToShow.transition().delay(200).duration(400).style('opacity', 1);
-            }
+            // select the nodes and links
+            var nodes = lineageContainer.selectAll('g.node');
+            var links = lineageContainer.selectAll('path.link');
 
-            // update the event time
-            $('#event-time').text(formatEventTime(ui.value, provenanceTableCtrl));
+            var previousMillis = maxMillis;
+            var slide = function (event, ui) {
+                if (previousMillis > ui.value) {
+                    // the slider is descending
 
-            // update the previous value
-            previousMillis = ui.value;
-        };
+                    // determine the nodes to hide
+                    var nodesToHide = nodes.filter(function (d) {
+                        return d.millis > ui.value && d.millis <= previousMillis;
+                    });
+                    var linksToHide = links.filter(function (d) {
+                        return d.millis > ui.value && d.millis <= previousMillis;
+                    });
 
-        // set up a slider for the showing the timeline of events
-        var slider = $('#provenance-lineage-slider').slider({
-            min: minMillis,
-            max: maxMillis,
-            step: (maxMillis - minMillis) / config.sliderTickCount,
-            value: maxMillis,
-            change: slide,
-            slide: slide
-        });
-
-        // renders flowfile nodes
-        var renderFlowFile = function (flowfiles) {
-            flowfiles.classed('flowfile', true);
-
-            // node
-            flowfiles.append('circle')
-                .attr({
-                    'r': 16,
-                    'fill': '#fff',
-                    'stroke': '#000',
-                    'stroke-width': 1.0
-                })
-                .on('mousedown', function (d) {
-                    // empty context menu if necessary
-                    $('#provenance-lineage-context-menu').hide().empty();
+                    // hide applicable nodes and lines
+                    linksToHide.transition().duration(400).style('opacity', 0);
+                    nodesToHide.transition().delay(200).duration(400).style('opacity', 0);
+                } else {
+                    // the slider is ascending
 
-                    // 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;
-                    })
-                        .classed('selected', true)
-                        .attr('marker-end', function (d) {
-                            return 'url(#' + d.target.type + '-SELECTED)';
-                        });
-                })
-                .on('mouseout', function (d) {
-                    links.filter(function (linkDatum) {
-                        return d.id === linkDatum.flowFileUuid;
-                    }).classed('selected', false)
-                        .attr('marker-end', function (d) {
-                            return 'url(#' + d.target.type + ')';
-                        });
-                });
+                    // determine the nodes to show
+                    var nodesToShow = nodes.filter(function (d) {
+                        return d.millis <= ui.value && d.millis > previousMillis;
+                    });
+                    var linksToShow = links.filter(function (d) {
+                        return d.millis <= ui.value && d.millis > previousMillis;
+                    });
 
-            var icon = flowfiles.append('g')
-                .attr({
-                    'class': 'flowfile-icon',
-                    'transform': function (d) {
-                        return 'translate(-9,-9)';
-                    }
-                }).append('text')
-                .attr({
-                    'font-family': 'flowfont',
-                    'font-size': '18px',
-                    'fill': '#ad9897',
-                    'transform': function (d) {
-                        return 'translate(0,15)';
-                    }
-                })
-                .on('mousedown', function (d) {
-                    // empty context menu if necessary
-                    $('#provenance-lineage-context-menu').hide().empty();
+                    // show applicable nodes and lines
+                    nodesToShow.transition().duration(400).style('opacity', 1);
+                    linksToShow.transition().delay(200).duration(400).style('opacity', 1);
+                }
 
-                    // 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;
-                    })
-                        .classed('selected', true)
-                        .attr('marker-end', function (d) {
-                            return 'url(#' + d.target.type + '-SELECTED)';
-                        });
-                })
-                .on('mouseout', function (d) {
-                    links.filter(function (linkDatum) {
-                        return d.id === linkDatum.flowFileUuid;
-                    }).classed('selected', false)
-                        .attr('marker-end', function (d) {
-                            return 'url(#' + d.target.type + ')';
-                        });
-                })
-                .text(function(d) { return '\ue808' });
-        };
+                // update the event time
+                $('#event-time').text(formatEventTime(ui.value, provenanceTableCtrl));
 
-        // 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);
+                // update the previous value
+                previousMillis = ui.value;
+            };
 
-                    // 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();
+            // set up a slider for the showing the timeline of events
+            var slider = $('#provenance-lineage-slider').slider({
+                min: minMillis,
+                max: maxMillis,
+                step: (maxMillis - minMillis) / config.sliderTickCount,
+                value: maxMillis,
+                change: slide,
+                slide: slide
+            });
 
-                    var menuItems = [{
-                        'class': 'lineage-view-event',
-                        'text': 'View details',
-                        'click': function () {
-                            provenanceTableCtrl.showEventDetails(d.id, clusterNodeId);
+            // renders flowfile nodes
+            var renderFlowFile = function (flowfiles) {
+                flowfiles.classed('flowfile', true);
+
+                // node
+                flowfiles.append('circle')
+                    .attr({
+                        'r': 16,
+                        'fill': '#fff',
+                        '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();
                         }
-                    }];
-
-                    // 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');
+                    }, true)
+                    .on('mouseover', function (d) {
+                        links.filter(function (linkDatum) {
+                            return d.id === linkDatum.flowFileUuid;
+                        })
+                            .classed('selected', true)
+                            .attr('marker-end', function (d) {
+                                return 'url(#' + d.target.type + '-SELECTED)';
+                            });
+                    })
+                    .on('mouseout', function (d) {
+                        links.filter(function (linkDatum) {
+                            return d.id === linkDatum.flowFileUuid;
+                        }).classed('selected', false)
+                            .attr('marker-end', function (d) {
+                                return 'url(#' + d.target.type + ')';
+                            });
+                    });
 
+                var icon = flowfiles.append('g')
+                    .attr({
+                        'class': 'flowfile-icon',
+                        'transform': function (d) {
+                            return 'translate(-9,-9)';
+                        }
+                    }).append('text')
+                    .attr({
+                        'font-family': 'flowfont',
+                        'font-size': '18px',
+                        'fill': '#ad9897',
+                        'transform': function (d) {
+                            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;
+                        })
+                            .classed('selected', true)
+                            .attr('marker-end', function (d) {
+                                return 'url(#' + d.target.type + '-SELECTED)';
+                            });
+                    })
+                    .on('mouseout', function (d) {
+                        links.filter(function (linkDatum) {
+                            return d.id === linkDatum.flowFileUuid;
+                        }).classed('selected', false)
+                            .attr('marker-end', function (d) {
+                                return 'url(#' + d.target.type + ')';
+                            });
+                    })
+                    .text(function (d) {
+                        return '\ue808'
+                    });
+            };
 
-                            // 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 (nf.Common.isDefinedAndNotNull(lineage)) {
-                                    cancelLineage(lineage);
-                                }
+            // 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);
 
-                                // close the dialog
-                                $('#lineage-query-dialog').modal('hide');
-                            };
+                        // 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();
 
-                            // polls for the event lineage
-                            var pollLineage = function () {
-                                getLineage(lineage).done(function (response) {
-                                    lineage = response.lineage;
+                        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');
 
-                                    // process the lineage
-                                    processLineage();
-                                }).fail(closeDialog);
-                            };
 
-                            // processes the event lineage
-                            var processLineage = function () {
-                                // if the request was cancelled just ignore the current response
-                                if (cancelled === true) {
-                                    closeDialog();
-                                    return;
-                                }
+                                // 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);
+                                    }
 
-                                // close the dialog if the results contain an error
-                                if (!nf.Common.isEmpty(lineage.results.errors)) {
-                                    var errors = lineage.results.errors;
-                                    nf.Dialog.showOkDialog({
-                                        headerText: 'Process Lineage',
-                                        dialogContent: nf.Common.formatUnorderedList(errors)
-                                    });
+                                    // close the dialog
+                                    $('#lineage-query-dialog').modal('hide');
+                                };
 
-                                    closeDialog();
-                                    return;
-                                }
+                                // polls for the event lineage
+                                var pollLineage = function () {
+                                    getLineage(lineage).done(function (response) {
+                                        lineage = response.lineage;
 
-                                // update the precent complete
-                                provenanceTableCtrl.updateProgress(lineageProgress, lineage.percentCompleted);
+                                        // process the lineage
+                                        processLineage();
+                                    }).fail(closeDialog);
+                                };
 
-                                // process the results if they are finished
-                                if (lineage.finished === true) {
-                                    var results = lineage.results;
+                                // processes the event lineage
+                                var processLineage = function () {
+                                    // if the request was cancelled just ignore the current response
+                                    if (cancelled === true) {
+                                        closeDialog();
+                                        return;
+                                    }
 
-                                    // 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
-                                        nf.Dialog.showOkDialog({
-                                            headerText: 'Lineage Results',
-                                            dialogContent: 'The lineage search has completed successfully but there no results were found. The events may have aged off.'
+                                    // 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)
                                         });
-                                    }
 
-                                    // close the searching.. dialog
-                                    closeDialog();
-                                } else {
-                                    lineageTimer = setTimeout(function () {
-                                        // clear the timer since we've been invoked
-                                        lineageTimer = null;
+                                        closeDialog();
+                                        return;
+                                    }
 
-                                        // for the lineage
-                                        pollLineage();
-                                    }, 2000);
-                                }
-                            };
+                                    // 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.'
+                                            });
+                                        }
 
-                            // 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);
-                        };
-
-                        // 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;
+                                        // close the searching.. dialog
+                                        closeDialog();
                                     } else {
-                                        return node.flowFileUuid !== eventUuid && $.inArray(eventUuid, node.parentUuids) === -1;
-                                    }
-                                };
+                                        lineageTimer = setTimeout(function () {
+                                            // clear the timer since we've been invoked
+                                            lineageTimer = null;
 
-                                // 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;
+                                            //  for the lineage
+                                            pollLineage();
+                                        }, 2000);
                                     }
                                 };
 
-                                // the event is fan in if the flowfile uuid is in the children
-                                var fanIn = $.inArray(eventUuid, provenanceEvent.childUuids) >= 0;
+                                // once the query is submitted wait until its finished
+                                submitLineage(lineageRequest).done(function (response) {
+                                    lineage = response.lineage;
 
-                                // collapses the specified uuids
-                                var collapse = function (uuids) {
-                                    var newUuids = false;
+                                    // process the lineage, if its not done computing wait 1 second before checking again
+                                    processLineage(1);
+                                }).fail(closeDialog);
+                            };
 
-                                    // 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);
+                            // handles updating the lineage graph
+                            var renderEventLineage = function (lineageResults) {
+                                addLineage(lineageResults.nodes, lineageResults.links, provenanceTableCtrl);
+                            };
 
-                                            // include all related outgoing flow file uuids
-                                            $.each(node.outgoing, function (_, outgoing) {
-                                                if (!uuids.has(outgoing.flowFileUuid)) {
-                                                    uuids.add(outgoing.flowFileUuid);
-                                                    newUuids = true;
-                                                }
-                                            });
+                            // 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;
+                                                    }
+                                                });
+                                            }
+                                        });
 
-                                    // 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;
+                                        // 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 any related uuids
+                                        if (newUuids) {
+                                            collapse(uuids);
                                         }
-                                    });
+                                    };
 
-                                    // collapse any related uuids
-                                    if (newUuids) {
-                                        collapse(uuids);
-                                    }
-                                };
+                                    // collapse the specified uuids
+                                    collapse(eventUuids);
 
-                                // collapse the specified uuids
-                                collapse(eventUuids);
+                                    // update the layout
+                                    refresh(provenanceTableCtrl);
+                                });
+                            };
 
-                                // 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);
+                                }
                             });
-                        };
+                        }
 
-                        // 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({
-                                    lin

<TRUNCATED>