mirror of https://github.com/apache/nifi.git
NIFI-730:
- Adding progress bar for showing current status of drop request. - Allowing the user to cancel the current drop request.
This commit is contained in:
parent
a2ae99f899
commit
cad0e7cf0f
|
@ -38,11 +38,14 @@ public class DropRequestDTO {
|
|||
private Boolean finished;
|
||||
|
||||
private Integer currentCount;
|
||||
private String currentSize;
|
||||
private Long currentSize;
|
||||
private String current;
|
||||
private Integer originalCount;
|
||||
private String originalSize;
|
||||
private Long originalSize;
|
||||
private String original;
|
||||
private Integer droppedCount;
|
||||
private String droppedSize;
|
||||
private Long droppedSize;
|
||||
private String dropped;
|
||||
|
||||
private String state;
|
||||
|
||||
|
@ -151,19 +154,33 @@ public class DropRequestDTO {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return the siez of the flow files currently queued.
|
||||
* @return the size of the flow files currently queued in bytes.
|
||||
*/
|
||||
@ApiModelProperty(
|
||||
value = "The size of flow files currently queued."
|
||||
value = "The size of flow files currently queued in bytes."
|
||||
)
|
||||
public String getCurrentSize() {
|
||||
public Long getCurrentSize() {
|
||||
return currentSize;
|
||||
}
|
||||
|
||||
public void setCurrentSize(String currentSize) {
|
||||
public void setCurrentSize(Long currentSize) {
|
||||
this.currentSize = currentSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the count and size of the currently queued flow files.
|
||||
*/
|
||||
@ApiModelProperty(
|
||||
value = "The count and size of flow files currently queued."
|
||||
)
|
||||
public String getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public void setCurrent(String current) {
|
||||
this.current = current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of flow files to be dropped as a result of this request.
|
||||
*/
|
||||
|
@ -179,19 +196,33 @@ public class DropRequestDTO {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return the size of the flow files to be dropped as a result of this request.
|
||||
* @return the size of the flow files to be dropped as a result of this request in bytes.
|
||||
*/
|
||||
@ApiModelProperty(
|
||||
value = "The size of flow files to be dropped as a result of this request."
|
||||
value = "The size of flow files to be dropped as a result of this request in bytes."
|
||||
)
|
||||
public String getOriginalSize() {
|
||||
public Long getOriginalSize() {
|
||||
return originalSize;
|
||||
}
|
||||
|
||||
public void setOriginalSize(String originalSize) {
|
||||
public void setOriginalSize(Long originalSize) {
|
||||
this.originalSize = originalSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the count and size of flow files to be dropped as a result of this request.
|
||||
*/
|
||||
@ApiModelProperty(
|
||||
value = "The count and size of flow files to be dropped as a result of this request."
|
||||
)
|
||||
public String getOriginal() {
|
||||
return original;
|
||||
}
|
||||
|
||||
public void setOriginal(String original) {
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of flow files that have been dropped thus far.
|
||||
*/
|
||||
|
@ -207,19 +238,33 @@ public class DropRequestDTO {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return the size of the flow files that have been dropped thus far.
|
||||
* @return the size of the flow files that have been dropped thus far in bytes.
|
||||
*/
|
||||
@ApiModelProperty(
|
||||
value = "The size of flow files that have been dropped thus far."
|
||||
value = "The size of flow files that have been dropped thus far in bytes."
|
||||
)
|
||||
public String getDroppedSize() {
|
||||
public Long getDroppedSize() {
|
||||
return droppedSize;
|
||||
}
|
||||
|
||||
public void setDroppedSize(String droppedSize) {
|
||||
public void setDroppedSize(Long droppedSize) {
|
||||
this.droppedSize = droppedSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the count and size of the flow files that have been dropped thus far.
|
||||
*/
|
||||
@ApiModelProperty(
|
||||
value = "The count and size of flow files that have been dropped thus far."
|
||||
)
|
||||
public String getDropped() {
|
||||
return dropped;
|
||||
}
|
||||
|
||||
public void setDropped(String dropped) {
|
||||
this.dropped = dropped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current state of the drop request.
|
||||
*/
|
||||
|
|
|
@ -552,8 +552,9 @@ public interface NiFiServiceFacade {
|
|||
* @param groupId group
|
||||
* @param connectionId The ID of the connection
|
||||
* @param dropRequestId The flow file drop request
|
||||
* @return The DropRequest
|
||||
*/
|
||||
void deleteFlowFileDropRequest(String groupId, String connectionId, String dropRequestId);
|
||||
DropRequestDTO deleteFlowFileDropRequest(String groupId, String connectionId, String dropRequestId);
|
||||
|
||||
// ----------------------------------------
|
||||
// InputPort methods
|
||||
|
|
|
@ -810,8 +810,8 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void deleteFlowFileDropRequest(String groupId, String connectionId, String dropRequestId) {
|
||||
connectionDAO.deleteFlowFileDropRequest(groupId, connectionId, dropRequestId);
|
||||
public DropRequestDTO deleteFlowFileDropRequest(String groupId, String connectionId, String dropRequestId) {
|
||||
return dtoFactory.createDropRequestDTO(connectionDAO.deleteFlowFileDropRequest(groupId, connectionId, dropRequestId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1086,7 +1086,8 @@ public class ConnectionResource extends ApplicationResource {
|
|||
}
|
||||
|
||||
// delete the drop request
|
||||
serviceFacade.deleteFlowFileDropRequest(groupId, connectionId, dropRequestId);
|
||||
final DropRequestDTO dropRequest = serviceFacade.deleteFlowFileDropRequest(groupId, connectionId, dropRequestId);
|
||||
dropRequest.setUri(generateResourceUri("controller", "process-groups", groupId, "connections", connectionId, "contents", "drop-requests", dropRequestId));
|
||||
|
||||
// create the revision
|
||||
final RevisionDTO revision = new RevisionDTO();
|
||||
|
@ -1095,6 +1096,7 @@ public class ConnectionResource extends ApplicationResource {
|
|||
// create the response entity
|
||||
final DropRequestEntity entity = new DropRequestEntity();
|
||||
entity.setRevision(revision);
|
||||
entity.setDropRequest(dropRequest);
|
||||
|
||||
return generateOkResponse(entity).build();
|
||||
}
|
||||
|
|
|
@ -312,13 +312,16 @@ public final class DtoFactory {
|
|||
|
||||
final QueueSize dropped = dropRequest.getDroppedSize();
|
||||
dto.setDroppedCount(dropped.getObjectCount());
|
||||
dto.setDroppedSize(FormatUtils.formatDataSize(dropped.getByteCount()));
|
||||
dto.setDroppedSize(dropped.getByteCount());
|
||||
dto.setDropped(FormatUtils.formatCount(dropped.getObjectCount()) + " / " + FormatUtils.formatDataSize(dropped.getByteCount()));
|
||||
|
||||
if (dropRequest.getOriginalSize() != null) {
|
||||
final QueueSize original = dropRequest.getOriginalSize();
|
||||
dto.setOriginalCount(original.getObjectCount());
|
||||
dto.setOriginalSize(FormatUtils.formatDataSize(original.getByteCount()));
|
||||
dto.setPercentCompleted((dropped.getObjectCount() * 100 ) / original.getObjectCount());
|
||||
dto.setOriginalSize(original.getByteCount());
|
||||
dto.setOriginal(FormatUtils.formatCount(original.getObjectCount()) + " / " + FormatUtils.formatDataSize(original.getByteCount()));
|
||||
|
||||
dto.setPercentCompleted((dropped.getObjectCount() * 100) / original.getObjectCount());
|
||||
} else {
|
||||
dto.setPercentCompleted(0);
|
||||
}
|
||||
|
@ -326,7 +329,8 @@ public final class DtoFactory {
|
|||
if (dropRequest.getCurrentSize() != null) {
|
||||
final QueueSize current = dropRequest.getCurrentSize();
|
||||
dto.setCurrentCount(current.getObjectCount());
|
||||
dto.setCurrentSize(FormatUtils.formatDataSize(current.getByteCount()));
|
||||
dto.setCurrentSize(current.getByteCount());
|
||||
dto.setCurrent(FormatUtils.formatCount(current.getObjectCount()) + " / " + FormatUtils.formatDataSize(current.getByteCount()));
|
||||
}
|
||||
|
||||
return dto;
|
||||
|
|
|
@ -134,6 +134,7 @@ public interface ConnectionDAO {
|
|||
* @param groupId group id
|
||||
* @param id The id of the connection
|
||||
* @param dropRequestId The drop request id
|
||||
* @return The drop request
|
||||
*/
|
||||
void deleteFlowFileDropRequest(String groupId, String id, String dropRequestId);
|
||||
DropFlowFileStatus deleteFlowFileDropRequest(String groupId, String id, String dropRequestId);
|
||||
}
|
||||
|
|
|
@ -496,10 +496,10 @@ public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO
|
|||
}
|
||||
|
||||
@Override
|
||||
public void deleteFlowFileDropRequest(String groupId, String connectionId, String dropRequestId) {
|
||||
public DropFlowFileStatus deleteFlowFileDropRequest(String groupId, String connectionId, String dropRequestId) {
|
||||
final Connection connection = locateConnection(groupId, connectionId);
|
||||
final FlowFileQueue queue = connection.getFlowFileQueue();
|
||||
queue.cancelDropFlowFileRequest(dropRequestId);
|
||||
return queue.cancelDropFlowFileRequest(dropRequestId);
|
||||
}
|
||||
|
||||
/* setters */
|
||||
|
|
|
@ -119,6 +119,7 @@
|
|||
<jsp:include page="/WEB-INF/partials/canvas/secure-port-details.jsp"/>
|
||||
<jsp:include page="/WEB-INF/partials/canvas/label-configuration.jsp"/>
|
||||
<jsp:include page="/WEB-INF/partials/canvas/connection-configuration.jsp"/>
|
||||
<jsp:include page="/WEB-INF/partials/canvas/drop-request-status-dialog.jsp"/>
|
||||
<jsp:include page="/WEB-INF/partials/connection-details.jsp"/>
|
||||
<div id="faded-background"></div>
|
||||
<div id="glass-pane"></div>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<%--
|
||||
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.
|
||||
--%>
|
||||
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
|
||||
<div id="drop-request-status-dialog">
|
||||
<div class="dialog-content">
|
||||
<div class="setting">
|
||||
<div class="setting-field">
|
||||
<div id="drop-request-status-message"></div>
|
||||
</div>
|
||||
<div class="setting-field">
|
||||
<div id="drop-request-percent-complete"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -269,6 +269,31 @@ div.go-to-link {
|
|||
height: 195px;
|
||||
}
|
||||
|
||||
#drop-request-status-dialog {
|
||||
display: none;
|
||||
width: 400px;
|
||||
height: 135px;
|
||||
z-index: 1301;
|
||||
}
|
||||
|
||||
#drop-request-percent-complete {
|
||||
width: 378px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#drop-request-percent-complete .ui-progressbar-value {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
div.progress-label {
|
||||
color: #000000;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
width: 378px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
#nf-ok-dialog {
|
||||
z-index: 1302;
|
||||
display: none;
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 785 B |
|
@ -24,6 +24,31 @@ nf.Actions = (function () {
|
|||
controller: '../nifi-api/controller'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the drop request status dialog.
|
||||
*/
|
||||
var initializeDropRequestStatusDialog = function () {
|
||||
// initialize the drop requst progress bar
|
||||
var dropRequestProgressBar = $('#drop-request-percent-complete').progressbar();
|
||||
|
||||
// configure the drop request status dialog
|
||||
$('#drop-request-status-dialog').modal({
|
||||
headerText: 'Emptying queue',
|
||||
overlayBackground: false,
|
||||
handler: {
|
||||
close: function () {
|
||||
// reset the progress bar
|
||||
dropRequestProgressBar.find('div.progress-label').remove();
|
||||
|
||||
// update the progress bar
|
||||
var label = $('<div class="progress-label"></div>').text('0%');
|
||||
dropRequestProgressBar.progressbar('value', 0).append(label);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the resource with the specified data.
|
||||
|
@ -78,6 +103,13 @@ nf.Actions = (function () {
|
|||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Initializes the actions.
|
||||
*/
|
||||
init: function () {
|
||||
initializeDropRequestStatusDialog();
|
||||
},
|
||||
|
||||
/**
|
||||
* Enters the specified process group.
|
||||
*
|
||||
|
@ -855,56 +887,144 @@ nf.Actions = (function () {
|
|||
return;
|
||||
}
|
||||
|
||||
var MAX_DELAY = 4;
|
||||
// prompt the user before emptying the queue
|
||||
nf.Dialog.showYesNoDialog({
|
||||
dialogContent: 'Are you sure you want to empty the queue? All data enqueued at the time of the request will be removed from the dataflow.',
|
||||
overlayBackground: false,
|
||||
yesHandler: function () {
|
||||
// get the connection data
|
||||
var connection = selection.datum();
|
||||
|
||||
var MAX_DELAY = 4;
|
||||
var cancelled = false;
|
||||
var dropRequest = null;
|
||||
var dropRequestTimer = null;
|
||||
|
||||
// process the drop request
|
||||
var processDropRequest = function (dropRequest, nextDelay) {
|
||||
// see if the drop request has completed
|
||||
if (dropRequest.finished === true) {
|
||||
// request is finished so it can be removed
|
||||
deleteDropRequest(dropRequest);
|
||||
} else {
|
||||
// update the UI with the current status of the drop request
|
||||
schedule(dropRequest, nextDelay);
|
||||
}
|
||||
};
|
||||
// updates the progress bar
|
||||
var updateProgress = function (percentComplete) {
|
||||
// remove existing labels
|
||||
var progressBar = $('#drop-request-percent-complete');
|
||||
progressBar.find('div.progress-label').remove();
|
||||
|
||||
// schedule for the next poll iteration
|
||||
var schedule = function (dropRequest, delay) {
|
||||
setTimeout(function () {
|
||||
// update the progress bar
|
||||
var label = $('<div class="progress-label"></div>').text(percentComplete + '%');
|
||||
if (percentComplete > 0) {
|
||||
label.css('margin-top', '-19px');
|
||||
}
|
||||
progressBar.progressbar('value', percentComplete).append(label);
|
||||
};
|
||||
|
||||
// update the button model of the drop request status dialog
|
||||
$('#drop-request-status-dialog').modal('setButtonModel', [{
|
||||
buttonText: 'Cancel',
|
||||
handler: {
|
||||
click: function () {
|
||||
cancelled = true;
|
||||
|
||||
// we are waiting for the next poll attempt
|
||||
if (dropRequestTimer !== null) {
|
||||
// cancel it
|
||||
clearTimeout(dropRequestTimer);
|
||||
|
||||
// cancel the provenance
|
||||
completeDropRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
||||
// completes the drop request by removing it and showing how many flowfiles were deleted
|
||||
var completeDropRequest = function () {
|
||||
if (nf.Common.isDefinedAndNotNull(dropRequest)) {
|
||||
$.ajax({
|
||||
type: 'DELETE',
|
||||
url: dropRequest.uri,
|
||||
dataType: 'json'
|
||||
}).done(function(response) {
|
||||
// report the results of this drop request
|
||||
dropRequest = response.dropRequest;
|
||||
|
||||
// parse the dropped stats to render results
|
||||
var tokens = dropRequest.dropped.split(/ \/ /);
|
||||
var results = $('<div></div>').text('Successfully removed ' + tokens[0] + ' (' + tokens[1] + ') FlowFiles');
|
||||
nf.Dialog.showOkDialog({
|
||||
dialogContent: results,
|
||||
overlayBackground: false
|
||||
});
|
||||
}).always(function() {
|
||||
$('#drop-request-status-dialog').modal('hide');
|
||||
});
|
||||
} else {
|
||||
// nothing was removed
|
||||
nf.Dialog.showYesNoDialog({
|
||||
dialogContent: 'No FlowFiles were removed.',
|
||||
overlayBackground: false
|
||||
});
|
||||
|
||||
// close the dialog
|
||||
$('#drop-request-status-dialog').modal('hide');
|
||||
}
|
||||
};
|
||||
|
||||
// process the drop request
|
||||
var processDropRequest = function (delay) {
|
||||
// update the percent complete
|
||||
updateProgress(dropRequest.percentCompleted);
|
||||
|
||||
// update the status of the drop request
|
||||
$('#drop-request-status-message').text(dropRequest.state);
|
||||
|
||||
// update the current number of enqueued flowfiles
|
||||
if (nf.Common.isDefinedAndNotNull(dropRequest.currentCount)) {
|
||||
connection.status.queued = dropRequest.current;
|
||||
nf.Connection.refresh(connection.id);
|
||||
}
|
||||
|
||||
// close the dialog if the
|
||||
if (dropRequest.finished === true || cancelled === true) {
|
||||
completeDropRequest();
|
||||
} else {
|
||||
// wait delay to poll again
|
||||
dropRequestTimer = setTimeout(function () {
|
||||
// clear the drop request timer
|
||||
dropRequestTimer = null;
|
||||
|
||||
// schedule to poll the status again in nextDelay
|
||||
pollDropRequest(Math.min(MAX_DELAY, delay * 2));
|
||||
}, delay * 1000);
|
||||
}
|
||||
};
|
||||
|
||||
// schedule for the next poll iteration
|
||||
var pollDropRequest = function (nextDelay) {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: dropRequest.uri,
|
||||
dataType: 'json'
|
||||
}).done(function(response) {
|
||||
dropRequest = response.dropRequest;
|
||||
processDropRequest(nextDelay);
|
||||
}).fail(completeDropRequest);
|
||||
};
|
||||
|
||||
// issue the request to delete the flow files
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: dropRequest.uri,
|
||||
type: 'DELETE',
|
||||
url: connection.component.uri + '/contents',
|
||||
dataType: 'json'
|
||||
}).done(function(response) {
|
||||
var dropRequest = response.dropRequest;
|
||||
processDropRequest(dropRequest, Math.min(MAX_DELAY, delay * 2));
|
||||
}).fail(nf.Common.handleAjaxError);
|
||||
}, delay * 1000);
|
||||
};
|
||||
|
||||
// delete the drop request
|
||||
var deleteDropRequest = function (dropRequest) {
|
||||
$.ajax({
|
||||
type: 'DELETE',
|
||||
url: dropRequest.uri,
|
||||
dataType: 'json'
|
||||
}).done(function() {
|
||||
// drop request has been deleted
|
||||
}).fail(nf.Common.handleAjaxError);
|
||||
};
|
||||
|
||||
// get the connection data
|
||||
var connection = selection.datum();
|
||||
|
||||
// issue the request to delete the flow files
|
||||
$.ajax({
|
||||
type: 'DELETE',
|
||||
url: connection.component.uri + '/contents',
|
||||
dataType: 'json'
|
||||
}).done(function(response) {
|
||||
processDropRequest(response.dropRequest, 1);
|
||||
}).fail(nf.Common.handleAjaxError);
|
||||
// initialize the progress bar value
|
||||
updateProgress(0);
|
||||
|
||||
// show the progress dialog
|
||||
$('#drop-request-status-dialog').modal('show');
|
||||
|
||||
// process the drop request
|
||||
dropRequest = response.dropRequest;
|
||||
processDropRequest(1);
|
||||
}).fail(completeDropRequest);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -188,7 +188,7 @@ nf.Canvas = (function () {
|
|||
if (!refreshContainer.is(':visible')) {
|
||||
$('#stats-last-refreshed').addClass('alert');
|
||||
var refreshMessage = "This flow has been modified by '" + revision.lastModifier + "'. Please refresh.";
|
||||
|
||||
|
||||
// update the tooltip
|
||||
var refreshRequiredIcon = $('#refresh-required-icon');
|
||||
if (refreshRequiredIcon.data('qtip')) {
|
||||
|
@ -198,10 +198,10 @@ nf.Canvas = (function () {
|
|||
content: refreshMessage
|
||||
}, nf.CanvasUtils.config.systemTooltipConfig));
|
||||
}
|
||||
|
||||
|
||||
refreshContainer.show();
|
||||
}
|
||||
|
||||
|
||||
// insert the refresh needed text in the settings - if necessary
|
||||
if (!settingsRefreshIcon.is(':visible')) {
|
||||
$('#settings-last-refreshed').addClass('alert');
|
||||
|
@ -333,7 +333,7 @@ nf.Canvas = (function () {
|
|||
'offset': '100%',
|
||||
'stop-color': '#ffffff'
|
||||
});
|
||||
|
||||
|
||||
// define the gradient for the expiration icon
|
||||
var expirationBackground = defs.append('linearGradient')
|
||||
.attr({
|
||||
|
@ -343,7 +343,7 @@ nf.Canvas = (function () {
|
|||
'x2': '0%',
|
||||
'y2': '100%'
|
||||
});
|
||||
|
||||
|
||||
expirationBackground.append('stop')
|
||||
.attr({
|
||||
'offset': '0%',
|
||||
|
@ -397,106 +397,106 @@ nf.Canvas = (function () {
|
|||
// prevent further propagation (to parents and others handlers
|
||||
// on the same element to prevent zoom behavior)
|
||||
d3.event.stopImmediatePropagation();
|
||||
|
||||
|
||||
// prevents the browser from changing to a text selection cursor
|
||||
d3.event.preventDefault();
|
||||
}
|
||||
})
|
||||
.on('mousemove.selection', function () {
|
||||
// update selection box if shift is held down
|
||||
if (d3.event.shiftKey) {
|
||||
// get the selection box
|
||||
var selectionBox = d3.select('rect.selection');
|
||||
if (!selectionBox.empty()) {
|
||||
// get the original position
|
||||
var originalPosition = selectionBox.datum();
|
||||
var position = d3.mouse(canvas.node());
|
||||
|
||||
var d = {};
|
||||
if (originalPosition[0] < position[0]) {
|
||||
d.x = originalPosition[0];
|
||||
d.width = position[0] - originalPosition[0];
|
||||
} else {
|
||||
d.x = position[0];
|
||||
d.width = originalPosition[0] - position[0];
|
||||
.on('mousemove.selection', function () {
|
||||
// update selection box if shift is held down
|
||||
if (d3.event.shiftKey) {
|
||||
// get the selection box
|
||||
var selectionBox = d3.select('rect.selection');
|
||||
if (!selectionBox.empty()) {
|
||||
// get the original position
|
||||
var originalPosition = selectionBox.datum();
|
||||
var position = d3.mouse(canvas.node());
|
||||
|
||||
var d = {};
|
||||
if (originalPosition[0] < position[0]) {
|
||||
d.x = originalPosition[0];
|
||||
d.width = position[0] - originalPosition[0];
|
||||
} else {
|
||||
d.x = position[0];
|
||||
d.width = originalPosition[0] - position[0];
|
||||
}
|
||||
|
||||
if (originalPosition[1] < position[1]) {
|
||||
d.y = originalPosition[1];
|
||||
d.height = position[1] - originalPosition[1];
|
||||
} else {
|
||||
d.y = position[1];
|
||||
d.height = originalPosition[1] - position[1];
|
||||
}
|
||||
|
||||
// update the selection box
|
||||
selectionBox.attr(d);
|
||||
|
||||
// prevent further propagation (to parents)
|
||||
d3.event.stopPropagation();
|
||||
}
|
||||
}
|
||||
})
|
||||
.on('mouseup.selection', function () {
|
||||
// ensure this originated from clicking the canvas, not a component.
|
||||
// when clicking on a component, the event propagation is stopped so
|
||||
// it never reaches the canvas. we cannot do this however on up events
|
||||
// since the drag events break down
|
||||
if (canvasClicked === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (originalPosition[1] < position[1]) {
|
||||
d.y = originalPosition[1];
|
||||
d.height = position[1] - originalPosition[1];
|
||||
} else {
|
||||
d.y = position[1];
|
||||
d.height = originalPosition[1] - position[1];
|
||||
// reset the canvas click flag
|
||||
canvasClicked = false;
|
||||
|
||||
// get the selection box
|
||||
var selectionBox = d3.select('rect.selection');
|
||||
if (!selectionBox.empty()) {
|
||||
var selectionBoundingBox = {
|
||||
x: parseInt(selectionBox.attr('x'), 10),
|
||||
y: parseInt(selectionBox.attr('y'), 10),
|
||||
width: parseInt(selectionBox.attr('width'), 10),
|
||||
height: parseInt(selectionBox.attr('height'), 10)
|
||||
};
|
||||
|
||||
// see if a component should be selected or not
|
||||
d3.selectAll('g.component').classed('selected', function (d) {
|
||||
// consider it selected if its already selected or enclosed in the bounding box
|
||||
return d3.select(this).classed('selected') ||
|
||||
d.component.position.x >= selectionBoundingBox.x && (d.component.position.x + d.dimensions.width) <= (selectionBoundingBox.x + selectionBoundingBox.width) &&
|
||||
d.component.position.y >= selectionBoundingBox.y && (d.component.position.y + d.dimensions.height) <= (selectionBoundingBox.y + selectionBoundingBox.height);
|
||||
});
|
||||
|
||||
// see if a connection should be selected or not
|
||||
d3.selectAll('g.connection').classed('selected', function (d) {
|
||||
// consider all points
|
||||
var points = [d.start].concat(d.bends, [d.end]);
|
||||
|
||||
// determine the bounding box
|
||||
var x = d3.extent(points, function (pt) {
|
||||
return pt.x;
|
||||
});
|
||||
var y = d3.extent(points, function (pt) {
|
||||
return pt.y;
|
||||
});
|
||||
|
||||
// consider it selected if its already selected or enclosed in the bounding box
|
||||
return d3.select(this).classed('selected') ||
|
||||
x[0] >= selectionBoundingBox.x && x[1] <= (selectionBoundingBox.x + selectionBoundingBox.width) &&
|
||||
y[0] >= selectionBoundingBox.y && y[1] <= (selectionBoundingBox.y + selectionBoundingBox.height);
|
||||
});
|
||||
|
||||
// remove the selection box
|
||||
selectionBox.remove();
|
||||
} else if (panning === false) {
|
||||
// deselect as necessary if we are not panning
|
||||
nf.CanvasUtils.getSelection().classed('selected', false);
|
||||
}
|
||||
|
||||
// update the selection box
|
||||
selectionBox.attr(d);
|
||||
|
||||
// prevent further propagation (to parents)
|
||||
d3.event.stopPropagation();
|
||||
}
|
||||
}
|
||||
})
|
||||
.on('mouseup.selection', function () {
|
||||
// ensure this originated from clicking the canvas, not a component.
|
||||
// when clicking on a component, the event propagation is stopped so
|
||||
// it never reaches the canvas. we cannot do this however on up events
|
||||
// since the drag events break down
|
||||
if (canvasClicked === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// reset the canvas click flag
|
||||
canvasClicked = false;
|
||||
|
||||
// get the selection box
|
||||
var selectionBox = d3.select('rect.selection');
|
||||
if (!selectionBox.empty()) {
|
||||
var selectionBoundingBox = {
|
||||
x: parseInt(selectionBox.attr('x'), 10),
|
||||
y: parseInt(selectionBox.attr('y'), 10),
|
||||
width: parseInt(selectionBox.attr('width'), 10),
|
||||
height: parseInt(selectionBox.attr('height'), 10)
|
||||
};
|
||||
|
||||
// see if a component should be selected or not
|
||||
d3.selectAll('g.component').classed('selected', function (d) {
|
||||
// consider it selected if its already selected or enclosed in the bounding box
|
||||
return d3.select(this).classed('selected') ||
|
||||
d.component.position.x >= selectionBoundingBox.x && (d.component.position.x + d.dimensions.width) <= (selectionBoundingBox.x + selectionBoundingBox.width) &&
|
||||
d.component.position.y >= selectionBoundingBox.y && (d.component.position.y + d.dimensions.height) <= (selectionBoundingBox.y + selectionBoundingBox.height);
|
||||
// update the toolbar
|
||||
nf.CanvasToolbar.refresh();
|
||||
});
|
||||
|
||||
// see if a connection should be selected or not
|
||||
d3.selectAll('g.connection').classed('selected', function (d) {
|
||||
// consider all points
|
||||
var points = [d.start].concat(d.bends, [d.end]);
|
||||
|
||||
// determine the bounding box
|
||||
var x = d3.extent(points, function (pt) {
|
||||
return pt.x;
|
||||
});
|
||||
var y = d3.extent(points, function (pt) {
|
||||
return pt.y;
|
||||
});
|
||||
|
||||
// consider it selected if its already selected or enclosed in the bounding box
|
||||
return d3.select(this).classed('selected') ||
|
||||
x[0] >= selectionBoundingBox.x && x[1] <= (selectionBoundingBox.x + selectionBoundingBox.width) &&
|
||||
y[0] >= selectionBoundingBox.y && y[1] <= (selectionBoundingBox.y + selectionBoundingBox.height);
|
||||
});
|
||||
|
||||
// remove the selection box
|
||||
selectionBox.remove();
|
||||
} else if (panning === false) {
|
||||
// deselect as necessary if we are not panning
|
||||
nf.CanvasUtils.getSelection().classed('selected', false);
|
||||
}
|
||||
|
||||
// update the toolbar
|
||||
nf.CanvasToolbar.refresh();
|
||||
});
|
||||
|
||||
// define a function for update the graph dimensions
|
||||
var updateGraphSize = function () {
|
||||
// get the location of the bottom of the graph
|
||||
|
@ -510,7 +510,7 @@ nf.Canvas = (function () {
|
|||
var top = parseInt(canvasContainer.css('top'), 10);
|
||||
var windowHeight = $(window).height();
|
||||
var canvasHeight = (windowHeight - (bottom + top));
|
||||
|
||||
|
||||
// canvas/svg
|
||||
canvasContainer.css({
|
||||
'height': canvasHeight + 'px',
|
||||
|
@ -536,7 +536,7 @@ nf.Canvas = (function () {
|
|||
}
|
||||
}).on('keydown', function (evt) {
|
||||
var isCtrl = evt.ctrlKey || evt.metaKey;
|
||||
|
||||
|
||||
// consider escape, before checking dialogs
|
||||
if (!isCtrl && evt.keyCode === 27) {
|
||||
// esc
|
||||
|
@ -552,7 +552,7 @@ nf.Canvas = (function () {
|
|||
// first consider read only property detail dialog
|
||||
if ($('div.property-detail').is(':visible')) {
|
||||
nf.Common.removeAllPropertyDetailDialogs();
|
||||
|
||||
|
||||
// prevent further bubbling as we're already handled it
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
|
@ -570,7 +570,7 @@ nf.Canvas = (function () {
|
|||
var dialogMax = null;
|
||||
|
||||
// identify the top most cancellable
|
||||
$.each(cancellables, function(_, cancellable) {
|
||||
$.each(cancellables, function (_, cancellable) {
|
||||
var dialog = $(cancellable);
|
||||
var zIndex = dialog.css('zIndex');
|
||||
|
||||
|
@ -615,10 +615,10 @@ nf.Canvas = (function () {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// if a dialog is open, disable canvas shortcuts
|
||||
if ($('.dialog').is(':visible')) {
|
||||
return;
|
||||
|
@ -833,7 +833,7 @@ nf.Canvas = (function () {
|
|||
bulletinIcon.show();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// update controller service and reporting task bulletins
|
||||
nf.Settings.setBulletins(controllerStatus.controllerServiceBulletins, controllerStatus.reportingTaskBulletins);
|
||||
|
||||
|
@ -934,21 +934,17 @@ nf.Canvas = (function () {
|
|||
};
|
||||
|
||||
return {
|
||||
|
||||
CANVAS_OFFSET: 0,
|
||||
|
||||
/**
|
||||
* Determines if the current broswer supports SVG.
|
||||
*/
|
||||
SUPPORTS_SVG: !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect,
|
||||
|
||||
/**
|
||||
* Hides the splash that is displayed while the application is loading.
|
||||
*/
|
||||
hideSplash: function () {
|
||||
$('#splash').fadeOut();
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop polling for revision.
|
||||
*/
|
||||
|
@ -956,7 +952,6 @@ nf.Canvas = (function () {
|
|||
// set polling flag
|
||||
revisionPolling = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the status poller.
|
||||
*/
|
||||
|
@ -964,7 +959,6 @@ nf.Canvas = (function () {
|
|||
// set polling flag
|
||||
statusPolling = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reloads the flow from the server based on the currently specified group id.
|
||||
* To load another group, update nf.Canvas.setGroupId and call nf.Canvas.reload.
|
||||
|
@ -973,7 +967,7 @@ nf.Canvas = (function () {
|
|||
return $.Deferred(function (deferred) {
|
||||
// hide the context menu
|
||||
nf.ContextMenu.hide();
|
||||
|
||||
|
||||
// get the process group to refresh everything
|
||||
var processGroupXhr = reloadProcessGroup(nf.Canvas.getGroupId());
|
||||
var statusXhr = reloadFlowStatus();
|
||||
|
@ -1007,7 +1001,6 @@ nf.Canvas = (function () {
|
|||
});
|
||||
}).promise();
|
||||
},
|
||||
|
||||
/**
|
||||
* Reloads the status.
|
||||
*/
|
||||
|
@ -1021,7 +1014,6 @@ nf.Canvas = (function () {
|
|||
});
|
||||
}).promise();
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize NiFi.
|
||||
*/
|
||||
|
@ -1099,6 +1091,7 @@ nf.Canvas = (function () {
|
|||
nf.GraphControl.init();
|
||||
nf.Search.init();
|
||||
nf.Settings.init();
|
||||
nf.Actions.init();
|
||||
|
||||
// initialize the component behaviors
|
||||
nf.Draggable.init();
|
||||
|
@ -1146,7 +1139,6 @@ nf.Canvas = (function () {
|
|||
}).fail(nf.Common.handleAjaxError);
|
||||
}).fail(nf.Common.handleAjaxError);
|
||||
},
|
||||
|
||||
/**
|
||||
* Defines the gradient colors used to render processors.
|
||||
*
|
||||
|
@ -1155,7 +1147,6 @@ nf.Canvas = (function () {
|
|||
defineProcessorColors: function (colors) {
|
||||
setColors(colors, 'processor');
|
||||
},
|
||||
|
||||
/**
|
||||
* Defines the gradient colors used to render label.
|
||||
*
|
||||
|
@ -1164,7 +1155,6 @@ nf.Canvas = (function () {
|
|||
defineLabelColors: function (colors) {
|
||||
setColors(colors, 'label');
|
||||
},
|
||||
|
||||
/**
|
||||
* Return whether this instance of NiFi is clustered.
|
||||
*
|
||||
|
@ -1173,14 +1163,12 @@ nf.Canvas = (function () {
|
|||
isClustered: function () {
|
||||
return clustered === true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether site to site communications is secure.
|
||||
*/
|
||||
isSecureSiteToSite: function () {
|
||||
return secureSiteToSite;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the group id.
|
||||
*
|
||||
|
@ -1189,14 +1177,12 @@ nf.Canvas = (function () {
|
|||
setGroupId: function (gi) {
|
||||
groupId = gi;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the group id.
|
||||
*/
|
||||
getGroupId: function () {
|
||||
return groupId;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the group name.
|
||||
*
|
||||
|
@ -1205,14 +1191,12 @@ nf.Canvas = (function () {
|
|||
setGroupName: function (gn) {
|
||||
groupName = gn;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the group name.
|
||||
*/
|
||||
getGroupName: function () {
|
||||
return groupName;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the parent group id.
|
||||
*
|
||||
|
@ -1221,16 +1205,14 @@ nf.Canvas = (function () {
|
|||
setParentGroupId: function (pgi) {
|
||||
parentGroupId = pgi;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the parent group id.
|
||||
*/
|
||||
getParentGroupId: function () {
|
||||
return parentGroupId;
|
||||
},
|
||||
|
||||
View: (function () {
|
||||
|
||||
|
||||
/**
|
||||
* Updates component visibility based on their proximity to the screen's viewport.
|
||||
*/
|
||||
|
@ -1297,8 +1279,8 @@ nf.Canvas = (function () {
|
|||
.classed('entering', function () {
|
||||
return visible && !wasVisible;
|
||||
}).classed('leaving', function () {
|
||||
return !visible && wasVisible;
|
||||
});
|
||||
return !visible && wasVisible;
|
||||
});
|
||||
};
|
||||
|
||||
// get the all components
|
||||
|
@ -1385,7 +1367,6 @@ nf.Canvas = (function () {
|
|||
// add the behavior to the canvas and disable dbl click zoom
|
||||
svg.call(behavior).on('dblclick.zoom', null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether or not a component should be rendered based solely on the current scale.
|
||||
*
|
||||
|
@ -1394,7 +1375,6 @@ nf.Canvas = (function () {
|
|||
shouldRenderPerScale: function () {
|
||||
return nf.Canvas.View.scale() >= MIN_SCALE_TO_RENDER;
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates component visibility based on the current translation/scale.
|
||||
*/
|
||||
|
@ -1402,7 +1382,6 @@ nf.Canvas = (function () {
|
|||
updateComponentVisibility();
|
||||
nf.Graph.pan();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets/gets the current translation.
|
||||
*
|
||||
|
@ -1415,7 +1394,6 @@ nf.Canvas = (function () {
|
|||
behavior.translate(translate);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets/gets the current scale.
|
||||
*
|
||||
|
@ -1428,7 +1406,6 @@ nf.Canvas = (function () {
|
|||
behavior.scale(scale);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Zooms in a single zoom increment.
|
||||
*/
|
||||
|
@ -1453,7 +1430,6 @@ nf.Canvas = (function () {
|
|||
height: 1
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Zooms out a single zoom increment.
|
||||
*/
|
||||
|
@ -1478,7 +1454,6 @@ nf.Canvas = (function () {
|
|||
height: 1
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Zooms to fit the entire graph on the canvas.
|
||||
*/
|
||||
|
@ -1525,7 +1500,6 @@ nf.Canvas = (function () {
|
|||
height: canvasHeight / newScale
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Zooms to the actual size (1 to 1).
|
||||
*/
|
||||
|
@ -1574,7 +1548,6 @@ nf.Canvas = (function () {
|
|||
// center as appropriate
|
||||
nf.CanvasUtils.centerBoundingBox(box);
|
||||
},
|
||||
|
||||
/**
|
||||
* Refreshes the view based on the configured translation and scale.
|
||||
*
|
||||
|
|
|
@ -278,11 +278,11 @@ nf.ContextMenu = (function () {
|
|||
};
|
||||
|
||||
/**
|
||||
* Only DFMs can delete flow files from a connection.
|
||||
* Only DFMs can empty a queue.
|
||||
*
|
||||
* @param {selection} selection
|
||||
*/
|
||||
var canDeleteFlowFiles = function (selection) {
|
||||
var canEmptyQueue = function (selection) {
|
||||
return nf.Common.isDFM() && isConnection(selection);
|
||||
};
|
||||
|
||||
|
@ -382,7 +382,7 @@ nf.ContextMenu = (function () {
|
|||
{condition: isCopyable, menuItem: {img: 'images/iconCopy.png', text: 'Copy', action: 'copy'}},
|
||||
{condition: isPastable, menuItem: {img: 'images/iconPaste.png', text: 'Paste', action: 'paste'}},
|
||||
{condition: canMoveToParent, menuItem: {img: 'images/iconMoveToParent.png', text: 'Move to parent group', action: 'moveIntoParent'}},
|
||||
{condition: canDeleteFlowFiles, menuItem: {img: 'images/iconDelete.png', text: 'Empty queue', action: 'emptyQueue'}},
|
||||
{condition: canEmptyQueue, menuItem: {img: 'images/iconEmptyQueue.png', text: 'Empty queue', action: 'emptyQueue'}},
|
||||
{condition: isDeletable, menuItem: {img: 'images/iconDelete.png', text: 'Delete', action: 'delete'}}
|
||||
];
|
||||
|
||||
|
|
Loading…
Reference in New Issue