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; package org.apache.nifi.web.api.entity;
import org.apache.nifi.web.api.dto.ProcessorDTO; import org.apache.nifi.web.api.dto.ProcessorDTO;
import org.apache.nifi.web.api.dto.status.ProcessorStatusDTO;
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlRootElement;
@ -27,6 +28,7 @@ import javax.xml.bind.annotation.XmlRootElement;
public class ProcessorEntity extends ComponentEntity { public class ProcessorEntity extends ComponentEntity {
private ProcessorDTO component; private ProcessorDTO component;
private ProcessorStatusDTO status;
/** /**
* The ProcessorDTO that is being serialized. * The ProcessorDTO that is being serialized.
@ -41,4 +43,16 @@ public class ProcessorEntity extends ComponentEntity {
this.component = component; 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) { 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"> <md-menu-item layout-align="space-around center">
<a id="bulletin-board-link" <a id="bulletin-board-link"
ng-click="appCtrl.serviceProvider.headerCtrl.globalMenuCtrl.bulletinBoard.shell.launch();"><i 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-item>
<md-menu-divider></md-menu-divider> <md-menu-divider></md-menu-divider>
<md-menu-item <md-menu-item

View File

@ -32,7 +32,7 @@
<div layout="row" layout-align="end center"> <div layout="row" layout-align="end center">
<input id="search-field" type="text"/> <input id="search-field" type="text"/>
<button id="search-button"><i class="fa fa-search"></i></button> <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> </div>
<div id="search-flow-results"></div> <div id="search-flow-results"></div>

View File

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

View File

@ -53,16 +53,78 @@ text.unset {
*/ */
g.component { 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 { g.component.selected rect.border {
stroke: #ffcc00; stroke: #004849;
stroke-width: 5; stroke-width: 3;
} }
g.funnel.selected rect.border { text.stats-label {
stroke-width: 2.5; 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 Processor
*/ */
text.processor-stats-label { text.processor-name {
fill: #fff; fill: #262626;
font-size: 11px; font-size: 14px;
font-weight: bold;
} }
text.processor-stats-value { text.processor-type {
font-size: 11px; fill: #728e9b;
font-size: 12px;
} }
text.processor-stats-info { text.processor-icon {
fill: #999999; fill: #ad9897;
font-size: 11px; font-family: flowfont;
font-size: 30px;
}
text.run-status-icon {
font-size: 13px;
} }
/* /*
@ -108,7 +175,27 @@ text.processor-stats-info {
*/ */
g.connection { 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 { path.connector {
@ -126,12 +213,6 @@ path.connector.connectable {
stroke-dasharray: 4; stroke-dasharray: 4;
} }
g.connection rect.connection-label {
fill: #ffffff;
stroke: #000000;
stroke-width: 2;
}
g.connection path.connection-path { g.connection path.connection-path {
fill: none; fill: none;
stroke: #000000; stroke: #000000;
@ -139,6 +220,21 @@ g.connection path.connection-path {
cursor: pointer; 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 */ /* grouped connection */
g.connection.grouped path.connection-path, g.connection.grouped rect.connection-label { g.connection.grouped path.connection-path, g.connection.grouped rect.connection-label {
@ -196,12 +292,6 @@ g.connection rect.endpoint {
cursor: pointer; cursor: pointer;
} }
g.connection text.connection-stats-label {
font-size: 11px;
font-weight: bold;
fill: #598599;
}
/* labels */ /* labels */
g.label rect.labelpoint { g.label rect.labelpoint {
@ -218,8 +308,30 @@ g.label.selected rect.labelpoint {
/* funnels */ /* funnels */
text.funnel-icon {
fill: #ad9897;
font-family: flowfont;
font-size: 30px;
}
/* ports */ /* 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 */ /* active thread count */
text.active-thread-count { text.active-thread-count {
@ -228,35 +340,55 @@ text.active-thread-count {
/* process groups */ /* 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 { g.process-group.drop rect.border {
stroke: #0000ff; stroke: #0000ff;
stroke-width: 3; stroke-width: 3;
} }
text.process-group-contents-count { text.process-group-contents-icon {
font-weight: bold; font-size: 13px;
fill: #294c58; fill: #728e9b;
}
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;
} }
/* remote process group */ /* 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 { text.remote-process-group-last-refresh {
fill: #cccccc; fill: #728e9b;
font-style: italic;
text-anchor: end; text-anchor: end;
} }

View File

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

View File

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

View File

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

View File

@ -405,16 +405,12 @@ nf.CanvasUtils = (function () {
.each(function () { .each(function () {
var bBox = this.getBBox(); var bBox = this.getBBox();
d3.select(this).attr('x', function () { d3.select(this).attr('x', function () {
return d.dimensions.width - bBox.width - 4; return d.dimensions.width - bBox.width - 15;
}); });
}); });
// update the background width // update the background width
selection.select('rect.active-thread-count-background') selection.select('text.active-thread-count-icon')
.attr('width', function () {
var bBox = activeThreadCount.node().getBBox();
return bBox.width + 8;
})
.attr('x', function () { .attr('x', function () {
var bBox = activeThreadCount.node().getBBox(); var bBox = activeThreadCount.node().getBBox();
@ -423,22 +419,11 @@ nf.CanvasUtils = (function () {
setOffset(bBox.width + 6); setOffset(bBox.width + 6);
} }
return d.dimensions.width - bBox.width - 8; return d.dimensions.width - bBox.width - 20;
})
.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(' ');
}) })
.style('display', 'block'); .style('display', 'block');
} else { } 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 there are bulletins show them, otherwise hide
if (nf.Common.isDefinedAndNotNull(d.status) && !nf.Common.isEmpty(d.status.bulletins)) { if (nf.Common.isDefinedAndNotNull(d.status) && !nf.Common.isEmpty(d.status.bulletins)) {
// update the tooltip // update the tooltip
selection.select('image.bulletin-icon') selection.select('text.bulletin-icon')
.style('display', 'block')
.each(function () { .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 // if there are bulletins generate a tooltip
tip = getTooltipContainer().append('div') tip = getTooltipContainer().append('div')
.attr('id', function () { .attr('id', function () {
@ -505,10 +482,8 @@ nf.CanvasUtils = (function () {
}); });
// add the tooltip // 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 // create arrow definitions for the various line types
defs.selectAll('marker') defs.selectAll('marker')
.data(['normal', 'ghost']) .data(['normal', 'ghost', 'unauthorized'])
.enter().append('marker') .enter().append('marker')
.attr({ .attr({
'id': function (d) { 'id': function (d) {
@ -256,6 +256,8 @@ nf.Canvas = (function () {
'fill': function (d) { 'fill': function (d) {
if (d === 'ghost') { if (d === 'ghost') {
return '#aaaaaa'; return '#aaaaaa';
} else if (d === 'unauthorized') {
return '#ba554a';
} else { } else {
return '#000000'; return '#000000';
} }
@ -264,77 +266,42 @@ nf.Canvas = (function () {
.append('path') .append('path')
.attr('d', 'M2,3 L0,6 L6,3 L0,0 z'); .attr('d', 'M2,3 L0,6 L6,3 L0,0 z');
// define the gradient for the processor stats background // filter for drop shadow
var processGroupStatsBackground = defs.append('linearGradient') var filter = defs.append('filter')
.attr({ .attr('id', 'component-drop-shadow');
'id': 'process-group-stats-background',
'x1': '0%',
'y1': '100%',
'x2': '0%',
'y2': '0%'
});
processGroupStatsBackground.append('stop') // blur
.attr({ filter.append('feGaussianBlur')
'offset': '0%', .attr('in', 'SourceAlpha')
'stop-color': '#dedede' .attr('stdDeviation', 2)
}); .attr('result', 'blur');
processGroupStatsBackground.append('stop') // offset
.attr({ filter.append('feOffset')
'offset': '50%', .attr('in', 'blur')
'stop-color': '#ffffff' .attr('dx', 0)
}); .attr('dy', 1)
.attr('result', 'offsetBlur');
processGroupStatsBackground.append('stop') // color/opacity
.attr({ filter.append('feFlood')
'offset': '100%', .attr('flood-color', '#000000')
'stop-color': '#dedede' .attr('flood-opacity', 0.25)
}); .attr('result', 'offsetColor');
// define the gradient for the processor stats background // combine
var processorStatsBackground = defs.append('linearGradient') filter.append('feComposite')
.attr({ .attr('in', 'offsetColor')
'id': 'processor-stats-background', .attr('in2', 'offsetBlur')
'x1': '0%', .attr('operator', 'in')
'y1': '100%', .attr('result', 'offsetColorBlur');
'x2': '0%',
'y2': '0%'
});
processorStatsBackground.append('stop') // stack the effect under the source graph
.attr({ var feMerge = filter.append('feMerge');
'offset': '0%', feMerge.append('feMergeNode')
'stop-color': '#6f97ac' .attr('in', 'offsetColorBlur');
}); feMerge.append('feMergeNode')
.attr('in', 'SourceGraphic');
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'
});
// define the gradient for the expiration icon // define the gradient for the expiration icon
var expirationBackground = defs.append('linearGradient') var expirationBackground = defs.append('linearGradient')
@ -636,51 +603,6 @@ nf.Canvas = (function () {
}).fail(nf.Common.handleAjaxError); }).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. * Reloads the current status of this flow.
*/ */
@ -1172,24 +1094,6 @@ nf.Canvas = (function () {
}).fail(nf.Common.handleAjaxError); }).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. * Return whether this instance of NiFi is clustered.
* *

View File

@ -248,29 +248,32 @@ nf.ConnectionConfiguration = (function () {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: config.urls.api + '/process-groups/' + encodeURIComponent(processGroupData.id), url: config.urls.api + '/flow/process-groups/' + encodeURIComponent(processGroupData.id),
data: { data: {
verbose: true verbose: true
}, },
dataType: 'json' dataType: 'json'
}).done(function (response) { }).done(function (response) {
var processGroup = response.component; var processGroup = response.processGroupFlow;
var processGroupContents = processGroup.contents; 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 // only proceed if there are output ports
if (!nf.Common.isEmpty(processGroupContents.outputPorts)) { if (!nf.Common.isEmpty(options)) {
$('#output-port-source').show(); $('#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 // sort the options
options.sort(function (a, b) { options.sort(function (a, b) {
return a.text.localeCompare(b.text); return a.text.localeCompare(b.text);
@ -331,7 +334,7 @@ nf.ConnectionConfiguration = (function () {
}, },
dataType: 'json' dataType: 'json'
}).done(function (response) { }).done(function (response) {
var remoteProcessGroup = response.remoteProcessGroup; var remoteProcessGroup = response.component;
var remoteProcessGroupContents = remoteProcessGroup.contents; var remoteProcessGroupContents = remoteProcessGroup.contents;
// only proceed if there are output ports // only proceed if there are output ports
@ -473,29 +476,29 @@ nf.ConnectionConfiguration = (function () {
$.ajax({ $.ajax({
type: 'GET', 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' dataType: 'json'
}).done(function (response) { }).done(function (response) {
var processGroup = response.component; var processGroup = response.processGroupFlow;
var processGroupContents = processGroup.contents; 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 // only proceed if there are output ports
if (!nf.Common.isEmpty(processGroupContents.inputPorts)) { if (!nf.Common.isEmpty(options)) {
$('#input-port-destination').show(); $('#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 // sort the options
options.sort(function (a, b) { options.sort(function (a, b) {
return a.text.localeCompare(b.text); return a.text.localeCompare(b.text);
@ -555,7 +558,7 @@ nf.ConnectionConfiguration = (function () {
}, },
dataType: 'json' dataType: 'json'
}).done(function (response) { }).done(function (response) {
var remoteProcessGroup = response.remoteProcessGroup; var remoteProcessGroup = response.component;
var remoteProcessGroupContents = remoteProcessGroup.contents; var remoteProcessGroupContents = remoteProcessGroup.contents;
// only proceed if there are output ports // only proceed if there are output ports

View File

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

View File

@ -53,7 +53,9 @@ nf.Label = (function () {
* Selects the labels elements against the current label map. * Selects the labels elements against the current label map.
*/ */
var select = function () { 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; 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 // update the border using the configured color
updated.select('rect.border') updated.select('rect.border')
.attr({ .attr({
@ -177,10 +164,7 @@ nf.Label = (function () {
} }
} }
// get just the color code part return color;
color = nf.Common.substringAfterLast(color, '#');
return 'url(#label-background-' + color + ')';
}, },
'width': function (d) { 'width': function (d) {
return d.dimensions.width; return d.dimensions.width;

View File

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

View File

@ -722,6 +722,43 @@ nf.Common = (function () {
return result; 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. * Updates the mouse pointer.
* *