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:37 UTC

[04/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-table.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-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
index 7da8593..2826e3a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
@@ -15,1446 +15,1508 @@
  * limitations under the License.
  */
 
-/* global nf, top, Slick */
-
-nf.ng.ProvenanceTable = function (provenanceLineageCtrl) {
+/* global nf, top, define, module, require, exports */
+
+(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        define(['jquery',
+                'Slick',
+                'nf.Common',
+                'nf.Dialog',
+                'nf.ErrorHandler',
+                'nf.Storage',
+                'nf.ng.Bridge'],
+            function ($, Slick, common, dialog, errorHandler, storage, angularBridge) {
+                return (nf.ng.ProvenanceTable = factory($, Slick, common, dialog, errorHandler, storage, angularBridge));
+            });
+    } else if (typeof exports === 'object' && typeof module === 'object') {
+        module.exports = (nf.ng.ProvenanceTable =
+            factory(require('jquery'),
+                require('Slick'),
+                require('nf.Common'),
+                require('nf.Dialog'),
+                require('nf.ErrorHandler'),
+                require('nf.Storage'),
+                require('nf.ng.Bridge')));
+    } else {
+        nf.ng.ProvenanceTable = factory(root.$,
+            root.Slick,
+            root.nf.Common,
+            root.nf.Dialog,
+            root.nf.ErrorHandler,
+            root.nf.Storage,
+            root.nf.ng.Bridge);
+    }
+}(this, function ($, Slick, common, dialog, errorHandler, storage, angularBridge) {
     'use strict';
 
-    /**
-     * Configuration object used to hold a number of configuration items.
-     */
-    var config = {
-        maxResults: 1000,
-        defaultStartTime: '00:00:00',
-        defaultEndTime: '23:59:59',
-        styles: {
-            hidden: 'hidden'
-        },
-        urls: {
-            searchOptions: '../nifi-api/provenance/search-options',
-            replays: '../nifi-api/provenance-events/replays',
-            provenance: '../nifi-api/provenance',
-            provenanceEvents: '../nifi-api/provenance-events/',
-            clusterSearch: '../nifi-api/flow/cluster/search-results',
-            d3Script: 'js/d3/d3.min.js',
-            lineageScript: 'js/nf/provenance/nf-provenance-lineage.js',
-            uiExtensionToken: '../nifi-api/access/ui-extension-token',
-            downloadToken: '../nifi-api/access/download-token'
-        }
-    };
+    var nfProvenanceTable = function (provenanceLineageCtrl) {
+        'use strict';
 
-    /**
-     * The last search performed
-     */
-    var cachedQuery = {};
-
-    /**
-     * Downloads the content for the provenance event that is currently loaded in the specified direction.
-     *
-     * @param {string} direction
-     */
-    var downloadContent = function (direction) {
-        var eventId = $('#provenance-event-id').text();
-
-        // build the url
-        var dataUri = config.urls.provenanceEvents + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction);
-
-        // perform the request once we've received a token
-        nf.Common.getAccessToken(config.urls.downloadToken).done(function (downloadToken) {
-            var parameters = {};
-
-            // conditionally include the ui extension token
-            if (!nf.Common.isBlank(downloadToken)) {
-                parameters['access_token'] = downloadToken;
+        /**
+         * Configuration object used to hold a number of configuration items.
+         */
+        var config = {
+            maxResults: 1000,
+            defaultStartTime: '00:00:00',
+            defaultEndTime: '23:59:59',
+            styles: {
+                hidden: 'hidden'
+            },
+            urls: {
+                searchOptions: '../nifi-api/provenance/search-options',
+                replays: '../nifi-api/provenance-events/replays',
+                provenance: '../nifi-api/provenance',
+                provenanceEvents: '../nifi-api/provenance-events/',
+                clusterSearch: '../nifi-api/flow/cluster/search-results',
+                d3Script: 'js/d3/d3.min.js',
+                lineageScript: 'js/nf/provenance/nf-provenance-lineage.js',
+                uiExtensionToken: '../nifi-api/access/ui-extension-token',
+                downloadToken: '../nifi-api/access/download-token'
             }
+        };
 
-            // conditionally include the cluster node id
-            var clusterNodeId = $('#provenance-event-cluster-node-id').text();
-            if (!nf.Common.isBlank(clusterNodeId)) {
-                parameters['clusterNodeId'] = clusterNodeId;
-            }
+        /**
+         * The last search performed
+         */
+        var cachedQuery = {};
 
-            // open the url
-            if ($.isEmptyObject(parameters)) {
-                window.open(dataUri);
-            } else {
-                window.open(dataUri + '?' + $.param(parameters));
-            }
-        }).fail(function () {
-            nf.Dialog.showOkDialog({
-                headerText: 'Provenance',
-                dialogContent: 'Unable to generate access token for downloading content.'
-            });
-        });
-    };
+        /**
+         * Downloads the content for the provenance event that is currently loaded in the specified direction.
+         *
+         * @param {string} direction
+         */
+        var downloadContent = function (direction) {
+            var eventId = $('#provenance-event-id').text();
 
-    /**
-     * Views the content for the provenance event that is currently loaded in the specified direction.
-     *
-     * @param {string} direction
-     */
-    var viewContent = function (direction) {
-        var controllerUri = $('#nifi-controller-uri').text();
-        var eventId = $('#provenance-event-id').text();
-
-        // build the uri to the data
-        var dataUri = controllerUri + 'provenance-events/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction);
-
-        // generate tokens as necessary
-        var getAccessTokens = $.Deferred(function (deferred) {
-            if (nf.Storage.hasItem('jwt')) {
-                // generate a token for the ui extension and another for the callback
-                var uiExtensionToken = $.ajax({
-                    type: 'POST',
-                    url: config.urls.uiExtensionToken
-                });
-                var downloadToken = $.ajax({
-                    type: 'POST',
-                    url: config.urls.downloadToken
+            // build the url
+            var dataUri = config.urls.provenanceEvents + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction);
+
+            // perform the request once we've received a token
+            common.getAccessToken(config.urls.downloadToken).done(function (downloadToken) {
+                var parameters = {};
+
+                // conditionally include the ui extension token
+                if (!common.isBlank(downloadToken)) {
+                    parameters['access_token'] = downloadToken;
+                }
+
+                // conditionally include the cluster node id
+                var clusterNodeId = $('#provenance-event-cluster-node-id').text();
+                if (!common.isBlank(clusterNodeId)) {
+                    parameters['clusterNodeId'] = clusterNodeId;
+                }
+
+                // open the url
+                if ($.isEmptyObject(parameters)) {
+                    window.open(dataUri);
+                } else {
+                    window.open(dataUri + '?' + $.param(parameters));
+                }
+            }).fail(function () {
+                dialog.showOkDialog({
+                    headerText: 'Provenance',
+                    dialogContent: 'Unable to generate access token for downloading content.'
                 });
+            });
+        };
 
-                // wait for each token
-                $.when(uiExtensionToken, downloadToken).done(function (uiExtensionTokenResult, downloadTokenResult) {
-                    var uiExtensionToken = uiExtensionTokenResult[0];
-                    var downloadToken = downloadTokenResult[0];
-                    deferred.resolve(uiExtensionToken, downloadToken);
-                }).fail(function () {
-                    nf.Dialog.showOkDialog({
-                        headerText: 'Provenance',
-                        dialogContent: 'Unable to generate access token for viewing content.'
+        /**
+         * Views the content for the provenance event that is currently loaded in the specified direction.
+         *
+         * @param {string} direction
+         */
+        var viewContent = function (direction) {
+            var controllerUri = $('#nifi-controller-uri').text();
+            var eventId = $('#provenance-event-id').text();
+
+            // build the uri to the data
+            var dataUri = controllerUri + 'provenance-events/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction);
+
+            // generate tokens as necessary
+            var getAccessTokens = $.Deferred(function (deferred) {
+                if (storage.hasItem('jwt')) {
+                    // generate a token for the ui extension and another for the callback
+                    var uiExtensionToken = $.ajax({
+                        type: 'POST',
+                        url: config.urls.uiExtensionToken
+                    });
+                    var downloadToken = $.ajax({
+                        type: 'POST',
+                        url: config.urls.downloadToken
                     });
-                    deferred.reject();
-                });
-            } else {
-                deferred.resolve('', '');
-            }
-        }).promise();
 
-        // perform the request after we've received the tokens
-        getAccessTokens.done(function (uiExtensionToken, downloadToken) {
-            var dataUriParameters = {};
+                    // wait for each token
+                    $.when(uiExtensionToken, downloadToken).done(function (uiExtensionTokenResult, downloadTokenResult) {
+                        var uiExtensionToken = uiExtensionTokenResult[0];
+                        var downloadToken = downloadTokenResult[0];
+                        deferred.resolve(uiExtensionToken, downloadToken);
+                    }).fail(function () {
+                        dialog.showOkDialog({
+                            headerText: 'Provenance',
+                            dialogContent: 'Unable to generate access token for viewing content.'
+                        });
+                        deferred.reject();
+                    });
+                } else {
+                    deferred.resolve('', '');
+                }
+            }).promise();
 
-            // conditionally include the cluster node id
-            var clusterNodeId = $('#provenance-event-cluster-node-id').text();
-            if (!nf.Common.isBlank(clusterNodeId)) {
-                dataUriParameters['clusterNodeId'] = clusterNodeId;
-            }
+            // perform the request after we've received the tokens
+            getAccessTokens.done(function (uiExtensionToken, downloadToken) {
+                var dataUriParameters = {};
 
-            // include the download token if applicable
-            if (!nf.Common.isBlank(downloadToken)) {
-                dataUriParameters['access_token'] = downloadToken;
-            }
+                // conditionally include the cluster node id
+                var clusterNodeId = $('#provenance-event-cluster-node-id').text();
+                if (!common.isBlank(clusterNodeId)) {
+                    dataUriParameters['clusterNodeId'] = clusterNodeId;
+                }
 
-            // include parameters if necessary
-            if ($.isEmptyObject(dataUriParameters) === false) {
-                dataUri = dataUri + '?' + $.param(dataUriParameters);
-            }
+                // include the download token if applicable
+                if (!common.isBlank(downloadToken)) {
+                    dataUriParameters['access_token'] = downloadToken;
+                }
 
-            // open the content viewer
-            var contentViewerUrl = $('#nifi-content-viewer-url').text();
+                // include parameters if necessary
+                if ($.isEmptyObject(dataUriParameters) === false) {
+                    dataUri = dataUri + '?' + $.param(dataUriParameters);
+                }
 
-            // if there's already a query string don't add another ?... this assumes valid
-            // input meaning that if the url has already included a ? it also contains at
-            // least one query parameter
-            if (contentViewerUrl.indexOf('?') === -1) {
-                contentViewerUrl += '?';
-            } else {
-                contentViewerUrl += '&';
-            }
+                // open the content viewer
+                var contentViewerUrl = $('#nifi-content-viewer-url').text();
 
-            var contentViewerParameters = {
-                'ref': dataUri
-            };
+                // if there's already a query string don't add another ?... this assumes valid
+                // input meaning that if the url has already included a ? it also contains at
+                // least one query parameter
+                if (contentViewerUrl.indexOf('?') === -1) {
+                    contentViewerUrl += '?';
+                } else {
+                    contentViewerUrl += '&';
+                }
 
-            // include the download token if applicable
-            if (!nf.Common.isBlank(uiExtensionToken)) {
-                contentViewerParameters['access_token'] = uiExtensionToken;
-            }
+                var contentViewerParameters = {
+                    'ref': dataUri
+                };
 
-            // open the content viewer
-            window.open(contentViewerUrl + $.param(contentViewerParameters));
-        });
-    };
+                // include the download token if applicable
+                if (!common.isBlank(uiExtensionToken)) {
+                    contentViewerParameters['access_token'] = uiExtensionToken;
+                }
 
-    /**
-     * Initializes the details dialog.
-     */
-    var initDetailsDialog = function () {
-        // initialize the properties tabs
-        $('#event-details-tabs').tabbs({
-            tabStyle: 'tab',
-            selectedTabStyle: 'selected-tab',
-            scrollableTabContentStyle: 'scrollable',
-            tabs: [{
-                name: 'Details',
-                tabContentId: 'event-details-tab-content'
-            }, {
-                name: 'Attributes',
-                tabContentId: 'attributes-tab-content'
-            }, {
-                name: 'Content',
-                tabContentId: 'content-tab-content'
-            }]
-        });
-
-        $('#event-details-dialog').modal({
-            scrollableContentStyle: 'scrollable',
-            headerText: 'Provenance Event',
-            buttons: [{
-                buttonText: 'Ok',
-                color: {
-                    base: '#728E9B',
-                    hover: '#004849',
-                    text: '#ffffff'
-                },
+                // open the content viewer
+                window.open(contentViewerUrl + $.param(contentViewerParameters));
+            });
+        };
+
+        /**
+         * Initializes the details dialog.
+         */
+        var initDetailsDialog = function () {
+            // initialize the properties tabs
+            $('#event-details-tabs').tabbs({
+                tabStyle: 'tab',
+                selectedTabStyle: 'selected-tab',
+                scrollableTabContentStyle: 'scrollable',
+                tabs: [{
+                    name: 'Details',
+                    tabContentId: 'event-details-tab-content'
+                }, {
+                    name: 'Attributes',
+                    tabContentId: 'attributes-tab-content'
+                }, {
+                    name: 'Content',
+                    tabContentId: 'content-tab-content'
+                }]
+            });
+
+            $('#event-details-dialog').modal({
+                scrollableContentStyle: 'scrollable',
+                headerText: 'Provenance Event',
+                buttons: [{
+                    buttonText: 'Ok',
+                    color: {
+                        base: '#728E9B',
+                        hover: '#004849',
+                        text: '#ffffff'
+                    },
+                    handler: {
+                        click: function () {
+                            $('#event-details-dialog').modal('hide');
+                        }
+                    }
+                }],
                 handler: {
-                    click: function () {
-                        $('#event-details-dialog').modal('hide');
+                    close: function () {
+                        // clear the details
+                        $('#additional-provenance-details').empty();
+                        $('#attributes-container').empty();
+                        $('#parent-flowfiles-container').empty();
+                        $('#child-flowfiles-container').empty();
+                        $('#provenance-event-cluster-node-id').text('');
+                        $('#modified-attribute-toggle').removeClass('checkbox-checked').addClass('checkbox-unchecked');
+                    },
+                    open: function () {
+                        common.toggleScrollable($('#' + this.find('.tab-container').attr('id') + '-content').get(0));
                     }
                 }
-            }],
-            handler: {
-                close: function () {
-                    // clear the details
-                    $('#additional-provenance-details').empty();
-                    $('#attributes-container').empty();
-                    $('#parent-flowfiles-container').empty();
-                    $('#child-flowfiles-container').empty();
-                    $('#provenance-event-cluster-node-id').text('');
-                    $('#modified-attribute-toggle').removeClass('checkbox-checked').addClass('checkbox-unchecked');
-                },
-                open: function () {
-                    nf.Common.toggleScrollable($('#' + this.find('.tab-container').attr('id') + '-content').get(0));
-                }
-            }
-        });
-
-        // toggle which attributes are visible
-        $('#modified-attribute-toggle').on('click', function () {
-            var unmodifiedAttributes = $('#attributes-container div.attribute-unmodified');
-            if (unmodifiedAttributes.is(':visible')) {
-                $('#attributes-container div.attribute-unmodified').hide();
-            } else {
-                $('#attributes-container div.attribute-unmodified').show();
-            }
-        });
-
-        // input download
-        $('#input-content-download').on('click', function () {
-            downloadContent('input');
-        });
-
-        // output download
-        $('#output-content-download').on('click', function () {
-            downloadContent('output');
-        });
-
-        // if a content viewer url is specified, use it
-        if (nf.Common.isContentViewConfigured()) {
-            // input view
-            $('#input-content-view').on('click', function () {
-                viewContent('input');
             });
 
-            // output view
-            $('#output-content-view').on('click', function () {
-                viewContent('output');
+            // toggle which attributes are visible
+            $('#modified-attribute-toggle').on('click', function () {
+                var unmodifiedAttributes = $('#attributes-container div.attribute-unmodified');
+                if (unmodifiedAttributes.is(':visible')) {
+                    $('#attributes-container div.attribute-unmodified').hide();
+                } else {
+                    $('#attributes-container div.attribute-unmodified').show();
+                }
             });
-        }
 
-        // handle the replay and downloading
-        $('#replay-content').on('click', function () {
-            var replayEntity = {
-                'eventId': $('#provenance-event-id').text()
-            };
+            // input download
+            $('#input-content-download').on('click', function () {
+                downloadContent('input');
+            });
 
-            // conditionally include the cluster node id
-            var clusterNodeId = $('#provenance-event-cluster-node-id').text();
-            if (!nf.Common.isBlank(clusterNodeId)) {
-                replayEntity['clusterNodeId'] = clusterNodeId;
-            }
+            // output download
+            $('#output-content-download').on('click', function () {
+                downloadContent('output');
+            });
 
-            $.ajax({
-                type: 'POST',
-                url: config.urls.replays,
-                data: JSON.stringify(replayEntity),
-                dataType: 'json',
-                contentType: 'application/json'
-            }).done(function (response) {
-                nf.Dialog.showOkDialog({
-                    headerText: 'Provenance',
-                    dialogContent: 'Successfully submitted replay request.'
+            // if a content viewer url is specified, use it
+            if (common.isContentViewConfigured()) {
+                // input view
+                $('#input-content-view').on('click', function () {
+                    viewContent('input');
                 });
-            }).fail(nf.Common.handleAjaxError);
 
-            $('#event-details-dialog').modal('hide');
-        });
+                // output view
+                $('#output-content-view').on('click', function () {
+                    viewContent('output');
+                });
+            }
 
-        // show the replay panel
-        $('#replay-details').show();
-    };
+            // handle the replay and downloading
+            $('#replay-content').on('click', function () {
+                var replayEntity = {
+                    'eventId': $('#provenance-event-id').text()
+                };
 
-    /**
-     * Initializes the search dialog.
-     *
-     * @param {boolean} isClustered     Whether or not this NiFi clustered
-     */
-    var initSearchDialog = function (isClustered, provenanceTableCtrl) {
-        // configure the start and end date picker
-        $('#provenance-search-start-date, #provenance-search-end-date').datepicker({
-            showAnim: '',
-            showOtherMonths: true,
-            selectOtherMonths: true
-        });
-
-        // initialize the default start date/time
-        $('#provenance-search-start-date').datepicker('setDate', '+0d');
-        $('#provenance-search-end-date').datepicker('setDate', '+0d');
-        $('#provenance-search-start-time').val('00:00:00');
-        $('#provenance-search-end-time').val('23:59:59');
-
-        // initialize the default file sizes
-        $('#provenance-search-minimum-file-size').val('');
-        $('#provenance-search-maximum-file-size').val('');
-
-        // allow users to be able to search a specific node
-        if (isClustered) {
-            // make the dialog larger to support the select location
-            $('#provenance-search-dialog').height(575);
-
-            // get the nodes in the cluster
-            $.ajax({
-                type: 'GET',
-                url: config.urls.clusterSearch,
-                dataType: 'json'
-            }).done(function (response) {
-                var nodeResults = response.nodeResults;
-
-                // create the searchable options
-                var searchableOptions = [{
-                    text: 'cluster',
-                    value: null
-                }];
-
-                // sort the nodes
-                nodeResults.sort(function (a, b) {
-                    var compA = a.address.toUpperCase();
-                    var compB = b.address.toUpperCase();
-                    return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
-                });
+                // conditionally include the cluster node id
+                var clusterNodeId = $('#provenance-event-cluster-node-id').text();
+                if (!common.isBlank(clusterNodeId)) {
+                    replayEntity['clusterNodeId'] = clusterNodeId;
+                }
 
-                // add each node
-                $.each(nodeResults, function (_, nodeResult) {
-                    searchableOptions.push({
-                        text: nodeResult.address,
-                        value: nodeResult.id
+                $.ajax({
+                    type: 'POST',
+                    url: config.urls.replays,
+                    data: JSON.stringify(replayEntity),
+                    dataType: 'json',
+                    contentType: 'application/json'
+                }).done(function (response) {
+                    dialog.showOkDialog({
+                        headerText: 'Provenance',
+                        dialogContent: 'Successfully submitted replay request.'
                     });
-                });
+                }).fail(errorHandler.handleAjaxError);
 
-                // populate the combo
-                $('#provenance-search-location').combo({
-                    options: searchableOptions
-                });
-            }).fail(nf.Common.handleAjaxError);
+                $('#event-details-dialog').modal('hide');
+            });
 
-            // show the node search combo
-            $('#provenance-search-location-container').show();
-        }
+            // show the replay panel
+            $('#replay-details').show();
+        };
 
-        // configure the search dialog
-        $('#provenance-search-dialog').modal({
-            scrollableContentStyle: 'scrollable',
-            headerText: 'Search Events',
-            buttons: [{
-                buttonText: 'Search',
-                color: {
-                    base: '#728E9B',
-                    hover: '#004849',
-                    text: '#ffffff'
-                },
-                handler: {
-                    click: function () {
-                        $('#provenance-search-dialog').modal('hide');
-
-                        var search = {};
-
-                        // extract the start date time
-                        var startDate = $.trim($('#provenance-search-start-date').val());
-                        var startTime = $.trim($('#provenance-search-start-time').val());
-                        if (startDate !== '') {
-                            if (startTime === '') {
-                                startTime = config.defaultStartTime;
-                                $('#provenance-search-start-time').val(startTime);
-                            }
-                            search['startDate'] = startDate + ' ' + startTime + ' ' + $('.timezone:first').text();
-                        }
+        /**
+         * Initializes the search dialog.
+         *
+         * @param {boolean} isClustered     Whether or not this NiFi clustered
+         */
+        var initSearchDialog = function (isClustered, provenanceTableCtrl) {
+            // configure the start and end date picker
+            $('#provenance-search-start-date, #provenance-search-end-date').datepicker({
+                showAnim: '',
+                showOtherMonths: true,
+                selectOtherMonths: true
+            });
 
-                        // extract the end date time
-                        var endDate = $.trim($('#provenance-search-end-date').val());
-                        var endTime = $.trim($('#provenance-search-end-time').val());
-                        if (endDate !== '') {
-                            if (endTime === '') {
-                                endTime = config.defaultEndTime;
-                                $('#provenance-search-end-time').val(endTime);
-                            }
-                            search['endDate'] = endDate + ' ' + endTime + ' ' + $('.timezone:first').text();
-                        }
+            // initialize the default start date/time
+            $('#provenance-search-start-date').datepicker('setDate', '+0d');
+            $('#provenance-search-end-date').datepicker('setDate', '+0d');
+            $('#provenance-search-start-time').val('00:00:00');
+            $('#provenance-search-end-time').val('23:59:59');
 
-                        // extract the min/max file size
-                        var minFileSize = $.trim($('#provenance-search-minimum-file-size').val());
-                        if (minFileSize !== '') {
-                            search['minimumFileSize'] = minFileSize;
-                        }
+            // initialize the default file sizes
+            $('#provenance-search-minimum-file-size').val('');
+            $('#provenance-search-maximum-file-size').val('');
 
-                        var maxFileSize = $.trim($('#provenance-search-maximum-file-size').val());
-                        if (maxFileSize !== '') {
-                            search['maximumFileSize'] = maxFileSize;
-                        }
+            // allow users to be able to search a specific node
+            if (isClustered) {
+                // make the dialog larger to support the select location
+                $('#provenance-search-dialog').height(575);
+
+                // get the nodes in the cluster
+                $.ajax({
+                    type: 'GET',
+                    url: config.urls.clusterSearch,
+                    dataType: 'json'
+                }).done(function (response) {
+                    var nodeResults = response.nodeResults;
+
+                    // create the searchable options
+                    var searchableOptions = [{
+                        text: 'cluster',
+                        value: null
+                    }];
+
+                    // sort the nodes
+                    nodeResults.sort(function (a, b) {
+                        var compA = a.address.toUpperCase();
+                        var compB = b.address.toUpperCase();
+                        return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
+                    });
 
-                        // limit search to a specific node
-                        if (isClustered) {
-                            var searchLocation = $('#provenance-search-location').combo('getSelectedOption');
-                            if (searchLocation.value !== null) {
-                                search['clusterNodeId'] = searchLocation.value;
-                            }
-                        }
+                    // add each node
+                    $.each(nodeResults, function (_, nodeResult) {
+                        searchableOptions.push({
+                            text: nodeResult.address,
+                            value: nodeResult.id
+                        });
+                    });
 
-                        // add the search criteria
-                        search['searchTerms'] = getSearchCriteria();
+                    // populate the combo
+                    $('#provenance-search-location').combo({
+                        options: searchableOptions
+                    });
+                }).fail(errorHandler.handleAjaxError);
 
-                        // reload the table
-                        provenanceTableCtrl.loadProvenanceTable(search);
-                    }
-                }
-            },
-                {
-                    buttonText: 'Cancel',
+                // show the node search combo
+                $('#provenance-search-location-container').show();
+            }
+
+            // configure the search dialog
+            $('#provenance-search-dialog').modal({
+                scrollableContentStyle: 'scrollable',
+                headerText: 'Search Events',
+                buttons: [{
+                    buttonText: 'Search',
                     color: {
-                        base: '#E3E8EB',
-                        hover: '#C7D2D7',
-                        text: '#004849'
+                        base: '#728E9B',
+                        hover: '#004849',
+                        text: '#ffffff'
                     },
                     handler: {
                         click: function () {
                             $('#provenance-search-dialog').modal('hide');
+
+                            var search = {};
+
+                            // extract the start date time
+                            var startDate = $.trim($('#provenance-search-start-date').val());
+                            var startTime = $.trim($('#provenance-search-start-time').val());
+                            if (startDate !== '') {
+                                if (startTime === '') {
+                                    startTime = config.defaultStartTime;
+                                    $('#provenance-search-start-time').val(startTime);
+                                }
+                                search['startDate'] = startDate + ' ' + startTime + ' ' + $('.timezone:first').text();
+                            }
+
+                            // extract the end date time
+                            var endDate = $.trim($('#provenance-search-end-date').val());
+                            var endTime = $.trim($('#provenance-search-end-time').val());
+                            if (endDate !== '') {
+                                if (endTime === '') {
+                                    endTime = config.defaultEndTime;
+                                    $('#provenance-search-end-time').val(endTime);
+                                }
+                                search['endDate'] = endDate + ' ' + endTime + ' ' + $('.timezone:first').text();
+                            }
+
+                            // extract the min/max file size
+                            var minFileSize = $.trim($('#provenance-search-minimum-file-size').val());
+                            if (minFileSize !== '') {
+                                search['minimumFileSize'] = minFileSize;
+                            }
+
+                            var maxFileSize = $.trim($('#provenance-search-maximum-file-size').val());
+                            if (maxFileSize !== '') {
+                                search['maximumFileSize'] = maxFileSize;
+                            }
+
+                            // limit search to a specific node
+                            if (isClustered) {
+                                var searchLocation = $('#provenance-search-location').combo('getSelectedOption');
+                                if (searchLocation.value !== null) {
+                                    search['clusterNodeId'] = searchLocation.value;
+                                }
+                            }
+
+                            // add the search criteria
+                            search['searchTerms'] = getSearchCriteria();
+
+                            // reload the table
+                            provenanceTableCtrl.loadProvenanceTable(search);
                         }
                     }
-                }]
-        });
-
-        return $.ajax({
-            type: 'GET',
-            url: config.urls.searchOptions,
-            dataType: 'json'
-        }).done(function (response) {
-            var provenanceOptions = response.provenanceOptions;
-
-            // load all searchable fields
-            $.each(provenanceOptions.searchableFields, function (_, field) {
-                appendSearchableField(field);
+                },
+                    {
+                        buttonText: 'Cancel',
+                        color: {
+                            base: '#E3E8EB',
+                            hover: '#C7D2D7',
+                            text: '#004849'
+                        },
+                        handler: {
+                            click: function () {
+                                $('#provenance-search-dialog').modal('hide');
+                            }
+                        }
+                    }]
             });
-        });
-    };
 
-    /**
-     * Initializes the provenance query dialog.
-     */
-    var initProvenanceQueryDialog = function () {
-        // initialize the dialog
-        $('#provenance-query-dialog').modal({
-            scrollableContentStyle: 'scrollable',
-            headerText: 'Searching provenance events...'
-        });
-    };
+            return $.ajax({
+                type: 'GET',
+                url: config.urls.searchOptions,
+                dataType: 'json'
+            }).done(function (response) {
+                var provenanceOptions = response.provenanceOptions;
 
-    /**
-     * Appends the specified searchable field to the search dialog.
-     *
-     * @param {type} field      The searchable field
-     */
-    var appendSearchableField = function (field) {
-        var searchableField = $('<div class="searchable-field"></div>').appendTo('#searchable-fields-container');
-        $('<span class="searchable-field-id hidden"></span>').text(field.id).appendTo(searchableField);
-        $('<div class="searchable-field-name"></div>').text(field.label).appendTo(searchableField);
-        $('<div class="searchable-field-value"><input type="text" class="searchable-field-input"/></div>').appendTo(searchableField);
-        $('<div class="clear"></div>').appendTo(searchableField);
-
-        // make the searchable accessible for populating
-        if (field.id === 'ProcessorID') {
-            searchableField.find('input').addClass('searchable-component-id');
-        } else if (field.id === 'FlowFileUUID') {
-            searchableField.find('input').addClass('searchable-flowfile-uuid');
-        }
+                // load all searchable fields
+                $.each(provenanceOptions.searchableFields, function (_, field) {
+                    appendSearchableField(field);
+                });
+            });
+        };
 
-        // ensure the no searchable fields message is hidden
-        $('#no-searchable-fields').hide();
-    };
+        /**
+         * Initializes the provenance query dialog.
+         */
+        var initProvenanceQueryDialog = function () {
+            // initialize the dialog
+            $('#provenance-query-dialog').modal({
+                scrollableContentStyle: 'scrollable',
+                headerText: 'Searching provenance events...'
+            });
+        };
 
-    /**
-     * Gets the search criteria that the user has specified.
-     */
-    var getSearchCriteria = function () {
-        var searchCriteria = {};
-        $('#searchable-fields-container').children('div.searchable-field').each(function () {
-            var searchableField = $(this);
-            var fieldId = searchableField.children('span.searchable-field-id').text();
-            var searchValue = $.trim(searchableField.find('input.searchable-field-input').val());
-
-            // if the field isn't blank include it in the search
-            if (!nf.Common.isBlank(searchValue)) {
-                searchCriteria[fieldId] = searchValue;
+        /**
+         * Appends the specified searchable field to the search dialog.
+         *
+         * @param {type} field      The searchable field
+         */
+        var appendSearchableField = function (field) {
+            var searchableField = $('<div class="searchable-field"></div>').appendTo('#searchable-fields-container');
+            $('<span class="searchable-field-id hidden"></span>').text(field.id).appendTo(searchableField);
+            $('<div class="searchable-field-name"></div>').text(field.label).appendTo(searchableField);
+            $('<div class="searchable-field-value"><input type="text" class="searchable-field-input"/></div>').appendTo(searchableField);
+            $('<div class="clear"></div>').appendTo(searchableField);
+
+            // make the searchable accessible for populating
+            if (field.id === 'ProcessorID') {
+                searchableField.find('input').addClass('searchable-component-id');
+            } else if (field.id === 'FlowFileUUID') {
+                searchableField.find('input').addClass('searchable-flowfile-uuid');
             }
-        });
-        return searchCriteria;
-    };
 
-    /**
-     * Initializes the provenance table.
-     *
-     * @param {boolean} isClustered     Whether or not this instance is clustered
-     */
-    var initProvenanceTable = function (isClustered, provenanceTableCtrl) {
-        // define the function for filtering the list
-        $('#provenance-filter').keyup(function () {
-            applyFilter();
-        });
-
-        // filter options
-        var filterOptions = [{
-            text: 'by component name',
-            value: 'componentName'
-        }, {
-            text: 'by component type',
-            value: 'componentType'
-        }, {
-            text: 'by type',
-            value: 'eventType'
-        }];
-
-        // if clustered, allowing filtering by node id
-        if (isClustered) {
-            filterOptions.push({
-                text: 'by node',
-                value: 'clusterNodeAddress'
+            // ensure the no searchable fields message is hidden
+            $('#no-searchable-fields').hide();
+        };
+
+        /**
+         * Gets the search criteria that the user has specified.
+         */
+        var getSearchCriteria = function () {
+            var searchCriteria = {};
+            $('#searchable-fields-container').children('div.searchable-field').each(function () {
+                var searchableField = $(this);
+                var fieldId = searchableField.children('span.searchable-field-id').text();
+                var searchValue = $.trim(searchableField.find('input.searchable-field-input').val());
+
+                // if the field isn't blank include it in the search
+                if (!common.isBlank(searchValue)) {
+                    searchCriteria[fieldId] = searchValue;
+                }
             });
-        }
+            return searchCriteria;
+        };
 
-        // initialize the filter combo
-        $('#provenance-filter-type').combo({
-            options: filterOptions,
-            select: function (option) {
+        /**
+         * Initializes the provenance table.
+         *
+         * @param {boolean} isClustered     Whether or not this instance is clustered
+         */
+        var initProvenanceTable = function (isClustered, provenanceTableCtrl) {
+            // define the function for filtering the list
+            $('#provenance-filter').keyup(function () {
                 applyFilter();
-            }
-        });
-
-        // clear the current search
-        $('#clear-provenance-search').click(function () {
-            // clear each searchable field
-            $('#searchable-fields-container').find('input.searchable-field-input').each(function () {
-                $(this).val('');
             });
 
-            // reset the default start date/time
-            $('#provenance-search-start-date').datepicker('setDate', '+0d');
-            $('#provenance-search-end-date').datepicker('setDate', '+0d');
-            $('#provenance-search-start-time').val('00:00:00');
-            $('#provenance-search-end-time').val('23:59:59');
-
-            // reset the minimum and maximum file size
-            $('#provenance-search-minimum-file-size').val('');
-            $('#provenance-search-maximum-file-size').val('');
+            // filter options
+            var filterOptions = [{
+                text: 'by component name',
+                value: 'componentName'
+            }, {
+                text: 'by component type',
+                value: 'componentType'
+            }, {
+                text: 'by type',
+                value: 'eventType'
+            }];
 
-            // if we are clustered reset the selected option
+            // if clustered, allowing filtering by node id
             if (isClustered) {
-                $('#provenance-search-location').combo('setSelectedOption', {
-                    text: 'cluster'
+                filterOptions.push({
+                    text: 'by node',
+                    value: 'clusterNodeAddress'
                 });
             }
 
-            // reset the stored query
-            cachedQuery = {};
-
-            // reload the table
-            provenanceTableCtrl.loadProvenanceTable();
-        });
-
-        // add hover effect and click handler for opening the dialog
-        $('#provenance-search-button').click(function () {
-            $('#provenance-search-dialog').modal('show');
-
-            // adjust the field width for a potential scrollbar
-            var searchFieldContainer = $('#searchable-fields-container');
-            if (searchFieldContainer.get(0).scrollHeight > searchFieldContainer.innerHeight()) {
-                $('input.searchable-field-input').width(245);
-            } else {
-                $('input.searchable-field-input').width(260);
-            }
-        });
+            // initialize the filter combo
+            $('#provenance-filter-type').combo({
+                options: filterOptions,
+                select: function (option) {
+                    applyFilter();
+                }
+            });
 
-        // define a custom formatter for the more details column
-        var moreDetailsFormatter = function (row, cell, value, columnDef, dataContext) {
-            return '<div title="View Details" class="pointer show-event-details fa fa-info-circle"></div>';
-        };
+            // clear the current search
+            $('#clear-provenance-search').click(function () {
+                // clear each searchable field
+                $('#searchable-fields-container').find('input.searchable-field-input').each(function () {
+                    $(this).val('');
+                });
 
-        // define how general values are formatted
-        var valueFormatter = function (row, cell, value, columnDef, dataContext) {
-            return nf.Common.formatValue(value);
-        };
+                // reset the default start date/time
+                $('#provenance-search-start-date').datepicker('setDate', '+0d');
+                $('#provenance-search-end-date').datepicker('setDate', '+0d');
+                $('#provenance-search-start-time').val('00:00:00');
+                $('#provenance-search-end-time').val('23:59:59');
 
-        // determine if the this page is in the shell
-        var isInShell = (top !== window);
+                // reset the minimum and maximum file size
+                $('#provenance-search-minimum-file-size').val('');
+                $('#provenance-search-maximum-file-size').val('');
 
-        // define how the column is formatted
-        var showLineageFormatter = function (row, cell, value, columnDef, dataContext) {
-            var markup = '';
+                // if we are clustered reset the selected option
+                if (isClustered) {
+                    $('#provenance-search-location').combo('setSelectedOption', {
+                        text: 'cluster'
+                    });
+                }
 
-            // conditionally include the cluster node id
-            if (nf.Common.SUPPORTS_SVG) {
-                markup += '<div title="Show Lineage" class="pointer show-lineage icon icon-lineage" style="margin-right: 3px;"></div>';
-            }
+                // reset the stored query
+                cachedQuery = {};
 
-            // conditionally support going to the component
-            var isRemotePort = dataContext.componentType === 'Remote Input Port' || dataContext.componentType === 'Remote Output Port';
-            if (isInShell && nf.Common.isDefinedAndNotNull(dataContext.groupId) && isRemotePort === false) {
-                markup += '<div class="pointer go-to fa fa-long-arrow-right" title="Go To"></div>';
-            }
+                // reload the table
+                provenanceTableCtrl.loadProvenanceTable();
+            });
 
-            return markup;
-        };
+            // add hover effect and click handler for opening the dialog
+            $('#provenance-search-button').click(function () {
+                $('#provenance-search-dialog').modal('show');
 
-        // initialize the provenance table
-        var provenanceColumns = [
-            {
-                id: 'moreDetails',
-                name: '&nbsp;',
-                sortable: false,
-                resizable: false,
-                formatter: moreDetailsFormatter,
-                width: 50,
-                maxWidth: 50
-            },
-            {
-                id: 'eventTime',
-                name: 'Date/Time',
-                field: 'eventTime',
-                sortable: true,
-                defaultSortAsc: false,
-                resizable: true
-            },
-            {id: 'eventType', name: 'Type', field: 'eventType', sortable: true, resizable: true},
-            {id: 'flowFileUuid', name: 'FlowFile Uuid', field: 'flowFileUuid', sortable: true, resizable: true},
-            {id: 'fileSize', name: 'Size', field: 'fileSize', sortable: true, defaultSortAsc: false, resizable: true},
-            {
-                id: 'componentName',
-                name: 'Component Name',
-                field: 'componentName',
-                sortable: true,
-                resizable: true,
-                formatter: valueFormatter
-            },
-            {id: 'componentType', name: 'Component Type', field: 'componentType', sortable: true, resizable: true}
-        ];
-
-        // conditionally show the cluster node identifier
-        if (isClustered) {
-            provenanceColumns.push({
-                id: 'clusterNodeAddress',
-                name: 'Node',
-                field: 'clusterNodeAddress',
-                sortable: true,
-                resizable: true
+                // adjust the field width for a potential scrollbar
+                var searchFieldContainer = $('#searchable-fields-container');
+                if (searchFieldContainer.get(0).scrollHeight > searchFieldContainer.innerHeight()) {
+                    $('input.searchable-field-input').width(245);
+                } else {
+                    $('input.searchable-field-input').width(260);
+                }
             });
-        }
 
-        // conditionally show the action column
-        if (nf.Common.SUPPORTS_SVG || isInShell) {
-            provenanceColumns.push({
-                id: 'actions',
-                name: '&nbsp;',
-                formatter: showLineageFormatter,
-                resizable: false,
-                sortable: false,
-                width: 50,
-                maxWidth: 50
-            });
-        }
+            // define a custom formatter for the more details column
+            var moreDetailsFormatter = function (row, cell, value, columnDef, dataContext) {
+                return '<div title="View Details" class="pointer show-event-details fa fa-info-circle"></div>';
+            };
 
-        var provenanceOptions = {
-            forceFitColumns: true,
-            enableTextSelectionOnCells: true,
-            enableCellNavigation: true,
-            enableColumnReorder: false,
-            autoEdit: false,
-            multiSelect: false,
-            rowHeight: 24
-        };
+            // define how general values are formatted
+            var valueFormatter = function (row, cell, value, columnDef, dataContext) {
+                return common.formatValue(value);
+            };
 
-        // create the remote model
-        var provenanceData = new Slick.Data.DataView({
-            inlineFilters: false
-        });
-        provenanceData.setItems([]);
-        provenanceData.setFilterArgs({
-            searchString: '',
-            property: 'name'
-        });
-        provenanceData.setFilter(filter);
-
-        // initialize the sort
-        sort({
-            columnId: 'eventTime',
-            sortAsc: false
-        }, provenanceData);
-
-        // initialize the grid
-        var provenanceGrid = new Slick.Grid('#provenance-table', provenanceData, provenanceColumns, provenanceOptions);
-        provenanceGrid.setSelectionModel(new Slick.RowSelectionModel());
-        provenanceGrid.registerPlugin(new Slick.AutoTooltips());
-
-        // initialize the grid sorting
-        provenanceGrid.setSortColumn('eventTime', false);
-        provenanceGrid.onSort.subscribe(function (e, args) {
-            sort({
-                columnId: args.sortCol.field,
-                sortAsc: args.sortAsc
-            }, provenanceData);
-        });
+            // determine if the this page is in the shell
+            var isInShell = (top !== window);
 
-        // configure a click listener
-        provenanceGrid.onClick.subscribe(function (e, args) {
-            var target = $(e.target);
+            // define how the column is formatted
+            var showLineageFormatter = function (row, cell, value, columnDef, dataContext) {
+                var markup = '';
 
-            // get the node at this row
-            var item = provenanceData.getItem(args.row);
+                // conditionally include the cluster node id
+                if (common.SUPPORTS_SVG) {
+                    markup += '<div title="Show Lineage" class="pointer show-lineage icon icon-lineage" style="margin-right: 3px;"></div>';
+                }
 
-            // determine the desired action
-            if (provenanceGrid.getColumns()[args.cell].id === 'actions') {
-                if (target.hasClass('show-lineage')) {
-                    provenanceLineageCtrl.showLineage(item.flowFileUuid, item.eventId.toString(), item.clusterNodeId, provenanceTableCtrl);
-                } else if (target.hasClass('go-to')) {
-                    goTo(item);
+                // conditionally support going to the component
+                var isRemotePort = dataContext.componentType === 'Remote Input Port' || dataContext.componentType === 'Remote Output Port';
+                if (isInShell && common.isDefinedAndNotNull(dataContext.groupId) && isRemotePort === false) {
+                    markup += '<div class="pointer go-to fa fa-long-arrow-right" title="Go To"></div>';
                 }
-            } else if (provenanceGrid.getColumns()[args.cell].id === 'moreDetails') {
-                if (target.hasClass('show-event-details')) {
-                    provenanceTableCtrl.showEventDetails(item.eventId, item.clusterNodeId);
+
+                return markup;
+            };
+
+            // initialize the provenance table
+            var provenanceColumns = [
+                {
+                    id: 'moreDetails',
+                    name: '&nbsp;',
+                    sortable: false,
+                    resizable: false,
+                    formatter: moreDetailsFormatter,
+                    width: 50,
+                    maxWidth: 50
+                },
+                {
+                    id: 'eventTime',
+                    name: 'Date/Time',
+                    field: 'eventTime',
+                    sortable: true,
+                    defaultSortAsc: false,
+                    resizable: true
+                },
+                {
+                    id: 'eventType',
+                    name: 'Type',
+                    field: 'eventType',
+                    sortable: true,
+                    resizable: true
+                },
+                {
+                    id: 'flowFileUuid',
+                    name: 'FlowFile Uuid',
+                    field: 'flowFileUuid',
+                    sortable: true,
+                    resizable: true
+                },
+                {
+                    id: 'fileSize',
+                    name: 'Size',
+                    field: 'fileSize',
+                    sortable: true,
+                    defaultSortAsc: false,
+                    resizable: true
+                },
+                {
+                    id: 'componentName',
+                    name: 'Component Name',
+                    field: 'componentName',
+                    sortable: true,
+                    resizable: true,
+                    formatter: valueFormatter
+                },
+                {
+                    id: 'componentType',
+                    name: 'Component Type',
+                    field: 'componentType',
+                    sortable: true,
+                    resizable: true
                 }
+            ];
+
+            // conditionally show the cluster node identifier
+            if (isClustered) {
+                provenanceColumns.push({
+                    id: 'clusterNodeAddress',
+                    name: 'Node',
+                    field: 'clusterNodeAddress',
+                    sortable: true,
+                    resizable: true
+                });
             }
-        });
-
-        // wire up the dataview to the grid
-        provenanceData.onRowCountChanged.subscribe(function (e, args) {
-            provenanceGrid.updateRowCount();
-            provenanceGrid.render();
-
-            // update the total number of displayed events if necessary
-            $('#displayed-events').text(nf.Common.formatInteger(args.current));
-        });
-        provenanceData.onRowsChanged.subscribe(function (e, args) {
-            provenanceGrid.invalidateRows(args.rows);
-            provenanceGrid.render();
-        });
-
-        // hold onto an instance of the grid
-        $('#provenance-table').data('gridInstance', provenanceGrid);
-
-        // initialize the number of displayed items
-        $('#displayed-events').text('0');
-        $('#total-events').text('0');
-    };
 
-    /**
-     * Applies the filter found in the filter expression text field.
-     */
-    var applyFilter = function () {
-        // get the dataview
-        var provenanceGrid = $('#provenance-table').data('gridInstance');
+            // conditionally show the action column
+            if (common.SUPPORTS_SVG || isInShell) {
+                provenanceColumns.push({
+                    id: 'actions',
+                    name: '&nbsp;',
+                    formatter: showLineageFormatter,
+                    resizable: false,
+                    sortable: false,
+                    width: 50,
+                    maxWidth: 50
+                });
+            }
 
-        // ensure the grid has been initialized
-        if (nf.Common.isDefinedAndNotNull(provenanceGrid)) {
-            var provenanceData = provenanceGrid.getData();
+            var provenanceOptions = {
+                forceFitColumns: true,
+                enableTextSelectionOnCells: true,
+                enableCellNavigation: true,
+                enableColumnReorder: false,
+                autoEdit: false,
+                multiSelect: false,
+                rowHeight: 24
+            };
 
-            // update the search criteria
+            // create the remote model
+            var provenanceData = new Slick.Data.DataView({
+                inlineFilters: false
+            });
+            provenanceData.setItems([]);
             provenanceData.setFilterArgs({
-                searchString: getFilterText(),
-                property: $('#provenance-filter-type').combo('getSelectedOption').value
+                searchString: '',
+                property: 'name'
             });
-            provenanceData.refresh();
-        }
-    };
+            provenanceData.setFilter(filter);
 
-    /**
-     * Get the text out of the filter field. If the filter field doesn't
-     * have any text it will contain the text 'filter list' so this method
-     * accounts for that.
-     */
-    var getFilterText = function () {
-        return $('#provenance-filter').val();
-    };
+            // initialize the sort
+            sort({
+                columnId: 'eventTime',
+                sortAsc: false
+            }, provenanceData);
 
-    /**
-     * Performs the provenance filtering.
-     *
-     * @param {object} item     The item subject to filtering
-     * @param {object} args     Filter arguments
-     * @returns {Boolean}       Whether or not to include the item
-     */
-    var filter = function (item, args) {
-        if (args.searchString === '') {
-            return true;
-        }
+            // initialize the grid
+            var provenanceGrid = new Slick.Grid('#provenance-table', provenanceData, provenanceColumns, provenanceOptions);
+            provenanceGrid.setSelectionModel(new Slick.RowSelectionModel());
+            provenanceGrid.registerPlugin(new Slick.AutoTooltips());
+
+            // initialize the grid sorting
+            provenanceGrid.setSortColumn('eventTime', false);
+            provenanceGrid.onSort.subscribe(function (e, args) {
+                sort({
+                    columnId: args.sortCol.field,
+                    sortAsc: args.sortAsc
+                }, provenanceData);
+            });
 
-        try {
-            // perform the row filtering
-            var filterExp = new RegExp(args.searchString, 'i');
-        } catch (e) {
-            // invalid regex
-            return false;
-        }
+            // configure a click listener
+            provenanceGrid.onClick.subscribe(function (e, args) {
+                var target = $(e.target);
 
-        return item[args.property].search(filterExp) >= 0;
-    };
+                // get the node at this row
+                var item = provenanceData.getItem(args.row);
 
-    /**
-     * Sorts the data according to the sort details.
-     *
-     * @param {type} sortDetails
-     * @param {type} data
-     */
-    var sort = function (sortDetails, data) {
-        // defines a function for sorting
-        var comparer = function (a, b) {
-            if (sortDetails.columnId === 'eventTime') {
-                var aTime = nf.Common.parseDateTime(a[sortDetails.columnId]).getTime();
-                var bTime = nf.Common.parseDateTime(b[sortDetails.columnId]).getTime();
-                if (aTime === bTime) {
-                    return a['id'] - b['id'];
-                } else {
-                    return aTime - bTime;
-                }
-            } else if (sortDetails.columnId === 'fileSize') {
-                var aSize = nf.Common.parseSize(a[sortDetails.columnId]);
-                var bSize = nf.Common.parseSize(b[sortDetails.columnId]);
-                if (aSize === bSize) {
-                    return a['id'] - b['id'];
-                } else {
-                    return aSize - bSize;
-                }
-            } else {
-                var aString = nf.Common.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : '';
-                var bString = nf.Common.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : '';
-                if (aString === bString) {
-                    return a['id'] - b['id'];
-                } else {
-                    return aString === bString ? 0 : aString > bString ? 1 : -1;
+                // determine the desired action
+                if (provenanceGrid.getColumns()[args.cell].id === 'actions') {
+                    if (target.hasClass('show-lineage')) {
+                        provenanceLineageCtrl.showLineage(item.flowFileUuid, item.eventId.toString(), item.clusterNodeId, provenanceTableCtrl);
+                    } else if (target.hasClass('go-to')) {
+                        goTo(item);
+                    }
+                } else if (provenanceGrid.getColumns()[args.cell].id === 'moreDetails') {
+                    if (target.hasClass('show-event-details')) {
+                        provenanceTableCtrl.showEventDetails(item.eventId, item.clusterNodeId);
+                    }
                 }
-            }
-        };
+            });
 
-        // perform the sort
-        data.sort(comparer, sortDetails.sortAsc);
-    };
+            // wire up the dataview to the grid
+            provenanceData.onRowCountChanged.subscribe(function (e, args) {
+                provenanceGrid.updateRowCount();
+                provenanceGrid.render();
 
-    /**
-     * Submits a new provenance query.
-     *
-     * @argument {object} provenance The provenance query
-     * @returns {deferred}
-     */
-    var submitProvenance = function (provenance) {
-        var provenanceEntity = {
-            'provenance': {
-                'request': $.extend({
-                    maxResults: config.maxResults,
-                    summarize: true,
-                    incrementalResults: false
-                }, provenance)
-            }
-        };
+                // update the total number of displayed events if necessary
+                $('#displayed-events').text(common.formatInteger(args.current));
+            });
+            provenanceData.onRowsChanged.subscribe(function (e, args) {
+                provenanceGrid.invalidateRows(args.rows);
+                provenanceGrid.render();
+            });
 
-        // submit the provenance request
-        return $.ajax({
-            type: 'POST',
-            url: config.urls.provenance,
-            data: JSON.stringify(provenanceEntity),
-            dataType: 'json',
-            contentType: 'application/json'
-        }).fail(nf.Common.handleAjaxError);
-    };
+            // hold onto an instance of the grid
+            $('#provenance-table').data('gridInstance', provenanceGrid);
 
-    /**
-     * Gets the results from the provenance query for the specified id.
-     *
-     * @param {object} provenance
-     * @returns {deferred}
-     */
-    var getProvenance = function (provenance) {
-        var url = provenance.uri;
-        if (nf.Common.isDefinedAndNotNull(provenance.request.clusterNodeId)) {
-            url += '?' + $.param({
-                    clusterNodeId: provenance.request.clusterNodeId,
-                    summarize: true,
-                    incrementalResults: false
-                });
-        } else {
-            url += '?' + $.param({
-                    summarize: true,
-                    incrementalResults: false
-                });
-        }
+            // initialize the number of displayed items
+            $('#displayed-events').text('0');
+            $('#total-events').text('0');
+        };
 
-        return $.ajax({
-            type: 'GET',
-            url: url,
-            dataType: 'json'
-        }).fail(nf.Common.handleAjaxError);
-    };
+        /**
+         * Applies the filter found in the filter expression text field.
+         */
+        var applyFilter = function () {
+            // get the dataview
+            var provenanceGrid = $('#provenance-table').data('gridInstance');
 
-    /**
-     * Cancels the specified provenance query.
-     *
-     * @param {object} provenance
-     * @return {deferred}
-     */
-    var cancelProvenance = function (provenance) {
-        var url = provenance.uri;
-        if (nf.Common.isDefinedAndNotNull(provenance.request.clusterNodeId)) {
-            url += '?' + $.param({
-                    clusterNodeId: provenance.request.clusterNodeId
-                });
-        }
+            // ensure the grid has been initialized
+            if (common.isDefinedAndNotNull(provenanceGrid)) {
+                var provenanceData = provenanceGrid.getData();
 
-        return $.ajax({
-            type: 'DELETE',
-            url: url,
-            dataType: 'json'
-        }).fail(nf.Common.handleAjaxError);
-    };
+                // update the search criteria
+                provenanceData.setFilterArgs({
+                    searchString: getFilterText(),
+                    property: $('#provenance-filter-type').combo('getSelectedOption').value
+                });
+                provenanceData.refresh();
+            }
+        };
 
-    /**
-     * Checks the results of the specified provenance.
-     *
-     * @param {object} provenance
-     */
-    var loadProvenanceResults = function (provenance, provenanceTableCtrl) {
-        var provenanceRequest = provenance.request;
-        var provenanceResults = provenance.results;
-
-        // ensure there are groups specified
-        if (nf.Common.isDefinedAndNotNull(provenanceResults.provenanceEvents)) {
-            var provenanceTable = $('#provenance-table').data('gridInstance');
-            var provenanceData = provenanceTable.getData();
-
-            // set the items
-            provenanceData.setItems(provenanceResults.provenanceEvents);
-            provenanceData.reSort();
-            provenanceTable.invalidate();
-
-            // update the stats last refreshed timestamp
-            $('#provenance-last-refreshed').text(provenanceResults.generated);
-
-            // update the oldest event available
-            $('#oldest-event').html(nf.Common.formatValue(provenanceResults.oldestEvent));
-
-            // record the server offset
-            provenanceTableCtrl.serverTimeOffset = provenanceResults.timeOffset;
-
-            // determines if the specified query is blank (no search terms, start or end date)
-            var isBlankQuery = function (query) {
-                return nf.Common.isUndefinedOrNull(query.startDate) && nf.Common.isUndefinedOrNull(query.endDate) && $.isEmptyObject(query.searchTerms);
-            };
+        /**
+         * Get the text out of the filter field. If the filter field doesn't
+         * have any text it will contain the text 'filter list' so this method
+         * accounts for that.
+         */
+        var getFilterText = function () {
+            return $('#provenance-filter').val();
+        };
 
-            // update the filter message based on the request
-            if (isBlankQuery(provenanceRequest)) {
-                var message = 'Showing the most recent ';
-                if (provenanceResults.totalCount >= config.maxResults) {
-                    message += (nf.Common.formatInteger(config.maxResults) + ' of ' + provenanceResults.total + ' events, please refine the search.');
-                } else {
-                    message += ('events.');
-                }
-                $('#provenance-query-message').text(message);
-                $('#clear-provenance-search').hide();
-            } else {
-                var message = 'Showing ';
-                if (provenanceResults.totalCount >= config.maxResults) {
-                    message += (nf.Common.formatInteger(config.maxResults) + ' of ' + provenanceResults.total + ' events that match the specified query, please refine the search.');
-                } else {
-                    message += ('the events that match the specified query.');
-                }
-                $('#provenance-query-message').text(message);
-                $('#clear-provenance-search').show();
+        /**
+         * Performs the provenance filtering.
+         *
+         * @param {object} item     The item subject to filtering
+         * @param {object} args     Filter arguments
+         * @returns {Boolean}       Whether or not to include the item
+         */
+        var filter = function (item, args) {
+            if (args.searchString === '') {
+                return true;
             }
 
-            // update the total number of events
-            $('#total-events').text(nf.Common.formatInteger(provenanceResults.provenanceEvents.length));
-        } else {
-            $('#total-events').text('0');
-        }
-    };
-
-    /**
-     * Goes to the specified component if possible.
-     *
-     * @argument {object} item       The event it
-     */
-    var goTo = function (item) {
-        // ensure the component is still present in the flow
-        if (nf.Common.isDefinedAndNotNull(item.groupId)) {
-            // only attempt this if we're within a frame
-            if (top !== window) {
-                // and our parent has canvas utils and shell defined
-                if (nf.Common.isDefinedAndNotNull(parent.nf) && nf.Common.isDefinedAndNotNull(parent.nf.CanvasUtils) && nf.Common.isDefinedAndNotNull(parent.nf.Shell)) {
-                    parent.nf.CanvasUtils.showComponent(item.groupId, item.componentId);
-                    parent.$('#shell-close-button').click();
-                }
+            try {
+                // perform the row filtering
+                var filterExp = new RegExp(args.searchString, 'i');
+            } catch (e) {
+                // invalid regex
+                return false;
             }
-        }
-    };
 
-    function ProvenanceTableCtrl() {
+            return item[args.property].search(filterExp) >= 0;
+        };
 
         /**
-         * The server time offset
+         * Sorts the data according to the sort details.
+         *
+         * @param {type} sortDetails
+         * @param {type} data
          */
-        this.serverTimeOffset = null;
-    }
+        var sort = function (sortDetails, data) {
+            // defines a function for sorting
+            var comparer = function (a, b) {
+                if (sortDetails.columnId === 'eventTime') {
+                    var aTime = common.parseDateTime(a[sortDetails.columnId]).getTime();
+                    var bTime = common.parseDateTime(b[sortDetails.columnId]).getTime();
+                    if (aTime === bTime) {
+                        return a['id'] - b['id'];
+                    } else {
+                        return aTime - bTime;
+                    }
+                } else if (sortDetails.columnId === 'fileSize') {
+                    var aSize = common.parseSize(a[sortDetails.columnId]);
+                    var bSize = common.parseSize(b[sortDetails.columnId]);
+                    if (aSize === bSize) {
+                        return a['id'] - b['id'];
+                    } else {
+                        return aSize - bSize;
+                    }
+                } else {
+                    var aString = common.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : '';
+                    var bString = common.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : '';
+                    if (aString === bString) {
+                        return a['id'] - b['id'];
+                    } else {
+                        return aString === bString ? 0 : aString > bString ? 1 : -1;
+                    }
+                }
+            };
 
-    ProvenanceTableCtrl.prototype = {
-        constructor: ProvenanceTableCtrl,
+            // perform the sort
+            data.sort(comparer, sortDetails.sortAsc);
+        };
 
         /**
-         * Initializes the provenance table. Returns a deferred that will indicate when/if the table has initialized successfully.
+         * Submits a new provenance query.
          *
-         * @param {boolean} isClustered     Whether or not this instance is clustered
+         * @argument {object} provenance The provenance query
+         * @returns {deferred}
          */
-        init: function (isClustered) {
-            var self = this;
-            return $.Deferred(function (deferred) {
-                // handles init failure
-                var failure = function (xhr, status, error) {
-                    deferred.reject();
-                    nf.Common.handleAjaxError(xhr, status, error);
-                };
-
-                // initialize the lineage view
-                provenanceLineageCtrl.init();
+        var submitProvenance = function (provenance) {
+            var provenanceEntity = {
+                'provenance': {
+                    'request': $.extend({
+                        maxResults: config.maxResults,
+                        summarize: true,
+                        incrementalResults: false
+                    }, provenance)
+                }
+            };
 
-                // initialize the table view
-                initDetailsDialog();
-                initProvenanceQueryDialog();
-                initProvenanceTable(isClustered, self);
-                initSearchDialog(isClustered, self).done(function () {
-                    deferred.resolve();
-                }).fail(failure);
-            }).promise();
-        },
+            // submit the provenance request
+            return $.ajax({
+                type: 'POST',
+                url: config.urls.provenance,
+                data: JSON.stringify(provenanceEntity),
+                dataType: 'json',
+                contentType: 'application/json'
+            }).fail(errorHandler.handleAjaxError);
+        };
 
         /**
-         * Update the size of the grid based on its container's current size.
+         * Gets the results from the provenance query for the specified id.
+         *
+         * @param {object} provenance
+         * @returns {deferred}
          */
-        resetTableSize: function () {
-            var provenanceGrid = $('#provenance-table').data('gridInstance');
-            if (nf.Common.isDefinedAndNotNull(provenanceGrid)) {
-                provenanceGrid.resizeCanvas();
+        var getProvenance = function (provenance) {
+            var url = provenance.uri;
+            if (common.isDefinedAndNotNull(provenance.request.clusterNodeId)) {
+                url += '?' + $.param({
+                        clusterNodeId: provenance.request.clusterNodeId,
+                        summarize: true,
+                        incrementalResults: false
+                    });
+            } else {
+                url += '?' + $.param({
+                        summarize: true,
+                        incrementalResults: false
+                    });
             }
-        },
+
+            return $.ajax({
+                type: 'GET',
+                url: url,
+                dataType: 'json'
+            }).fail(errorHandler.handleAjaxError);
+        };
 
         /**
-         * Updates the value of the specified progress bar.
+         * Cancels the specified provenance query.
          *
-         * @param {jQuery}  progressBar
-         * @param {integer} value
-         * @returns {undefined}
+         * @param {object} provenance
+         * @return {deferred}
          */
-        updateProgress: function (progressBar, value) {
-            // remove existing labels
-            progressBar.find('div.progress-label').remove();
-            progressBar.find('md-progress-linear').remove();
+        var cancelProvenance = function (provenance) {
+            var url = provenance.uri;
+            if (common.isDefinedAndNotNull(provenance.request.clusterNodeId)) {
+                url += '?' + $.param({
+                        clusterNodeId: provenance.request.clusterNodeId
+                    });
+            }
 
-            // update the progress bar
-            var label = $('<div class="progress-label"></div>').text(value + '%');
-            (nf.ng.Bridge.injector.get('$compile')($('<md-progress-linear ng-cloak ng-value="' + value + '" class="md-hue-2" md-mode="determinate" aria-label="Progress"></md-progress-linear>'))(nf.ng.Bridge.rootScope)).appendTo(progressBar);
-            progressBar.append(label);
-        },
+            return $.ajax({
+                type: 'DELETE',
+                url: url,
+                dataType: 'json'
+            }).fail(errorHandler.handleAjaxError);
+        };
 
         /**
-         * Loads the provenance table with events according to the specified optional
-         * query. If not query is specified or it is empty, the most recent entries will
-         * be returned.
+         * Checks the results of the specified provenance.
          *
-         * @param {object} query
+         * @param {object} provenance
          */
-        loadProvenanceTable: function (query) {
-            var self = this;
-            var provenanceProgress = $('#provenance-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 provenance = null;
-            var provenanceTimer = null;
-
-            // update the progress bar value
-            self.updateProgress(provenanceProgress, 0);
-
-            // show the 'searching...' dialog
-            $('#provenance-query-dialog').modal('setButtonModel', [{
-                buttonText: 'Cancel',
-                color: {
-                    base: '#E3E8EB',
-                    hover: '#C7D2D7',
-                    text: '#004849'
-                },
-                handler: {
-                    click: function () {
-                        cancelled = true;
+        var loadProvenanceResults = function (provenance, provenanceTableCtrl) {
+            var provenanceRequest = provenance.request;
+            var provenanceResults = provenance.results;
 
-                        // we are waiting for the next poll attempt
-                        if (provenanceTimer !== null) {
-                            // cancel it
-                            clearTimeout(provenanceTimer);
+            // ensure there are groups specified
+            if (common.isDefinedAndNotNull(provenanceResults.provenanceEvents)) {
+                var provenanceTable = $('#provenance-table').data('gridInstance');
+                var provenanceData = provenanceTable.getData();
 
-                            // cancel the provenance
-                            closeDialog();
-                        }
+                // set the items
+                provenanceData.setItems(provenanceResults.provenanceEvents);
+                provenanceData.reSort();
+                provenanceTable.invalidate();
+
+                // update the stats last refreshed timestamp
+                $('#provenance-last-refreshed').text(provenanceResults.generated);
+
+                // update the oldest event available
+                $('#oldest-event').html(common.formatValue(provenanceResults.oldestEvent));
+
+                // record the server offset
+                provenanceTableCtrl.serverTimeOffset = provenanceResults.timeOffset;
+
+                // determines if the specified query is blank (no search terms, start or end date)
+                var isBlankQuery = function (query) {
+                    return common.isUndefinedOrNull(query.startDate) && common.isUndefinedOrNull(query.endDate) && $.isEmptyObject(query.searchTerms);
+                };
+
+                // update the filter message based on the request
+                if (isBlankQuery(provenanceRequest)) {
+                    var message = 'Showing the most recent ';
+                    if (provenanceResults.totalCount >= config.maxResults) {
+                        message += (common.formatInteger(config.maxResults) + ' of ' + provenanceResults.total + ' events, please refine the search.');
+                    } else {
+                        message += ('events.');
+                    }
+                    $('#provenance-query-message').text(message);
+                    $('#clear-provenance-search').hide();
+                } else {
+                    var message = 'Showing ';
+                    if (provenanceResults.totalCount >= config.maxResults) {
+                        message += (common.formatInteger(config.maxResults) + ' of ' + provenanceResults.total + ' events that match the specified query, please refine the search.');
+                    } else {
+                        message += ('the events that match the specified query.');
                     }
+                    $('#provenance-query-message').text(message);
+                    $('#clear-provenance-search').show();
                 }
-            }]).modal('show');
-
-            // -----------------------------
-            // determine the provenance query
-            // -----------------------------
-
-            // handle the specified query appropriately
-            if (nf.Common.isDefinedAndNotNull(query)) {
-                // store the last query performed
-                cachedQuery = query;
-            } else if (!$.isEmptyObject(cachedQuery)) {
-                // use the last query performed
-                query = cachedQuery;
+
+                // update the total number of events
+                $('#total-events').text(common.formatInteger(provenanceResults.provenanceEvents.length));
             } else {
-                // don't use a query
-                query = {};
+                $('#total-events').text('0');
             }
+        };
 
-            // 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(provenance)) {
-                    cancelProvenance(provenance);
+        /**
+         * Goes to the specified component if possible.
+         *
+         * @argument {object} item       The event it
+         */
+        var goTo = function (item) {
+            // ensure the component is still present in the flow
+            if (common.isDefinedAndNotNull(item.groupId)) {
+                // only attempt this if we're within a frame
+                if (top !== window) {
+                    // and our parent has canvas utils and shell defined
+                    if (common.isDefinedAndNotNull(parent.nf) && common.isDefinedAndNotNull(parent.nf.CanvasUtils) && common.isDefinedAndNotNull(parent.nf.Shell)) {
+                        parent.nf.CanvasUtils.showComponent(item.groupId, item.componentId);
+                        parent.$('#shell-close-button').click();
+                    }
                 }
+            }
+        };
 
-                // close the dialog
-                $('#provenance-query-dialog').modal('hide');
-            };
+        function ProvenanceTableCtrl() {
 
-            // polls the server for the status of the provenance
-            var pollProvenance = function () {
-                getProvenance(provenance).done(function (response) {
-                    // update the provenance
-                    provenance = response.provenance;
+            /**
 
-                    // process the provenance
-                    processProvenanceResponse();
-                }).fail(closeDialog);
-            };
+             * The server time offset
+             */
+            this.serverTimeOffset = null;
+        }
+
+        ProvenanceTableCtrl.prototype = {
+            constructor: ProvenanceTableCtrl,
+
+            /**
+             * Initializes the provenance table. Returns a deferred that will indicate when/if the table has initialized successfully.
+             *
+             * @param {boolean} isClustered     Whether or not this instance is clustered
+             */
+            init: function (isClustered) {
+                var provenanceTableCtrl = this;
+                return $.Deferred(function (deferred) {
+                    // handles init failure
+                    var failure = function (xhr, status, error) {
+                        deferred.reject();
+                        errorHandler.handleAjaxError(xhr, status, error);
+                    };
+
+                    // initialize the lineage view
+                    provenanceLineageCtrl.init();
+
+                    // initialize the table view
+                    initDetailsDialog();
+                    initProvenanceQueryDialog();
+                    initProvenanceTable(isClustered, provenanceTableCtrl);
+                    initSearchDialog(isClustered, provenanceTableCtrl).done(function () {
+                        deferred.resolve();
+                    }).fail(failure);
+

<TRUNCATED>