NIFI-1781:

- Incorporating updated styles to reflect component level authorization.
- Updating canvas for new look and feel.
- This closes #417
This commit is contained in:
Matt Gilman 2016-05-06 13:30:49 -04:00
parent 04c41c0654
commit 9db1def6c6
60 changed files with 2992 additions and 2683 deletions

View File

@ -17,6 +17,7 @@
package org.apache.nifi.web.api.entity;
import org.apache.nifi.web.api.dto.ProcessorDTO;
import org.apache.nifi.web.api.dto.status.ProcessorStatusDTO;
import javax.xml.bind.annotation.XmlRootElement;
@ -27,6 +28,7 @@ import javax.xml.bind.annotation.XmlRootElement;
public class ProcessorEntity extends ComponentEntity {
private ProcessorDTO component;
private ProcessorStatusDTO status;
/**
* The ProcessorDTO that is being serialized.
@ -41,4 +43,16 @@ public class ProcessorEntity extends ComponentEntity {
this.component = component;
}
/**
* The Processor status.
*
* @return status
*/
public ProcessorStatusDTO getStatus() {
return status;
}
public void setStatus(ProcessorStatusDTO status) {
this.status = status;
}
}

View File

@ -634,7 +634,7 @@ public class StatusMerger {
}
public static String prettyPrint(final Integer count, final Long bytes) {
return formatCount(count) + " / " + formatDataSize(bytes);
return formatCount(count) + " (" + formatDataSize(bytes) + ")";
}
}

View File

@ -85,7 +85,7 @@
<md-menu-item layout-align="space-around center">
<a id="bulletin-board-link"
ng-click="appCtrl.serviceProvider.headerCtrl.globalMenuCtrl.bulletinBoard.shell.launch();"><i
class="icon icon-bulletin"></i>Bulletin Board</a>
class="fa fa-sticky-note-o"></i>Bulletin Board</a>
</md-menu-item>
<md-menu-divider></md-menu-divider>
<md-menu-item

View File

@ -32,7 +32,7 @@
<div layout="row" layout-align="end center">
<input id="search-field" type="text"/>
<button id="search-button"><i class="fa fa-search"></i></button>
<button id="bulletin-button"><i class="icon-bulletin"></i></button>
<button id="bulletin-button"><i class="fa fa-sticky-note-o"></i></button>
</div>
</div>
<div id="search-flow-results"></div>

View File

@ -78,7 +78,7 @@
background-color:#728E9B; /*base-color*/
}
#bulletin-button i{
#bulletin-button i.fa {
color: #fff;
font-size:15px;
}

View File

@ -53,16 +53,78 @@ text.unset {
*/
g.component {
font-family: Arial, sans-serif;
font-family: Roboto;
}
g.component rect.body {
fill: #ffffff;
}
g.component rect.body.unauthorized {
fill: #f4f6f7;
}
g.component rect.border.unauthorized {
stroke-width: 1.5;
stroke: #ba554a;
stroke-dasharray: 3,3;
}
g.component.selected rect.border {
stroke: #ffcc00;
stroke-width: 5;
stroke: #004849;
stroke-width: 3;
}
g.funnel.selected rect.border {
stroke-width: 2.5;
text.stats-label {
fill: #262626;
font-size: 12px;
font-family: Roboto Slab;
}
text.stats-value {
fill: #775351;
font-size: 12px;
font-weight: bold;
}
text.stats-value tspan.size, text.stats-value tspan.size {
font-weight: normal;
}
text.stats-info {
fill: #728E9B;
font-size: 12px;
}
text.bulletin-icon {
font-size: 13px;
font-family: FontAwesome;
fill: #ffffff;
}
rect.bulletin-background {
fill: #728e9b;
}
rect.bulletin-background.has-bulletins {
fill: #ba554a;
}
text.process-group-invalid.has-validation-errors {
fill: #ba554a;
}
text.active-thread-count-icon {
font-family: flowfont;
font-size: 12px;
fill: #728e9b;
text-anchor: end;
}
text.active-thread-count {
fill: #775351;
font-size: 12px;
font-weight: bold;
}
/*
@ -88,19 +150,24 @@ image.add-connect {
Processor
*/
text.processor-stats-label {
fill: #fff;
font-size: 11px;
font-weight: bold;
text.processor-name {
fill: #262626;
font-size: 14px;
}
text.processor-stats-value {
font-size: 11px;
text.processor-type {
fill: #728e9b;
font-size: 12px;
}
text.processor-stats-info {
fill: #999999;
font-size: 11px;
text.processor-icon {
fill: #ad9897;
font-family: flowfont;
font-size: 30px;
}
text.run-status-icon {
font-size: 13px;
}
/*
@ -108,7 +175,27 @@ text.processor-stats-info {
*/
g.connection {
font-family: Arial, sans-serif;
font-family: Roboto;
}
g.connection rect.body {
fill: #ffffff;
}
g.connection rect.body.unauthorized {
fill: #f4f6f7;
}
g.connection rect.border.unauthorized {
stroke-width: 1.5;
stroke: #ba554a;
stroke-dasharray: 3,3;
}
g.connection.selected rect.border {
/*stroke: #004849;*/
stroke: #ffcc00;
stroke-width: 3;
}
path.connector {
@ -126,12 +213,6 @@ path.connector.connectable {
stroke-dasharray: 4;
}
g.connection rect.connection-label {
fill: #ffffff;
stroke: #000000;
stroke-width: 2;
}
g.connection path.connection-path {
fill: none;
stroke: #000000;
@ -139,6 +220,21 @@ g.connection path.connection-path {
cursor: pointer;
}
g.connection path.connection-path.unauthorized {
stroke: #ba554a;
stroke-dasharray: 3,3;
}
text.connection-from-run-status, text.connection-to-run-status {
fill: #728e9b;
font-family: FontAwesome;
font-size: 10px;
}
text.connection-from-run-status.is-missing-port, text.connection-to-run-status.is-missing-port {
fill: #ba554a;
}
/* grouped connection */
g.connection.grouped path.connection-path, g.connection.grouped rect.connection-label {
@ -196,12 +292,6 @@ g.connection rect.endpoint {
cursor: pointer;
}
g.connection text.connection-stats-label {
font-size: 11px;
font-weight: bold;
fill: #598599;
}
/* labels */
g.label rect.labelpoint {
@ -218,8 +308,30 @@ g.label.selected rect.labelpoint {
/* funnels */
text.funnel-icon {
fill: #ad9897;
font-family: flowfont;
font-size: 30px;
}
/* ports */
text.port-name {
fill: #262626;
font-size: 14px;
}
text.port-icon {
fill: #ad9897;
font-family: flowfont;
font-size: 30px;
}
text.port-transmission-icon {
font-size: 11px;
fill: #728e9b
}
/* active thread count */
text.active-thread-count {
@ -228,35 +340,55 @@ text.active-thread-count {
/* process groups */
text.process-group-name {
fill: #262626;
font-size: 14px;
}
text.process-group-contents-count {
font-weight: bold;
font-size: 13px;
fill: #294c58;
}
g.process-group.drop rect.border {
stroke: #0000ff;
stroke-width: 3;
}
text.process-group-contents-count {
font-weight: bold;
fill: #294c58;
}
text.process-group-stats-label {
font-size: 11px;
font-weight: bold;
fill: #598599;
}
text.process-group-stats-value {
font-size: 11px;
}
text.process-group-stats-info {
fill: #999999;
font-size: 11px;
text.process-group-contents-icon {
font-size: 13px;
fill: #728e9b;
}
/* remote process group */
text.remote-process-group-name {
fill: #262626;
font-size: 14px;
}
text.remote-process-group-uri {
fill: #004849;
font-size: 12px;
}
text.remote-process-group-transmission-status {
font-size: 13px;
fill: #728e9b
}
text.remote-process-group-transmission-status.has-authorization-errors {
fill: #ba554a;
}
text.remote-process-group-transmission-secure {
font-family: FontAwesome;
font-size: 13px;
fill: #004849;
}
text.remote-process-group-last-refresh {
fill: #cccccc;
font-style: italic;
fill: #728e9b;
text-anchor: end;
}

View File

@ -74,12 +74,13 @@ md-toolbar.md-small .md-toolbar-tools {
#global-menu-content {
font-size: 13px;
padding: 3px 0;
padding: 0px 0;
background-color:rgba(249,250,251,0.97); /*tint base-color 96%*/
border:1px solid #004849; /*link-color*/
border-radius:2px;
box-shadow:0 2px 3px rgba(0,0,0,0.35);
width: 215px;
max-height: inherit;
}
#global-menu-content md-menu-item{

View File

@ -69,7 +69,7 @@
}
@font-face {
font-family: 'Roboto+Slab';
font-family: 'Roboto Slab';
font-style: normal;
font-weight: normal;
src: local('RobotoSlab Regular'),
@ -78,7 +78,7 @@
}
@font-face {
font-family: 'Roboto+Slab';
font-family: 'Roboto Slab';
font-style: normal;
font-weight: bold;
src: local('RobotoSlab Bold'),

View File

@ -152,7 +152,7 @@ nf.Actions = (function () {
url: d.component.uri,
dataType: 'json'
}).done(function (response) {
var remoteProcessGroup = response.remoteProcessGroup;
var remoteProcessGroup = response.component;
// the timestamp has not updated yet, poll again
if (refreshTimestamp === remoteProcessGroup.flowRefreshed) {
@ -163,7 +163,7 @@ nf.Actions = (function () {
// reload the group's connections
var connections = nf.Connection.getComponentConnections(remoteProcessGroup.id);
$.each(connections, function (_, connection) {
nf.Connection.reload(connection);
nf.Connection.reload(connection.component);
});
}
});

View File

@ -405,16 +405,12 @@ nf.CanvasUtils = (function () {
.each(function () {
var bBox = this.getBBox();
d3.select(this).attr('x', function () {
return d.dimensions.width - bBox.width - 4;
return d.dimensions.width - bBox.width - 15;
});
});
// update the background width
selection.select('rect.active-thread-count-background')
.attr('width', function () {
var bBox = activeThreadCount.node().getBBox();
return bBox.width + 8;
})
selection.select('text.active-thread-count-icon')
.attr('x', function () {
var bBox = activeThreadCount.node().getBBox();
@ -423,22 +419,11 @@ nf.CanvasUtils = (function () {
setOffset(bBox.width + 6);
}
return d.dimensions.width - bBox.width - 8;
})
.attr('stroke-dasharray', function() {
var rect = d3.select(this);
var width = parseFloat(rect.attr('width'));
var height = parseFloat(rect.attr('height'));
var dashArray = [];
dashArray.push(0);
dashArray.push(width + height);
dashArray.push(width + height);
return dashArray.join(' ');
return d.dimensions.width - bBox.width - 20;
})
.style('display', 'block');
} else {
selection.selectAll('text.active-thread-count, rect.active-thread-count-background').style('display', 'none');
selection.selectAll('text.active-thread-count, text.active-thread-count-icon').style('display', 'none');
}
},
@ -475,16 +460,8 @@ nf.CanvasUtils = (function () {
// if there are bulletins show them, otherwise hide
if (nf.Common.isDefinedAndNotNull(d.status) && !nf.Common.isEmpty(d.status.bulletins)) {
// update the tooltip
selection.select('image.bulletin-icon')
.style('display', 'block')
selection.select('text.bulletin-icon')
.each(function () {
var bBox = this.getBBox();
var bulletinIcon = d3.select(this);
bulletinIcon.attr('x', function () {
return d.dimensions.width - offset - bBox.width - 4;
});
// if there are bulletins generate a tooltip
tip = getTooltipContainer().append('div')
.attr('id', function () {
@ -505,10 +482,8 @@ nf.CanvasUtils = (function () {
});
// add the tooltip
nf.CanvasUtils.canvasTooltip(tip, bulletinIcon);
nf.CanvasUtils.canvasTooltip(tip, d3.select(this));
});
} else {
selection.selectAll('image.bulletin-icon').style('display', 'none');
}
},

View File

@ -241,7 +241,7 @@ nf.Canvas = (function () {
// create arrow definitions for the various line types
defs.selectAll('marker')
.data(['normal', 'ghost'])
.data(['normal', 'ghost', 'unauthorized'])
.enter().append('marker')
.attr({
'id': function (d) {
@ -256,6 +256,8 @@ nf.Canvas = (function () {
'fill': function (d) {
if (d === 'ghost') {
return '#aaaaaa';
} else if (d === 'unauthorized') {
return '#ba554a';
} else {
return '#000000';
}
@ -264,77 +266,42 @@ nf.Canvas = (function () {
.append('path')
.attr('d', 'M2,3 L0,6 L6,3 L0,0 z');
// define the gradient for the processor stats background
var processGroupStatsBackground = defs.append('linearGradient')
.attr({
'id': 'process-group-stats-background',
'x1': '0%',
'y1': '100%',
'x2': '0%',
'y2': '0%'
});
// filter for drop shadow
var filter = defs.append('filter')
.attr('id', 'component-drop-shadow');
processGroupStatsBackground.append('stop')
.attr({
'offset': '0%',
'stop-color': '#dedede'
});
// blur
filter.append('feGaussianBlur')
.attr('in', 'SourceAlpha')
.attr('stdDeviation', 2)
.attr('result', 'blur');
processGroupStatsBackground.append('stop')
.attr({
'offset': '50%',
'stop-color': '#ffffff'
});
// offset
filter.append('feOffset')
.attr('in', 'blur')
.attr('dx', 0)
.attr('dy', 1)
.attr('result', 'offsetBlur');
processGroupStatsBackground.append('stop')
.attr({
'offset': '100%',
'stop-color': '#dedede'
});
// color/opacity
filter.append('feFlood')
.attr('flood-color', '#000000')
.attr('flood-opacity', 0.25)
.attr('result', 'offsetColor');
// define the gradient for the processor stats background
var processorStatsBackground = defs.append('linearGradient')
.attr({
'id': 'processor-stats-background',
'x1': '0%',
'y1': '100%',
'x2': '0%',
'y2': '0%'
});
// combine
filter.append('feComposite')
.attr('in', 'offsetColor')
.attr('in2', 'offsetBlur')
.attr('operator', 'in')
.attr('result', 'offsetColorBlur');
processorStatsBackground.append('stop')
.attr({
'offset': '0%',
'stop-color': '#6f97ac'
});
processorStatsBackground.append('stop')
.attr({
'offset': '100%',
'stop-color': '#30505c'
});
// define the gradient for the port background
var portBackground = defs.append('linearGradient')
.attr({
'id': 'port-background',
'x1': '0%',
'y1': '100%',
'x2': '0%',
'y2': '0%'
});
portBackground.append('stop')
.attr({
'offset': '0%',
'stop-color': '#aaaaaa'
});
portBackground.append('stop')
.attr({
'offset': '100%',
'stop-color': '#ffffff'
});
// stack the effect under the source graph
var feMerge = filter.append('feMerge');
feMerge.append('feMergeNode')
.attr('in', 'offsetColorBlur');
feMerge.append('feMergeNode')
.attr('in', 'SourceGraphic');
// define the gradient for the expiration icon
var expirationBackground = defs.append('linearGradient')
@ -636,51 +603,6 @@ nf.Canvas = (function () {
}).fail(nf.Common.handleAjaxError);
};
/**
* Sets the colors for the specified type.
*
* @param {array} colors The possible colors
* @param {string} type The component type for these colors
*/
var setColors = function (colors, type) {
var defs = d3.select('defs');
// update processors
var processorSelection = defs.selectAll('linearGradient.' + type + '-background').data(colors, function (d) {
return d;
});
// define the gradient for the processor background
var gradient = processorSelection.enter().append('linearGradient')
.attr({
'id': function (d) {
return type + '-background-' + d;
},
'class': type + '-background',
'x1': '0%',
'y1': '100%',
'x2': '0%',
'y2': '0%'
});
gradient.append('stop')
.attr({
'offset': '0%',
'stop-color': function (d) {
return '#' + d;
}
});
gradient.append('stop')
.attr({
'offset': '100%',
'stop-color': '#ffffff'
});
// remove old processor colors
processorSelection.exit().remove();
};
/**
* Reloads the current status of this flow.
*/
@ -1172,24 +1094,6 @@ nf.Canvas = (function () {
}).fail(nf.Common.handleAjaxError);
},
/**
* Defines the gradient colors used to render processors.
*
* @param {array} colors The colors
*/
defineProcessorColors: function (colors) {
setColors(colors, 'processor');
},
/**
* Defines the gradient colors used to render label.
*
* @param {array} colors The colors
*/
defineLabelColors: function (colors) {
setColors(colors, 'label');
},
/**
* Return whether this instance of NiFi is clustered.
*

View File

@ -248,29 +248,32 @@ nf.ConnectionConfiguration = (function () {
$.ajax({
type: 'GET',
url: config.urls.api + '/process-groups/' + encodeURIComponent(processGroupData.id),
url: config.urls.api + '/flow/process-groups/' + encodeURIComponent(processGroupData.id),
data: {
verbose: true
},
dataType: 'json'
}).done(function (response) {
var processGroup = response.component;
var processGroupContents = processGroup.contents;
var processGroup = response.processGroupFlow;
var processGroupContents = processGroup.flow;
// show the output port options
var options = [];
$.each(processGroupContents.outputPorts, function (i, outputPort) {
if (outputPort.accessPolicy.canRead && outputPort.accessPolicy.canWrite) {
var component = outputPort.component;
options.push({
text: component.name,
value: component.id,
description: nf.Common.escapeHtml(component.comments)
});
}
});
// only proceed if there are output ports
if (!nf.Common.isEmpty(processGroupContents.outputPorts)) {
if (!nf.Common.isEmpty(options)) {
$('#output-port-source').show();
// show the output port options
var options = [];
$.each(processGroupContents.outputPorts, function (i, outputPort) {
options.push({
text: outputPort.name,
value: outputPort.id,
description: nf.Common.escapeHtml(outputPort.comments)
});
});
// sort the options
options.sort(function (a, b) {
return a.text.localeCompare(b.text);
@ -331,7 +334,7 @@ nf.ConnectionConfiguration = (function () {
},
dataType: 'json'
}).done(function (response) {
var remoteProcessGroup = response.remoteProcessGroup;
var remoteProcessGroup = response.component;
var remoteProcessGroupContents = remoteProcessGroup.contents;
// only proceed if there are output ports
@ -473,29 +476,29 @@ nf.ConnectionConfiguration = (function () {
$.ajax({
type: 'GET',
url: config.urls.api + '/process-groups/' + encodeURIComponent(processGroupData.id),
data: {
verbose: true
},
url: config.urls.api + '/flow/process-groups/' + encodeURIComponent(processGroupData.id),
dataType: 'json'
}).done(function (response) {
var processGroup = response.component;
var processGroupContents = processGroup.contents;
var processGroup = response.processGroupFlow;
var processGroupContents = processGroup.flow;
// show the input port options
var options = [];
$.each(processGroupContents.inputPorts, function (i, inputPort) {
if (inputPort.accessPolicy.canRead && inputPort.accessPolicy.canWrite) {
var component = inputPort.component;
options.push({
text: component.name,
value: component.id,
description: nf.Common.escapeHtml(component.comments)
});
}
});
// only proceed if there are output ports
if (!nf.Common.isEmpty(processGroupContents.inputPorts)) {
if (!nf.Common.isEmpty(options)) {
$('#input-port-destination').show();
// show the input port options
var options = [];
$.each(processGroupContents.inputPorts, function (i, inputPort) {
options.push({
text: inputPort.name,
value: inputPort.id,
description: nf.Common.escapeHtml(inputPort.comments)
});
});
// sort the options
options.sort(function (a, b) {
return a.text.localeCompare(b.text);
@ -555,7 +558,7 @@ nf.ConnectionConfiguration = (function () {
},
dataType: 'json'
}).done(function (response) {
var remoteProcessGroup = response.remoteProcessGroup;
var remoteProcessGroup = response.component;
var remoteProcessGroupContents = remoteProcessGroup.contents;
// only proceed if there are output ports

View File

@ -20,8 +20,8 @@
nf.Funnel = (function () {
var dimensions = {
width: 61,
height: 61
width: 48,
height: 48
};
// -----------------------------
@ -51,7 +51,7 @@ nf.Funnel = (function () {
/**
* Renders the funnels in the specified selection.
*
*
* @param {selection} entered The selection of funnels to be rendered
* @param {boolean} selected Whether the element should be selected
*/
@ -61,40 +61,60 @@ nf.Funnel = (function () {
}
var funnel = entered.append('g')
.attr({
'id': function (d) {
return 'id-' + d.id;
},
'class': 'funnel component'
})
.classed('selected', selected)
.call(nf.CanvasUtils.position);
.attr({
'id': function (d) {
return 'id-' + d.id;
},
'class': 'funnel component'
})
.classed('selected', selected)
.call(nf.CanvasUtils.position);
// funnel border
funnel.append('rect')
.attr({
'class': 'border',
'width': function (d) {
return d.dimensions.width;
},
'height': function (d) {
return d.dimensions.height;
},
'fill': 'transparent',
'stroke-opacity': 0.8,
'stroke-width': 1
});
.attr({
'rx': 2,
'ry': 2,
'class': 'border',
'width': function (d) {
return d.dimensions.width;
},
'height': function (d) {
return d.dimensions.height;
},
'fill': 'transparent',
'stroke': 'transparent'
}).classed('unauthorized', function (d) {
return d.accessPolicy.canRead === false;
});
// processor icon
funnel.append('image')
.call(nf.CanvasUtils.disableImageHref)
.attr({
'xlink:href': 'images/iconFunnel.png',
'width': 41,
'height': 41,
'x': 10,
'y': 10
});
// funnel body
funnel.append('rect')
.attr({
'rx': 2,
'ry': 2,
'class': 'body',
'width': function (d) {
return d.dimensions.width;
},
'height': function (d) {
return d.dimensions.height;
},
'filter': 'url(#component-drop-shadow)',
'stroke-width': 0
})
.classed('unauthorized', function (d) {
return d.accessPolicy.canRead === false;
});
// funnel icon
funnel.append('text')
.attr({
'class': 'funnel-icon',
'x': 9,
'y': 34
})
.text('\ue803');
// always support selection
funnel.call(nf.Selectable.activate).call(nf.ContextMenu.activate);
@ -109,7 +129,7 @@ nf.Funnel = (function () {
/**
* Updates the funnels in the specified selection.
*
*
* @param {selection} updated The funnels to be updated
*/
var updateFunnels = function (updated) {
@ -117,7 +137,7 @@ nf.Funnel = (function () {
/**
* Removes the funnels in the specified selection.
*
*
* @param {selection} removed The funnels to be removed
*/
var removeFunnels = function (removed) {
@ -133,15 +153,15 @@ nf.Funnel = (function () {
// create the funnel container
funnelContainer = d3.select('#canvas').append('g')
.attr({
'pointer-events': 'all',
'class': 'funnels'
});
.attr({
'pointer-events': 'all',
'class': 'funnels'
});
},
/**
* Populates the graph with the specified funnels.
*
*
* @argument {object | array} funnelEntities The funnels to add
* @argument {boolean} selectAll Whether or not to select the new contents
*/
@ -168,11 +188,11 @@ nf.Funnel = (function () {
// apply the selection and handle all new processors
select().enter().call(renderFunnels, selectAll);
},
/**
* If the funnel id is specified it is returned. If no funnel id
* specified, all funnels are returned.
*
*
* @param {string} id
*/
get: function (id) {
@ -182,11 +202,11 @@ nf.Funnel = (function () {
return funnelMap.get(id);
}
},
/**
* If the funnel id is specified it is refresh according to the current
* If the funnel id is specified it is refresh according to the current
* state. If not funnel id is specified, all funnels are refreshed.
*
*
* @param {string} id Optional
*/
refresh: function (id) {
@ -196,11 +216,11 @@ nf.Funnel = (function () {
d3.selectAll('g.funnel').call(updateFunnels);
}
},
/**
* Reloads the funnel state from the server and refreshes the UI.
* If the funnel is currently unknown, this function just returns.
*
*
* @param {object} funnel The funnel to reload
*/
reload: function (funnel) {
@ -214,21 +234,21 @@ nf.Funnel = (function () {
});
}
},
/**
* Positions the component.
*
*
* @param {string} id The id
*/
position: function (id) {
d3.select('#id-' + id).call(nf.CanvasUtils.position);
},
/**
* Sets the specified funnel(s). If the is an array, it
* will set each funnel. If it is not an array, it will
* Sets the specified funnel(s). If the is an array, it
* will set each funnel. If it is not an array, it will
* attempt to set the specified funnel.
*
*
* @param {object | array} funnelEntities
*/
set: function (funnelEntities) {
@ -237,7 +257,7 @@ nf.Funnel = (function () {
// update the current entry
var funnelEntry = funnelMap.get(funnelEntity.id);
$.extend(funnelEntry, funnelEntity);
// update the connection in the UI
d3.select('#id-' + funnelEntity.id).call(updateFunnels);
}
@ -255,7 +275,7 @@ nf.Funnel = (function () {
/**
* Removes the specified funnel.
*
*
* @param {array|string} funnels The funnel id
*/
remove: function (funnels) {
@ -270,7 +290,7 @@ nf.Funnel = (function () {
// apply the selection and handle all removed funnels
select().exit().call(removeFunnels);
},
/**
* Removes all processors.
*/

View File

@ -53,7 +53,9 @@ nf.Label = (function () {
* Selects the labels elements against the current label map.
*/
var select = function () {
return labelContainer.selectAll('g.label').data(labelMap.values());
return labelContainer.selectAll('g.label').data(labelMap.values(), function (d) {
return d.id;
});
};
/**
@ -126,21 +128,6 @@ nf.Label = (function () {
return;
}
// reset the colors
var colors = d3.set();
colors.add(nf.Common.substringAfterLast(nf.Label.defaultColor(), '#'));
// determine all unique colors
labelMap.forEach(function (id, d) {
if (d.accessPolicy.canRead) {
var color = d.component.style['background-color'];
if (nf.Common.isDefinedAndNotNull(color)) {
colors.add(nf.Common.substringAfterLast(color, '#'));
}
}
});
nf.Canvas.defineLabelColors(colors.values());
// update the border using the configured color
updated.select('rect.border')
.attr({
@ -177,10 +164,7 @@ nf.Label = (function () {
}
}
// get just the color code part
color = nf.Common.substringAfterLast(color, '#');
return 'url(#label-background-' + color + ')';
return color;
},
'width': function (d) {
return d.dimensions.width;

View File

@ -20,15 +20,15 @@
nf.Port = (function () {
var PREVIEW_NAME_LENGTH = 15;
var OFFSET_VALUE = 12;
var OFFSET_VALUE = 25;
var portDimensions = {
width: 160,
height: 40
width: 240,
height: 50
};
var remotePortDimensions = {
width: 160,
height: 56
width: 240,
height: 75
};
// ----------------------------
@ -51,7 +51,9 @@ nf.Port = (function () {
* Selects the port elements against the current port map.
*/
var select = function () {
return portContainer.selectAll('g.input-port, g.output-port').data(portMap.values());
return portContainer.selectAll('g.input-port, g.output-port').data(portMap.values(), function (d) {
return d.id;
});
};
/**
@ -66,36 +68,54 @@ nf.Port = (function () {
}
var port = entered.append('g')
.attr({
'id': function (d) {
return 'id-' + d.id;
},
'class': function (d) {
if (d.portType === 'INPUT_PORT') {
return 'input-port component';
} else {
return 'output-port component';
}
.attr({
'id': function (d) {
return 'id-' + d.id;
},
'class': function (d) {
if (d.portType === 'INPUT_PORT') {
return 'input-port component';
} else {
return 'output-port component';
}
})
.classed('selected', selected)
.call(nf.CanvasUtils.position);
}
})
.classed('selected', selected)
.call(nf.CanvasUtils.position);
// port border
port.append('rect')
.attr({
'class': 'border',
'width': function (d) {
return d.dimensions.width;
},
'height': function (d) {
return d.dimensions.height;
},
'fill': 'transparent',
'stroke-opacity': 0.8,
'stroke-width': 1,
'stroke': '#aaaaaa'
});
.attr({
'class': 'border',
'width': function (d) {
return d.dimensions.width;
},
'height': function (d) {
return d.dimensions.height;
},
'fill': 'transparent',
'stroke': 'transparent'
})
.classed('unauthorized', function (d) {
return d.accessPolicy.canRead === false;
});
// port body
port.append('rect')
.attr({
'class': 'body',
'width': function (d) {
return d.dimensions.width;
},
'height': function (d) {
return d.dimensions.height;
},
'filter': 'url(#component-drop-shadow)',
'stroke-width': 0
})
.classed('unauthorized', function (d) {
return d.accessPolicy.canRead === false;
});
var offset = 0;
@ -105,84 +125,47 @@ nf.Port = (function () {
// port remote banner
port.append('rect')
.attr({
'class': 'remote-banner',
'width': function (d) {
return d.dimensions.width;
},
'height': offset,
'fill': '#294c58',
'fill-opacity': 0.95
});
}
// port body
port.append('rect')
.attr({
'x': 0,
'y': offset,
'class': 'port-body',
'class': 'remote-banner',
'width': function (d) {
return d.dimensions.width;
},
'height': function (d) {
return d.dimensions.height - offset;
},
'fill': 'url(#port-background)',
'fill-opacity': 0.8,
'stroke-opacity': 0.8,
'stroke-width': 0,
'stroke': '#aaaaaa'
'height': offset,
'fill': '#e3e8eb'
});
}
// port icon
port.append('image')
.call(nf.CanvasUtils.disableImageHref)
.attr({
'xlink:href': function (d) {
if (d.portTtype === 'INPUT_PORT') {
return 'images/iconInputPort.png';
} else {
return 'images/iconOutputPort.png';
}
},
'width': 46,
'height': 31,
'x': function (d) {
if (d.portType === 'INPUT_PORT') {
return 0;
} else {
return 114;
}
},
'y': 5 + offset
});
port.append('text')
.attr({
'class': 'port-icon',
'x': 10,
'y': 38 + offset
})
.text(function (d) {
if (d.portType === 'INPUT_PORT') {
return '\ue832';
} else {
return '\ue833';
}
});
// port name
port.append('text')
.attr({
'x': function (d) {
if (d.portType === 'INPUT_PORT') {
return 52;
} else {
return 5;
}
},
'y': 18 + offset,
'width': 95,
'height': 30,
'font-size': '10px',
'font-weight': 'bold',
'fill': '#294c58',
'class': 'port-name'
});
.attr({
'x': 70,
'y': 25 + offset,
'width': 95,
'height': 30,
'class': 'port-name'
});
// make ports selectable
port.call(nf.Selectable.activate).call(nf.ContextMenu.activate);
// only activate dragging and connecting if appropriate
port.filter(function (d) {
return d.accessPolicy.canWrite && d.accessPolicy.canRead;
return d.accessPolicy.canWrite && d.accessPolicy.canRead;
}).call(nf.Draggable.activate).call(nf.Connectable.activate);
// call update to trigger some rendering
@ -213,132 +196,85 @@ nf.Port = (function () {
offset = OFFSET_VALUE;
// port transmitting icon
details.append('image')
.call(nf.CanvasUtils.disableImageHref)
.attr({
'class': 'port-transmission-icon',
'width': 10,
'height': 10,
'x': 3,
'y': 1
});
details.append('text')
.attr({
'class': 'port-transmission-icon',
'x': 10,
'y': 15
});
// bulletin background
details.append('rect')
.attr({
'class': 'bulletin-background',
'x': function (d) {
return portData.dimensions.width - offset;
},
'width': offset,
'height': offset
});
// bulletin icon
details.append('image')
.call(nf.CanvasUtils.disableImageHref)
.attr({
'class': 'bulletin-icon',
'xlink:href': 'images/iconBulletin.png',
'width': 12,
'height': 12,
'x': 147,
'y': 0
});
details.append('text')
.attr({
'class': 'bulletin-icon',
'x': function (d) {
return portData.dimensions.width - 18;
},
'y': 18
})
.text('\uf24a');
}
// run status icon
details.append('image')
.call(nf.CanvasUtils.disableImageHref)
.attr({
'class': 'port-run-status-icon',
'width': 16,
'height': 16,
'x': function (d) {
if (d.portType === 'INPUT_PORT') {
return 33;
} else {
return 107;
}
},
'y': function () {
return 24 + offset;
}
});
// active thread count
details.append('rect')
.attr({
'class': 'active-thread-count-background',
'height': 11,
'y': 0,
'fill': '#fff',
'fill-opacity': '0.65',
'stroke': '#aaa',
'stroke-width': '1'
});
// active thread bacground
details.append('text')
.attr({
'class': 'active-thread-count',
'height': 11,
'y': 9,
'fill': '#000'
});
.attr({
'class': 'run-status-icon',
'x': 50,
'y': function () {
return 25 + offset;
}
});
// -------------------
// active thread count
// -------------------
// active thread count
details.append('text')
.attr({
'class': 'active-thread-count-icon',
'y': 68
})
.text('\ue83f');
// active thread icon
details.append('text')
.attr({
'class': 'active-thread-count',
'y': 68
});
}
if (portData.accessPolicy.canRead) {
// update the run status
details.select('image.port-run-status-icon')
.attr('xlink:href', function (d) {
var img = '';
if (d.component.state === 'DISABLED') {
img = 'images/iconDisable.png';
} else if (!nf.Common.isEmpty(d.component.validationErrors)) {
img = 'images/iconAlert.png';
} else if (d.component.state === 'RUNNING') {
img = 'images/iconRun.png';
} else if (d.component.state === 'STOPPED') {
img = 'images/iconStop.png';
}
return img;
})
.each(function (d) {
// remove the existing tip if necessary
var tip = d3.select('#run-status-tip-' + d.id);
if (!tip.empty()) {
tip.remove();
}
// if there are validation errors generate a tooltip
if (!nf.Common.isEmpty(d.component.validationErrors)) {
tip = d3.select('#port-tooltips').append('div')
.attr('id', function () {
return 'run-status-tip-' + d.id;
})
.attr('class', 'tooltip nifi-tooltip')
.html(function () {
var list = nf.Common.formatUnorderedList(d.component.validationErrors);
if (list === null || list.length === 0) {
return '';
} else {
return $('<div></div>').append(list).html();
}
});
// add the tooltip
nf.CanvasUtils.canvasTooltip(tip, d3.select(this));
}
});
// update the port name
port.select('text.port-name')
.each(function (d) {
var portName = d3.select(this);
var name = d.component.name;
var words = name.split(/\s+/);
.each(function (d) {
var portName = d3.select(this);
var name = d.component.name;
var words = name.split(/\s+/);
// reset the port name to handle any previous state
portName.text(null).selectAll('tspan, title').remove();
// reset the port name to handle any previous state
portName.text(null).selectAll('tspan, title').remove();
// handle based on the number of tokens in the port name
if (words.length === 1) {
// apply ellipsis to the port name as necessary
nf.CanvasUtils.ellipsis(portName, name);
} else {
nf.CanvasUtils.multilineEllipsis(portName, 2, name);
}
}).append('title').text(function (d) {
// handle based on the number of tokens in the port name
if (words.length === 1) {
// apply ellipsis to the port name as necessary
nf.CanvasUtils.ellipsis(portName, name);
} else {
nf.CanvasUtils.multilineEllipsis(portName, 2, name);
}
}).append('title').text(function (d) {
return d.component.name;
});
}
@ -349,14 +285,14 @@ nf.Port = (function () {
if (portData.accessPolicy.canRead) {
// update the port name
port.select('text.port-name')
.text(function (d) {
var name = d.component.name;
if (name.length > PREVIEW_NAME_LENGTH) {
return name.substring(0, PREVIEW_NAME_LENGTH) + String.fromCharCode(8230);
} else {
return name;
}
});
.text(function (d) {
var name = d.component.name;
if (name.length > PREVIEW_NAME_LENGTH) {
return name.substring(0, PREVIEW_NAME_LENGTH) + String.fromCharCode(8230);
} else {
return name;
}
});
}
// remove tooltips if necessary
@ -372,7 +308,7 @@ nf.Port = (function () {
/**
* Updates the port status.
*
*
* @param {selection} updated The ports to be updated
*/
var updatePortStatus = function (updated) {
@ -380,14 +316,82 @@ nf.Port = (function () {
return;
}
updated.select('image.port-transmission-icon')
.attr('xlink:href', function (d) {
if (d.status.transmitting === true) {
return 'images/iconPortTransmitting.png';
} else {
return 'images/iconPortNotTransmitting.png';
// update the run status
updated.select('text.run-status-icon')
.attr({
'fill': function (d) {
var fill = '#728e9b';
if (d.status.runStatus === 'Invalid') {
fill = '#ba554a';
}
});
return fill;
},
'font-family': function (d) {
var family = 'FontAwesome';
if (d.status.runStatus === 'Disabled') {
family = 'flowfont';
}
return family;
}
})
.text(function (d) {
var img = '';
if (d.status.runStatus === 'Disabled') {
img = '\ue802';
} else if (d.status.runStatus === 'Invalid') {
img = '\uf071';
} else if (d.status.runStatus === 'Running') {
img = '\uf04b';
} else if (d.status.runStatus === 'Stopped') {
img = '\uf04d';
}
return img;
})
.each(function (d) {
// remove the existing tip if necessary
var tip = d3.select('#run-status-tip-' + d.id);
if (!tip.empty()) {
tip.remove();
}
// if there are validation errors generate a tooltip
if (d.accessPolicy.canRead && !nf.Common.isEmpty(d.component.validationErrors)) {
tip = d3.select('#port-tooltips').append('div')
.attr('id', function () {
return 'run-status-tip-' + d.id;
})
.attr('class', 'tooltip nifi-tooltip')
.html(function () {
var list = nf.Common.formatUnorderedList(d.component.validationErrors);
if (list === null || list.length === 0) {
return '';
} else {
return $('<div></div>').append(list).html();
}
});
// add the tooltip
nf.CanvasUtils.canvasTooltip(tip, d3.select(this));
}
});
updated.select('text.port-transmission-icon')
.attr({
'font-family': function (d) {
if (d.status.transmitting === true) {
return 'FontAwesome';
} else {
return 'flowfont';
}
}
})
.text(function (d) {
if (d.status.transmitting === true) {
return '\uf140';
} else {
return '\ue80a';
}
});
updated.each(function (d) {
var port = d3.select(this);
@ -405,6 +409,10 @@ nf.Port = (function () {
// bulletins
// ---------
port.select('rect.bulletin-background').classed('has-bulletins', function () {
return nf.Common.isDefinedAndNotNull(d.status) && !nf.Common.isEmpty(d.status.bulletins);
});
nf.CanvasUtils.bulletins(port, d, function () {
return d3.select('#port-tooltips');
}, offset);
@ -426,7 +434,7 @@ nf.Port = (function () {
/**
* Removes the tooltips for the ports in the specified selection.
*
*
* @param {selection} removed
*/
var removeTooltips = function (removed) {
@ -446,12 +454,12 @@ nf.Port = (function () {
// create the port container
portContainer = d3.select('#canvas').append('g')
.attr({
'pointer-events': 'all',
'class': 'ports'
});
.attr({
'pointer-events': 'all',
'class': 'ports'
});
},
/**
* Populates the graph with the specified ports.
*
@ -490,7 +498,7 @@ nf.Port = (function () {
// apply the selection and handle all new ports
select().enter().call(renderPorts, selectAll);
},
/**
* If the port id is specified it is returned. If no port id
* specified, all ports are returned.
@ -504,7 +512,7 @@ nf.Port = (function () {
return portMap.get(id);
}
},
/**
* If the port id is specified it is refresh according to the current
* state. If not port id is specified, all ports are refreshed.
@ -518,14 +526,14 @@ nf.Port = (function () {
d3.selectAll('g.input-port, g.output-port').call(updatePorts);
}
},
/**
* Refreshes the components necessary after a pan event.
*/
pan: function () {
d3.selectAll('g.input-port.entering, g.output-port.entering, g.input-port.leaving, g.output-port.leaving').call(updatePorts);
},
/**
* Reloads the port state from the server and refreshes the UI.
* If the port is currently unknown, this function just returns.
@ -547,16 +555,16 @@ nf.Port = (function () {
});
}
},
/**
* Positions the component.
*
*
* @param {string} id The id
*/
position: function (id) {
d3.select('#id-' + id).call(nf.CanvasUtils.position);
},
/**
* Sets the specified port(s). If the is an array, it
* will set each port. If it is not an array, it will
@ -585,10 +593,10 @@ nf.Port = (function () {
set(portEntities);
}
},
/**
* Sets the port status using the specified status.
*
*
* @param {array} portStatus Port status
*/
setStatus: function (portStatus) {
@ -625,7 +633,7 @@ nf.Port = (function () {
// apply the selection and handle all removed ports
select().exit().call(removePorts);
},
/**
* Removes all ports..
*/

View File

@ -722,6 +722,43 @@ nf.Common = (function () {
return result;
},
/**
* Extracts the contents of the specified str after the strToFind. If the
* strToFind is not found or the last part of the str, an empty string is
* returned.
*
* @argument {string} str The full string
* @argument {string} strToFind The substring to find
*/
substringAfterFirst: function (str, strToFind) {
var result = '';
var indexOfStrToFind = str.indexOf(strToFind);
if (indexOfStrToFind >= 0) {
var indexAfterStrToFind = indexOfStrToFind + strToFind.length;
if (indexAfterStrToFind < str.length) {
result = str.substr(indexAfterStrToFind);
}
}
return result;
},
/**
* Extracts the contents of the specified str before the strToFind. If the
* strToFind is not found or the first part of the str, an empty string is
* returned.
*
* @argument {string} str The full string
* @argument {string} strToFind The substring to find
*/
substringBeforeFirst: function(str, strToFind) {
var result = '';
var indexOfStrToFind = str.indexOf(strToFind);
if (indexOfStrToFind >= 0) {
result = str.substr(0, indexOfStrToFind);
}
return result
},
/**
* Updates the mouse pointer.
*