| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* global 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, nfCommon, nfDialog, nfErrorHandler, nfStorage, nfNgBridge) { |
| return (nf.ng.ProvenanceTable = factory($, Slick, nfCommon, nfDialog, nfErrorHandler, nfStorage, nfNgBridge)); |
| }); |
| } 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, nfCommon, nfDialog, nfErrorHandler, nfStorage, nfNgBridge) { |
| 'use strict'; |
| |
| var nfProvenanceTable = function (provenanceLineageCtrl) { |
| '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/build/d3.min.js', |
| lineageScript: 'js/nf/provenance/nf-provenance-lineage.js' |
| } |
| }; |
| |
| /** |
| * 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); |
| |
| var parameters = {}; |
| |
| // conditionally include the cluster node id |
| var clusterNodeId = $('#provenance-event-cluster-node-id').text(); |
| if (!nfCommon.isBlank(clusterNodeId)) { |
| parameters['clusterNodeId'] = clusterNodeId; |
| } |
| |
| // open the url |
| if ($.isEmptyObject(parameters)) { |
| window.open(dataUri); |
| } else { |
| window.open(dataUri + '?' + $.param(parameters)); |
| } |
| }; |
| |
| /** |
| * 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); |
| |
| var dataUriParameters = {}; |
| |
| // conditionally include the cluster node id |
| var clusterNodeId = $('#provenance-event-cluster-node-id').text(); |
| if (!nfCommon.isBlank(clusterNodeId)) { |
| dataUriParameters['clusterNodeId'] = clusterNodeId; |
| } |
| |
| // include parameters if necessary |
| if ($.isEmptyObject(dataUriParameters) === false) { |
| dataUri = dataUri + '?' + $.param(dataUriParameters); |
| } |
| |
| // open the content viewer |
| var contentViewerUrl = $('#nifi-content-viewer-url').text(); |
| |
| // 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 += '&'; |
| } |
| |
| var contentViewerParameters = { |
| 'ref': dataUri |
| }; |
| |
| // 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: { |
| 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 () { |
| nfCommon.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 (nfCommon.isContentViewConfigured()) { |
| // input view |
| $('#input-content-view').on('click', function () { |
| viewContent('input'); |
| }); |
| |
| // output view |
| $('#output-content-view').on('click', function () { |
| viewContent('output'); |
| }); |
| } |
| |
| // handle the replay and downloading |
| $('#replay-content').on('click', function () { |
| var replayEntity = { |
| 'eventId': $('#provenance-event-id').text() |
| }; |
| |
| // conditionally include the cluster node id |
| var clusterNodeId = $('#provenance-event-cluster-node-id').text(); |
| if (!nfCommon.isBlank(clusterNodeId)) { |
| replayEntity['clusterNodeId'] = clusterNodeId; |
| } |
| |
| $.ajax({ |
| type: 'POST', |
| url: config.urls.replays, |
| data: JSON.stringify(replayEntity), |
| dataType: 'json', |
| contentType: 'application/json' |
| }).done(function (response) { |
| nfDialog.showOkDialog({ |
| headerText: 'Provenance', |
| dialogContent: 'Successfully submitted replay request.' |
| }); |
| }).fail(nfErrorHandler.handleAjaxError); |
| |
| $('#event-details-dialog').modal('hide'); |
| }); |
| |
| // show the replay panel |
| $('#replay-details').show(); |
| }; |
| |
| /** |
| * 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; |
| }); |
| |
| // add each node |
| $.each(nodeResults, function (_, nodeResult) { |
| searchableOptions.push({ |
| text: nodeResult.address, |
| value: nodeResult.id |
| }); |
| }); |
| |
| // populate the combo |
| $('#provenance-search-location').combo({ |
| options: searchableOptions |
| }); |
| }).fail(nfErrorHandler.handleAjaxError); |
| |
| // 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: '#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); |
| } |
| } |
| }, |
| { |
| buttonText: 'Cancel', |
| color: { |
| base: '#E3E8EB', |
| hover: '#C7D2D7', |
| text: '#004849' |
| }, |
| handler: { |
| click: function () { |
| $('#provenance-search-dialog').modal('hide'); |
| } |
| } |
| }] |
| }); |
| |
| 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); |
| }); |
| }); |
| }; |
| |
| /** |
| * Initializes the provenance query dialog. |
| */ |
| var initProvenanceQueryDialog = function () { |
| // initialize the dialog |
| $('#provenance-query-dialog').modal({ |
| scrollableContentStyle: 'scrollable', |
| headerText: 'Searching provenance events...' |
| }); |
| }; |
| |
| /** |
| * 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 setting-name"></div>').text(field.label).appendTo(searchableField); |
| $('<div class="searchable-field-value"><input type="text" class="searchable-field-input"/></div>').appendTo(searchableField); |
| $('<div class="searchable-checkbox-value nf-checkbox checkbox-unchecked"></div>').appendTo(searchableField); |
| $('<div class="searchable-checkbox-label nf-checkbox-label">Exclude from search results</div>').appendTo(searchableField); |
| $('<div class="searchable-checkbox-tooltip fa fa-question-circle" title="Query for all values except what is entered."></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'); |
| } |
| |
| // 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()); |
| var searchDetails = {}; |
| |
| // if the field isn't blank include it in the search |
| if (!nfCommon.isBlank(searchValue)) { |
| searchCriteria[fieldId] = searchDetails; |
| searchDetails["value"] = searchValue; |
| var inverse = "inverse"; |
| var searchInverse = searchableField.find('div.searchable-checkbox-value').hasClass('checkbox-checked'); |
| if (searchInverse == true) |
| { |
| searchDetails[inverse] = true; |
| } else { |
| searchDetails[inverse] = false; |
| } |
| } |
| }); |
| 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' |
| }); |
| } |
| |
| // initialize the filter combo |
| $('#provenance-filter-type').combo({ |
| options: filterOptions, |
| select: function (option) { |
| 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(''); |
| |
| // if we are clustered reset the selected option |
| if (isClustered) { |
| $('#provenance-search-location').combo('setSelectedOption', { |
| text: 'cluster' |
| }); |
| } |
| |
| // 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'); |
| }); |
| |
| // 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>'; |
| }; |
| |
| // define how general values are formatted |
| var valueFormatter = function (row, cell, value, columnDef, dataContext) { |
| return nfCommon.formatValue(value); |
| }; |
| |
| // determine if the this page is in the shell |
| var isInShell = (top !== window); |
| |
| // define how the column is formatted |
| var showLineageFormatter = function (row, cell, value, columnDef, dataContext) { |
| var markup = ''; |
| |
| // conditionally include the cluster node id |
| if (nfCommon.SUPPORTS_SVG) { |
| markup += '<div title="Show Lineage" class="pointer show-lineage icon icon-lineage"></div>'; |
| } |
| |
| // conditionally support going to the component |
| var isRemotePort = dataContext.componentType === 'Remote Input Port' || dataContext.componentType === 'Remote Output Port'; |
| if (isInShell && nfCommon.isDefinedAndNotNull(dataContext.groupId) && isRemotePort === false) { |
| markup += '<div class="pointer go-to fa fa-long-arrow-right" title="Go To"></div>'; |
| } |
| |
| return markup; |
| }; |
| |
| // initialize the provenance table |
| var provenanceColumns = [ |
| { |
| id: 'moreDetails', |
| name: ' ', |
| sortable: false, |
| resizable: false, |
| formatter: moreDetailsFormatter, |
| width: 50, |
| maxWidth: 50 |
| }, |
| { |
| id: 'eventTime', |
| name: 'Date/Time', |
| field: 'eventTime', |
| sortable: true, |
| defaultSortAsc: false, |
| resizable: true, |
| formatter: nfCommon.genericValueFormatter |
| }, |
| { |
| id: 'eventType', |
| name: 'Type', |
| field: 'eventType', |
| sortable: true, |
| resizable: true, |
| formatter: nfCommon.genericValueFormatter |
| }, |
| { |
| id: 'flowFileUuid', |
| name: 'FlowFile Uuid', |
| field: 'flowFileUuid', |
| sortable: true, |
| resizable: true, |
| formatter: nfCommon.genericValueFormatter |
| }, |
| { |
| id: 'fileSize', |
| name: 'Size', |
| field: 'fileSize', |
| sortable: true, |
| defaultSortAsc: false, |
| resizable: true, |
| formatter: nfCommon.genericValueFormatter |
| }, |
| { |
| id: 'componentName', |
| name: 'Component Name', |
| field: 'componentName', |
| sortable: true, |
| resizable: true, |
| formatter: valueFormatter |
| }, |
| { |
| id: 'componentType', |
| name: 'Component Type', |
| field: 'componentType', |
| sortable: true, |
| resizable: true, |
| formatter: nfCommon.genericValueFormatter |
| } |
| ]; |
| |
| // conditionally show the cluster node identifier |
| if (isClustered) { |
| provenanceColumns.push({ |
| id: 'clusterNodeAddress', |
| name: 'Node', |
| field: 'clusterNodeAddress', |
| sortable: true, |
| resizable: true, |
| formatter: nfCommon.genericValueFormatter |
| }); |
| } |
| |
| // conditionally show the action column |
| if (nfCommon.SUPPORTS_SVG || isInShell) { |
| provenanceColumns.push({ |
| id: 'actions', |
| name: ' ', |
| formatter: showLineageFormatter, |
| resizable: false, |
| sortable: false, |
| width: 50, |
| maxWidth: 50 |
| }); |
| } |
| |
| var provenanceOptions = { |
| forceFitColumns: true, |
| enableTextSelectionOnCells: true, |
| enableCellNavigation: true, |
| enableColumnReorder: false, |
| autoEdit: false, |
| multiSelect: false, |
| rowHeight: 24 |
| }; |
| |
| // 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); |
| }); |
| |
| // configure a click listener |
| provenanceGrid.onClick.subscribe(function (e, args) { |
| var target = $(e.target); |
| |
| // get the node at this row |
| var item = provenanceData.getItem(args.row); |
| |
| // 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); |
| } |
| } |
| }); |
| |
| // 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(nfCommon.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'); |
| |
| // ensure the grid has been initialized |
| if (nfCommon.isDefinedAndNotNull(provenanceGrid)) { |
| var provenanceData = provenanceGrid.getData(); |
| |
| // update the search criteria |
| provenanceData.setFilterArgs({ |
| searchString: getFilterText(), |
| property: $('#provenance-filter-type').combo('getSelectedOption').value |
| }); |
| provenanceData.refresh(); |
| } |
| }; |
| |
| /** |
| * 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(); |
| }; |
| |
| /** |
| * 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; |
| } |
| |
| try { |
| // perform the row filtering |
| var filterExp = new RegExp(args.searchString, 'i'); |
| } catch (e) { |
| // invalid regex |
| return false; |
| } |
| |
| return item[args.property].search(filterExp) >= 0; |
| }; |
| |
| /** |
| * 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 = nfCommon.parseDateTime(a[sortDetails.columnId]).getTime(); |
| var bTime = nfCommon.parseDateTime(b[sortDetails.columnId]).getTime(); |
| if (aTime === bTime) { |
| return a['id'] - b['id']; |
| } else { |
| return aTime - bTime; |
| } |
| } else if (sortDetails.columnId === 'fileSize') { |
| var aSize = nfCommon.parseSize(a[sortDetails.columnId]); |
| var bSize = nfCommon.parseSize(b[sortDetails.columnId]); |
| if (aSize === bSize) { |
| return a['id'] - b['id']; |
| } else { |
| return aSize - bSize; |
| } |
| } else { |
| var aString = nfCommon.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : ''; |
| var bString = nfCommon.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : ''; |
| if (aString === bString) { |
| return a['id'] - b['id']; |
| } else { |
| return aString === bString ? 0 : aString > bString ? 1 : -1; |
| } |
| } |
| }; |
| |
| // perform the sort |
| data.sort(comparer, sortDetails.sortAsc); |
| }; |
| |
| /** |
| * 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) |
| } |
| }; |
| |
| // submit the provenance request |
| return $.ajax({ |
| type: 'POST', |
| url: config.urls.provenance, |
| data: JSON.stringify(provenanceEntity), |
| dataType: 'json', |
| contentType: 'application/json' |
| }).fail(nfErrorHandler.handleAjaxError); |
| }; |
| |
| /** |
| * 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 (nfCommon.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(nfErrorHandler.handleAjaxError); |
| }; |
| |
| /** |
| * Cancels the specified provenance query. |
| * |
| * @param {object} provenance |
| * @return {deferred} |
| */ |
| var cancelProvenance = function (provenance) { |
| var url = provenance.uri; |
| if (nfCommon.isDefinedAndNotNull(provenance.request.clusterNodeId)) { |
| url += '?' + $.param({ |
| clusterNodeId: provenance.request.clusterNodeId |
| }); |
| } |
| |
| return $.ajax({ |
| type: 'DELETE', |
| url: url, |
| dataType: 'json' |
| }).fail(nfErrorHandler.handleAjaxError); |
| }; |
| |
| /** |
| * 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 (nfCommon.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(nfCommon.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 nfCommon.isUndefinedOrNull(query.startDate) && nfCommon.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 += (nfCommon.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 += (nfCommon.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(); |
| } |
| |
| // update the total number of events |
| $('#total-events').text(nfCommon.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 (nfCommon.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 (nfCommon.isDefinedAndNotNull(parent.nf) && nfCommon.isDefinedAndNotNull(parent.nf.CanvasUtils) && nfCommon.isDefinedAndNotNull(parent.nf.Shell)) { |
| parent.nf.CanvasUtils.showComponent(item.groupId, item.componentId); |
| parent.$('#shell-close-button').click(); |
| } |
| } |
| } |
| }; |
| |
| function ProvenanceTableCtrl() { |
| |
| /** |
| |
| * 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(); |
| nfErrorHandler.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); |
| }).promise(); |
| }, |
| |
| /** |
| * Update the size of the grid based on its container's current size. |
| */ |
| resetTableSize: function () { |
| var provenanceGrid = $('#provenance-table').data('gridInstance'); |
| if (nfCommon.isDefinedAndNotNull(provenanceGrid)) { |
| provenanceGrid.resizeCanvas(); |
| } |
| }, |
| |
| /** |
| * Updates the value of the specified progress bar. |
| * |
| * @param {jQuery} progressBar |
| * @param {integer} value |
| * @returns {undefined} |
| */ |
| updateProgress: function (progressBar, value) { |
| // remove existing labels |
| progressBar.find('div.progress-label').remove(); |
| progressBar.find('md-progress-linear').remove(); |
| |
| // update the progress bar |
| var label = $('<div class="progress-label"></div>').text(value + '%'); |
| (nfNgBridge.injector.get('$compile')($('<md-progress-linear ng-cloak ng-value="' + value + '" class="md-hue-2" md-mode="determinate" aria-label="Progress"></md-progress-linear>'))(nfNgBridge.rootScope)).appendTo(progressBar); |
| progressBar.append(label); |
| }, |
| |
| /** |
| * 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. |
| * |
| * @param {object} query |
| */ |
| loadProvenanceTable: function (query) { |
| var provenanceTableCtrl = 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 |
| provenanceTableCtrl.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; |
| |
| // we are waiting for the next poll attempt |
| if (provenanceTimer !== null) { |
| // cancel it |
| clearTimeout(provenanceTimer); |
| |
| // cancel the provenance |
| closeDialog(); |
| } |
| } |
| } |
| }]).modal('show'); |
| |
| // ----------------------------- |
| // determine the provenance query |
| // ----------------------------- |
| |
| // handle the specified query appropriately |
| if (nfCommon.isDefinedAndNotNull(query)) { |
| // store the last query performed |
| cachedQuery = query; |
| } else if (!$.isEmptyObject(cachedQuery)) { |
| // use the last query performed |
| query = cachedQuery; |
| } else { |
| // don't use a query |
| query = {}; |
| } |
| |
| // 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 (nfCommon.isDefinedAndNotNull(provenance)) { |
| cancelProvenance(provenance); |
| } |
| |
| // close the dialog |
| $('#provenance-query-dialog').modal('hide'); |
| }; |
| |
| // 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); |
| }; |
| |
| // processes the provenance |
| var processProvenanceResponse = function () { |
| // if the request was cancelled just ignore the current response |
| if (cancelled === true) { |
| closeDialog(); |
| return; |
| } |
| |
| // update the percent complete |
| provenanceTableCtrl.updateProgress(provenanceProgress, provenance.percentCompleted); |
| |
| // process the results if they are finished |
| if (provenance.finished === true) { |
| // show any errors when the query finishes |
| if (!nfCommon.isEmpty(provenance.results.errors)) { |
| var errors = provenance.results.errors; |
| nfDialog.showOkDialog({ |
| headerText: 'Provenance', |
| dialogContent: nfCommon.formatUnorderedList(errors), |
| }); |
| } |
| |
| // process the results |
| loadProvenanceResults(provenance, provenanceTableCtrl); |
| |
| // hide the dialog |
| closeDialog(); |
| } else { |
| // start the wait to poll again |
| provenanceTimer = setTimeout(function () { |
| // clear the timer since we've been invoked |
| provenanceTimer = null; |
| |
| // poll provenance |
| pollProvenance(); |
| }, 2000); |
| } |
| }; |
| |
| // once the query is submitted wait until its finished |
| submitProvenance(query).done(function (response) { |
| // update the provenance |
| provenance = response.provenance; |
| |
| // process the results, if they are not done wait 1 second before trying again |
| processProvenanceResponse(); |
| }).fail(closeDialog); |
| }, |
| |
| /** |
| * Gets the details for the specified event. |
| * |
| * @param {string} eventId |
| * @param {string} clusterNodeId The id of the node in the cluster where this event/flowfile originated |
| */ |
| getEventDetails: function (eventId, clusterNodeId) { |
| var url; |
| if (nfCommon.isDefinedAndNotNull(clusterNodeId)) { |
| url = config.urls.provenanceEvents + encodeURIComponent(eventId) + '?' + $.param({ |
| clusterNodeId: clusterNodeId |
| }); |
| } else { |
| url = config.urls.provenanceEvents + encodeURIComponent(eventId); |
| } |
| |
| return $.ajax({ |
| type: 'GET', |
| url: url, |
| dataType: 'json' |
| }).fail(nfErrorHandler.handleAjaxError); |
| }, |
| |
| /** |
| * Shows the details for the specified action. |
| * |
| * @param {string} eventId |
| * @param {string} clusterNodeId The id of the node in the cluster where this event/flowfile originated |
| */ |
| showEventDetails: function (eventId, clusterNodeId) { |
| provenanceTableCtrl.getEventDetails(eventId, clusterNodeId).done(function (response) { |
| var event = response.provenanceEvent; |
| |
| // Hide or show dialog tabs as required if base properties are defined |
| var tabs = $('#event-details-tabs').find("li"); |
| $(tabs).each(function(index) { |
| if ((event["attributes"] === undefined && index == 1) || |
| (event["inputContentAvailable"] === undefined && index ==2)) { |
| $(this).hide(); |
| } else { |
| $(this).show(); |
| } |
| }); |
| |
| // ensure the details are selected in case other tabs we're previously selected and have been hidden |
| $(tabs).first().click(); |
| |
| // update the event details |
| $('#provenance-event-id').text(event.eventId); |
| $('#provenance-event-time').html(nfCommon.formatValue(event.eventTime)).ellipsis(); |
| $('#provenance-event-type').html(nfCommon.formatValue(event.eventType)).ellipsis(); |
| $('#provenance-event-flowfile-uuid').html(nfCommon.formatValue(event.flowFileUuid)).ellipsis(); |
| $('#provenance-event-component-id').html(nfCommon.formatValue(event.componentId)).ellipsis(); |
| $('#provenance-event-component-name').html(nfCommon.formatValue(event.componentName)).ellipsis(); |
| $('#provenance-event-component-type').html(nfCommon.formatValue(event.componentType)).ellipsis(); |
| $('#provenance-event-details').html(nfCommon.formatValue(event.details)).ellipsis(); |
| |
| // over the default tooltip with the actual byte count |
| var fileSize = $('#provenance-event-file-size').html(nfCommon.formatValue(event.fileSize)).ellipsis(); |
| fileSize.attr('title', nfCommon.formatInteger(event.fileSizeBytes) + ' bytes'); |
| |
| // sets an duration |
| var setDuration = function (field, value) { |
| if (nfCommon.isDefinedAndNotNull(value)) { |
| if (value === 0) { |
| field.text('< 1ms'); |
| } else { |
| field.text(nfCommon.formatDuration(value)); |
| } |
| } else { |
| field.html('<span class="unset">No value set</span>'); |
| } |
| }; |
| |
| // handle durations |
| setDuration($('#provenance-event-duration'), event.eventDuration); |
| setDuration($('#provenance-lineage-duration'), event.lineageDuration); |
| |
| // formats an event detail |
| var formatEventDetail = function (label, value) { |
| $('<div class="event-detail"></div>').append( |
| $('<div class="detail-name"></div>').text(label)).append( |
| $('<div class="detail-value">' + nfCommon.formatValue(value) + '</div>').ellipsis()).append( |
| $('<div class="clear"></div>')).appendTo('#additional-provenance-details'); |
| }; |
| |
| // conditionally show RECEIVE details |
| if (event.eventType === 'RECEIVE') { |
| formatEventDetail('Source FlowFile Id', event.sourceSystemFlowFileId); |
| formatEventDetail('Transit Uri', event.transitUri); |
| } |
| |
| // conditionally show SEND details |
| if (event.eventType === 'SEND') { |
| formatEventDetail('Transit Uri', event.transitUri); |
| } |
| |
| // conditionally show REMOTE_INVOCATION details |
| if (event.eventType === 'REMOTE_INVOCATION') { |
| formatEventDetail('Transit Uri', event.transitUri); |
| } |
| |
| // conditionally show ADDINFO details |
| if (event.eventType === 'ADDINFO') { |
| formatEventDetail('Alternate Identifier Uri', event.alternateIdentifierUri); |
| } |
| |
| // conditionally show ROUTE details |
| if (event.eventType === 'ROUTE') { |
| formatEventDetail('Relationship', event.relationship); |
| } |
| |
| // conditionally show FETCH details |
| if (event.eventType === 'FETCH') { |
| formatEventDetail('Transit Uri', event.transitUri); |
| } |
| |
| // conditionally show the cluster node identifier |
| if (nfCommon.isDefinedAndNotNull(event.clusterNodeId)) { |
| // save the cluster node id |
| $('#provenance-event-cluster-node-id').text(event.clusterNodeId); |
| |
| // render the cluster node address |
| formatEventDetail('Node Address', event.clusterNodeAddress); |
| } |
| |
| // populate the parent/child flowfile uuids |
| var parentUuids = $('#parent-flowfiles-container'); |
| var childUuids = $('#child-flowfiles-container'); |
| |
| // handle parent flowfiles |
| if (nfCommon.isEmpty(event.parentUuids)) { |
| $('#parent-flowfile-count').text(0); |
| parentUuids.append('<span class="unset">No parents</span>'); |
| } else { |
| $('#parent-flowfile-count').text(event.parentUuids.length); |
| $.each(event.parentUuids, function (_, uuid) { |
| $('<div></div>').text(uuid).appendTo(parentUuids); |
| }); |
| } |
| |
| // handle child flowfiles |
| if (nfCommon.isEmpty(event.childUuids)) { |
| $('#child-flowfile-count').text(0); |
| childUuids.append('<span class="unset">No children</span>'); |
| } else { |
| $('#child-flowfile-count').text(event.childUuids.length); |
| $.each(event.childUuids, function (_, uuid) { |
| $('<div></div>').text(uuid).appendTo(childUuids); |
| }); |
| } |
| |
| // get the attributes container |
| var attributesContainer = $('#attributes-container'); |
| |
| // get any action details |
| $.each(event.attributes, function (_, attribute) { |
| // create the attribute record |
| var attributeRecord = $('<div class="attribute-detail"></div>') |
| .append($('<div class="attribute-name">' + nfCommon.formatValue(attribute.name) + '</div>').ellipsis()) |
| .appendTo(attributesContainer); |
| |
| // add the current value |
| attributeRecord |
| .append($('<div class="attribute-value">' + nfCommon.formatValue(attribute.value) + '</div>').ellipsis()) |
| .append('<div class="clear"></div>'); |
| |
| // show the previous value if the property has changed |
| if (attribute.value !== attribute.previousValue) { |
| if (nfCommon.isDefinedAndNotNull(attribute.previousValue)) { |
| attributeRecord |
| .append($('<div class="modified-attribute-value">' + nfCommon.formatValue(attribute.previousValue) + '<span class="unset"> (previous)</span></div>').ellipsis()) |
| .append('<div class="clear"></div>'); |
| } else { |
| attributeRecord |
| .append($('<div class="unset" style="font-size: 13px; padding-top: 2px;">' + nfCommon.formatValue(attribute.previousValue) + '</div>').ellipsis()) |
| .append('<div class="clear"></div>'); |
| } |
| } else { |
| // mark this attribute as not modified |
| attributeRecord.addClass('attribute-unmodified'); |
| } |
| }); |
| |
| var formatContentValue = function (element, value) { |
| if (nfCommon.isDefinedAndNotNull(value)) { |
| element.removeClass('unset').text(value); |
| } else { |
| element.addClass('unset').text('No value previously set'); |
| } |
| }; |
| |
| // content |
| $('#input-content-header').text('Input Claim'); |
| formatContentValue($('#input-content-container'), event.inputContentClaimContainer); |
| formatContentValue($('#input-content-section'), event.inputContentClaimSection); |
| formatContentValue($('#input-content-identifier'), event.inputContentClaimIdentifier); |
| formatContentValue($('#input-content-offset'), event.inputContentClaimOffset); |
| formatContentValue($('#input-content-bytes'), event.inputContentClaimFileSizeBytes); |
| |
| // input content file size |
| var inputContentSize = $('#input-content-size'); |
| formatContentValue(inputContentSize, event.inputContentClaimFileSize); |
| if (nfCommon.isDefinedAndNotNull(event.inputContentClaimFileSize)) { |
| // over the default tooltip with the actual byte count |
| inputContentSize.attr('title', nfCommon.formatInteger(event.inputContentClaimFileSizeBytes) + ' bytes'); |
| } |
| |
| formatContentValue($('#output-content-container'), event.outputContentClaimContainer); |
| formatContentValue($('#output-content-section'), event.outputContentClaimSection); |
| formatContentValue($('#output-content-identifier'), event.outputContentClaimIdentifier); |
| formatContentValue($('#output-content-offset'), event.outputContentClaimOffset); |
| formatContentValue($('#output-content-bytes'), event.outputContentClaimFileSizeBytes); |
| |
| // output content file size |
| var outputContentSize = $('#output-content-size'); |
| formatContentValue(outputContentSize, event.outputContentClaimFileSize); |
| if (nfCommon.isDefinedAndNotNull(event.outputContentClaimFileSize)) { |
| // over the default tooltip with the actual byte count |
| outputContentSize.attr('title', nfCommon.formatInteger(event.outputContentClaimFileSizeBytes) + ' bytes'); |
| } |
| |
| if (event.inputContentAvailable === true) { |
| $('#input-content-download').show(); |
| |
| if (nfCommon.isContentViewConfigured()) { |
| $('#input-content-view').show(); |
| } else { |
| $('#input-content-view').hide(); |
| } |
| } else { |
| $('#input-content-download').hide(); |
| $('#input-content-view').hide(); |
| } |
| |
| if (event.outputContentAvailable === true) { |
| $('#output-content-download').show(); |
| |
| if (nfCommon.isContentViewConfigured()) { |
| $('#output-content-view').show(); |
| } else { |
| $('#output-content-view').hide(); |
| } |
| } else { |
| $('#output-content-download').hide(); |
| $('#output-content-view').hide(); |
| } |
| |
| if (event.replayAvailable === true) { |
| $('#replay-content, #replay-content-connection').show(); |
| formatContentValue($('#replay-connection-id'), event.sourceConnectionIdentifier); |
| $('#replay-content-message').hide(); |
| } else { |
| $('#replay-content, #replay-content-connection').hide(); |
| $('#replay-content-message').text(event.replayExplanation).show(); |
| } |
| |
| // show the dialog |
| $('#event-details-dialog').modal('show'); |
| }); |
| } |
| } |
| |
| var provenanceTableCtrl = new ProvenanceTableCtrl(); |
| return provenanceTableCtrl; |
| }; |
| |
| return nfProvenanceTable; |
| })); |