summaryrefslogtreecommitdiffstats
path: root/mod/designtool/designtool-web/src/main/webapp/js/nf/canvas/nf-process-group.js
diff options
context:
space:
mode:
Diffstat (limited to 'mod/designtool/designtool-web/src/main/webapp/js/nf/canvas/nf-process-group.js')
-rw-r--r--mod/designtool/designtool-web/src/main/webapp/js/nf/canvas/nf-process-group.js1744
1 files changed, 1744 insertions, 0 deletions
diff --git a/mod/designtool/designtool-web/src/main/webapp/js/nf/canvas/nf-process-group.js b/mod/designtool/designtool-web/src/main/webapp/js/nf/canvas/nf-process-group.js
new file mode 100644
index 0000000..614472c
--- /dev/null
+++ b/mod/designtool/designtool-web/src/main/webapp/js/nf/canvas/nf-process-group.js
@@ -0,0 +1,1744 @@
+/*
+ * 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.
+ *
+ * Modifications to the original nifi code for the ONAP project are made
+ * available under the Apache License, Version 2.0
+ */
+
+/* global d3, define, module, require, exports */
+
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ define(['jquery',
+ 'd3',
+ 'nf.Connection',
+ 'nf.Common',
+ 'nf.Client',
+ 'nf.CanvasUtils',
+ 'nf.Dialog'],
+ function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog) {
+ return (nf.ProcessGroup = factory($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog));
+ });
+ } else if (typeof exports === 'object' && typeof module === 'object') {
+ module.exports = (nf.ProcessGroup =
+ factory(require('jquery'),
+ require('d3'),
+ require('nf.Connection'),
+ require('nf.Common'),
+ require('nf.Client'),
+ require('nf.CanvasUtils'),
+ require('nf.Dialog')));
+ } else {
+ nf.ProcessGroup = factory(root.$,
+ root.d3,
+ root.nf.Connection,
+ root.nf.Common,
+ root.nf.Client,
+ root.nf.CanvasUtils,
+ root.nf.Dialog);
+ }
+}
+(this, function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog) {
+ 'use strict';
+
+ var nfConnectable;
+ var nfDraggable;
+ var nfSelectable;
+ var nfContextMenu;
+
+ var PREVIEW_NAME_LENGTH = 30;
+
+ var dimensions = {
+ width: 380,
+ height: 172
+ };
+
+ // ----------------------------
+ // process groups currently on the graph
+ // ----------------------------
+
+ var processGroupMap;
+
+ // -----------------------------------------------------------
+ // cache for components that are added/removed from the canvas
+ // -----------------------------------------------------------
+
+ var removedCache;
+ var addedCache;
+
+ // --------------------
+ // component containers
+ // --------------------
+
+ var processGroupContainer;
+
+ // --------------------------
+ // privately scoped functions
+ // --------------------------
+
+ /**
+ * Determines whether the specified process group is under version control.
+ *
+ * @param d
+ */
+ var isUnderVersionControl = function (d) {
+ return nfCommon.isDefinedAndNotNull(d.versionedFlowState);
+ };
+
+ /**
+ * Selects the process group elements against the current process group map.
+ */
+ var select = function () {
+ return processGroupContainer.selectAll('g.process-group').data(processGroupMap.values(), function (d) {
+ return d.id;
+ });
+ };
+
+
+ /**
+ * Renders the process groups in the specified selection.
+ *
+ * @param {selection} entered The selection of process groups to be rendered
+ * @param {boolean} selected Whether the process group should be selected
+ * @return the entered selection
+ */
+ var renderProcessGroups = function (entered, selected) {
+ if (entered.empty()) {
+ return entered;
+ }
+
+ var processGroup = entered.append('g')
+ .attrs({
+ 'id': function (d) {
+ return 'id-' + d.id;
+ },
+ 'class': 'process-group component'
+ })
+ .classed('selected', selected)
+ .call(nfCanvasUtils.position);
+
+ // ----
+ // body
+ // ----
+
+ // process group border
+ processGroup.append('rect')
+ .attrs({
+ 'class': 'border',
+ 'width': function (d) {
+ return d.dimensions.width;
+ },
+ 'height': function (d) {
+ return d.dimensions.height;
+ },
+ 'fill': 'transparent',
+ 'stroke': 'transparent'
+ });
+
+ // process group body
+ processGroup.append('rect')
+ .attrs({
+ 'class': 'body',
+ 'width': function (d) {
+ return d.dimensions.width;
+ },
+ 'height': function (d) {
+ return d.dimensions.height;
+ },
+ 'filter': 'url(#component-drop-shadow)',
+ 'stroke-width': 0
+ });
+
+ // process group name background
+ processGroup.append('rect')
+ .attrs({
+ 'width': function (d) {
+ return d.dimensions.width;
+ },
+ 'height': 32,
+ 'fill': '#b8c6cd'
+ });
+
+ // process group name
+ processGroup.append('text')
+ .attrs({
+ 'x': 10,
+ 'y': 20,
+ 'width': 300,
+ 'height': 16,
+ 'class': 'process-group-name'
+ });
+
+ // process group name
+ processGroup.append('text')
+ .attrs({
+ 'x': 10,
+ 'y': 21,
+ 'class': 'version-control'
+ });
+
+ console.log(processGroup);
+
+
+ // always support selecting and navigation
+ processGroup.on('dblclick', function (d) {
+ // enter this group on double click
+ nfProcessGroup.enterGroup(d.id);
+ })
+ .call(nfSelectable.activate).call(nfContextMenu.activate);
+
+ // only support dragging, connection, and drag and drop if appropriate
+ processGroup.filter(function (d) {
+ return d.permissions.canWrite && d.permissions.canRead;
+ })
+ .on('mouseover.drop', function (d) {
+ // Using mouseover/out to workaround chrome issue #122746
+
+ // get the target and ensure its not already been marked for drop
+ var target = d3.select(this);
+ if (!target.classed('drop')) {
+ var targetData = target.datum();
+
+ // see if there is a selection being dragged
+ var drag = d3.select('rect.drag-selection');
+ if (!drag.empty()) {
+ // filter the current selection by this group
+ var selection = nfCanvasUtils.getSelection().filter(function (d) {
+ return targetData.id === d.id;
+ });
+
+ // ensure this group isn't in the selection
+ if (selection.empty()) {
+ // mark that we are hovering over a drop area if appropriate
+ target.classed('drop', function () {
+ // get the current selection and ensure its disconnected
+ return nfConnection.isDisconnected(nfCanvasUtils.getSelection());
+ });
+ }
+ }
+ }
+ })
+ .on('mouseout.drop', function (d) {
+ // mark that we are no longer hovering over a drop area unconditionally
+ d3.select(this).classed('drop', false);
+ })
+ .call(nfDraggable.activate)
+ .call(nfConnectable.activate);
+
+ return processGroup;
+ };
+
+ // attempt of space between component count and icon for process group contents
+ var CONTENTS_SPACER = 10;
+ var CONTENTS_VALUE_SPACER = 5;
+
+ /**
+ * Updates the process groups in the specified selection.
+ *
+ * @param {selection} updated The process groups to be updated
+ */
+ var updateProcessGroups = function (updated) {
+ if (updated.empty()) {
+ return;
+ }
+
+ // process group border authorization
+ updated.select('rect.border')
+ .classed('unauthorized', function (d) {
+ return d.permissions.canRead === false;
+ });
+
+ // process group body authorization
+ updated.select('rect.body')
+ .classed('unauthorized', function (d) {
+ return d.permissions.canRead === false;
+ });
+
+ updated.each(function (processGroupData) {
+ var processGroup = d3.select(this);
+ var details = processGroup.select('g.process-group-details');
+
+ // update the component behavior as appropriate
+ nfCanvasUtils.editable(processGroup, nfConnectable, nfDraggable);
+
+ // if this processor is visible, render everything
+ if (processGroup.classed('visible')) {
+ if (details.empty()) {
+ details = processGroup.append('g').attr('class', 'process-group-details');
+
+ // -------------------
+ // contents background
+ // -------------------
+
+ details.append('rect')
+ .attrs({
+ 'x': 0,
+ 'y': 32,
+ 'width': function () {
+ return processGroupData.dimensions.width
+ },
+ 'height': 24,
+ 'fill': '#e3e8eb'
+ });
+
+ details.append('rect')
+ .attrs({
+ 'x': 0,
+ 'y': function () {
+ return processGroupData.dimensions.height - 24;
+ },
+ 'width': function () {
+ return processGroupData.dimensions.width;
+ },
+ 'height': 24,
+ 'fill': '#e3e8eb'
+ });
+
+ // --------
+ // contents
+ // --------
+
+ // transmitting icon
+// details.append('text')
+// .attrs({
+// 'x': 10,
+// 'y': 49,
+// 'class': 'process-group-transmitting process-group-contents-icon',
+// 'font-family': 'FontAwesome'
+// })
+// .text('\uf140')
+// .append("title")
+// .text("Transmitting Remote Process Groups");
+
+
+ // transmitting count
+// details.append('text')
+// .attrs({
+// 'y': 49,
+// 'class': 'process-group-transmitting-count process-group-contents-count'
+// });
+
+ // not transmitting icon
+// details.append('text')
+// .attrs({
+// 'y': 49,
+// 'class': 'process-group-not-transmitting process-group-contents-icon',
+// 'font-family': 'flowfont'
+// })
+// .text('\ue80a')
+// .append("title")
+// .text("Not Transmitting Remote Process Groups");
+
+ // not transmitting count
+// details.append('text')
+// .attrs({
+// 'y': 49,
+// 'class': 'process-group-not-transmitting-count process-group-contents-count'
+// });
+
+ // running icon
+// details.append('text')
+// .attrs({
+// 'y': 49,
+// 'class': 'process-group-running process-group-contents-icon',
+// 'font-family': 'FontAwesome'
+// })
+// .text('\uf04b')
+// .append("title")
+// .text("Running Components");
+
+ // running count
+// details.append('text')
+// .attrs({
+// 'y': 49,
+// 'class': 'process-group-running-count process-group-contents-count'
+// });
+
+ // stopped icon
+// details.append('text')
+// .attrs({
+// 'y': 49,
+// 'class': 'process-group-stopped process-group-contents-icon',
+// 'font-family': 'FontAwesome'
+// })
+// .text('\uf04d')
+// .append("title")
+// .text("Stopped Components");
+
+ // stopped count
+// details.append('text')
+// .attrs({
+// 'y': 49,
+// 'class': 'process-group-stopped-count process-group-contents-count'
+// });
+
+ // invalid icon
+// details.append('text')
+// .attrs({
+// 'y': 49,
+// 'class': 'process-group-invalid process-group-contents-icon',
+// 'font-family': 'FontAwesome'
+// })
+// .text('\uf071')
+// .append("title")
+// .text("Invalid Components");
+
+ // invalid count
+// details.append('text')
+// .attrs({
+// 'y': 49,
+// 'class': 'process-group-invalid-count process-group-contents-count'
+// });
+
+ // disabled icon
+// details.append('text')
+// .attrs({
+// 'y': 49,
+// 'class': 'process-group-disabled process-group-contents-icon',
+// 'font-family': 'flowfont'
+// })
+// .text('\ue802')
+// .append("title")
+// .text("Disabled Components");
+
+ // disabled count
+// details.append('text')
+// .attrs({
+// 'y': 49,
+// 'class': 'process-group-disabled-count process-group-contents-count'
+// });
+
+ // up to date icon
+ details.append('text')
+ .attrs({
+ 'x': 10,
+ 'y': function () {
+ return processGroupData.dimensions.height - 7;
+ },
+ 'class': 'process-group-up-to-date process-group-contents-icon',
+ 'font-family': 'FontAwesome'
+ })
+ .text('\uf00c')
+ .append("title")
+ .text("Up to date Versioned Process Groups");
+
+ // up to date count
+ details.append('text')
+ .attrs({
+ 'y': function () {
+ return processGroupData.dimensions.height - 7;
+ },
+ 'class': 'process-group-up-to-date-count process-group-contents-count'
+ });
+
+ // locally modified icon
+ details.append('text')
+ .attrs({
+ 'y': function () {
+ return processGroupData.dimensions.height - 7;
+ },
+ 'class': 'process-group-locally-modified process-group-contents-icon',
+ 'font-family': 'FontAwesome'
+ })
+ .text('\uf069')
+ .append("title")
+ .text("Locally modified Versioned Process Groups");
+
+ // locally modified count
+ details.append('text')
+ .attrs({
+ 'y': function () {
+ return processGroupData.dimensions.height - 7;
+ },
+ 'class': 'process-group-locally-modified-count process-group-contents-count'
+ });
+
+ // stale icon
+ details.append('text')
+ .attrs({
+ 'y': function () {
+ return processGroupData.dimensions.height - 7;
+ },
+ 'class': 'process-group-stale process-group-contents-icon',
+ 'font-family': 'FontAwesome'
+ })
+ .text('\uf0aa')
+ .append("title")
+ .text("Stale Versioned Process Groups");
+
+ // stale count
+ details.append('text')
+ .attrs({
+ 'y': function () {
+ return processGroupData.dimensions.height - 7;
+ },
+ 'class': 'process-group-stale-count process-group-contents-count'
+ });
+
+ // locally modified and stale icon
+ details.append('text')
+ .attrs({
+ 'y': function () {
+ return processGroupData.dimensions.height - 7;
+ },
+ 'class': 'process-group-locally-modified-and-stale process-group-contents-icon',
+ 'font-family': 'FontAwesome'
+ })
+ .text('\uf06a')
+ .append("title")
+ .text("Locally modified and stale Versioned Process Groups");
+
+ // locally modified and stale count
+ details.append('text')
+ .attrs({
+ 'y': function () {
+ return processGroupData.dimensions.height - 7;
+ },
+ 'class': 'process-group-locally-modified-and-stale-count process-group-contents-count'
+ });
+
+ // sync failure icon
+ details.append('text')
+ .attrs({
+ 'y': function () {
+ return processGroupData.dimensions.height - 7;
+ },
+ 'class': 'process-group-sync-failure process-group-contents-icon',
+ 'font-family': 'FontAwesome'
+ })
+ .text('\uf128')
+ .append("title")
+ .text("Sync failure Versioned Process Groups");
+
+ // sync failure count
+ details.append('text')
+ .attrs({
+ 'y': function () {
+ return processGroupData.dimensions.height - 7;
+ },
+ 'class': 'process-group-sync-failure-count process-group-contents-count'
+ });
+
+ // ----------------
+ // stats background
+ // ----------------
+
+ // queued
+ details.append('rect')
+ .attrs({
+ 'width': function () {
+ return processGroupData.dimensions.width;
+ },
+ 'height': 19,
+ 'x': 0,
+ 'y': 66,
+ 'fill': '#f4f6f7'
+ });
+
+ // border
+ details.append('rect')
+ .attrs({
+ 'width': function () {
+ return processGroupData.dimensions.width;
+ },
+ 'height': 1,
+ 'x': 0,
+ 'y': 84,
+ 'fill': '#c7d2d7'
+ });
+
+ // in
+ details.append('rect')
+ .attrs({
+ 'width': function () {
+ return processGroupData.dimensions.width;
+ },
+ 'height': 19,
+ 'x': 0,
+ 'y': 85,
+ 'fill': '#ffffff'
+ });
+
+ // border
+ details.append('rect')
+ .attrs({
+ 'width': function () {
+ return processGroupData.dimensions.width;
+ },
+ 'height': 1,
+ 'x': 0,
+ 'y': 103,
+ 'fill': '#c7d2d7'
+ });
+
+ // read/write
+ details.append('rect')
+ .attrs({
+ 'width': function () {
+ return processGroupData.dimensions.width;
+ },
+ 'height': 19,
+ 'x': 0,
+ 'y': 104,
+ 'fill': '#f4f6f7'
+ });
+
+ // border
+ details.append('rect')
+ .attrs({
+ 'width': function () {
+ return processGroupData.dimensions.width;
+ },
+ 'height': 1,
+ 'x': 0,
+ 'y': 122,
+ 'fill': '#c7d2d7'
+ });
+
+ // out
+ details.append('rect')
+ .attrs({
+ 'width': function () {
+ return processGroupData.dimensions.width;
+ },
+ 'height': 19,
+ 'x': 0,
+ 'y': 123,
+ 'fill': '#ffffff'
+ });
+
+ // -----
+ // stats
+ // -----
+
+ // stats label container
+ var processGroupStatsLabel = details.append('g')
+ .attrs({
+ 'transform': 'translate(6, 75)'
+ });
+
+ // queued label
+ processGroupStatsLabel.append('text')
+ .attrs({
+ 'width': 73,
+ 'height': 10,
+ 'x': 4,
+ 'y': 5,
+ 'class': 'stats-label'
+ })
+ .text('Queued');
+
+ // in label
+ processGroupStatsLabel.append('text')
+ .attrs({
+ 'width': 73,
+ 'height': 10,
+ 'x': 4,
+ 'y': 24,
+ 'class': 'stats-label'
+ })
+ .text('In');
+
+ // read/write label
+ processGroupStatsLabel.append('text')
+ .attrs({
+ 'width': 73,
+ 'height': 10,
+ 'x': 4,
+ 'y': 42,
+ 'class': 'stats-label'
+ })
+ .text('Read/Write');
+
+ // out label
+ processGroupStatsLabel.append('text')
+ .attrs({
+ 'width': 73,
+ 'height': 10,
+ 'x': 4,
+ 'y': 60,
+ 'class': 'stats-label'
+ })
+ .text('Out');
+
+ // stats value container
+ var processGroupStatsValue = details.append('g')
+ .attrs({
+ 'transform': 'translate(95, 75)'
+ });
+
+ // queued value
+ var queuedText = processGroupStatsValue.append('text')
+ .attrs({
+ 'width': 180,
+ 'height': 10,
+ 'x': 4,
+ 'y': 5,
+ 'class': 'process-group-queued stats-value'
+ });
+
+ // queued count
+ queuedText.append('tspan')
+ .attrs({
+ 'class': 'count'
+ });
+
+ // queued size
+ queuedText.append('tspan')
+ .attrs({
+ 'class': 'size'
+ });
+
+ // in value
+ var inText = processGroupStatsValue.append('text')
+ .attrs({
+ 'width': 180,
+ 'height': 10,
+ 'x': 4,
+ 'y': 24,
+ 'class': 'process-group-in stats-value'
+ });
+
+ // in count
+ inText.append('tspan')
+ .attrs({
+ 'class': 'count'
+ });
+
+ // in size
+ inText.append('tspan')
+ .attrs({
+ 'class': 'size'
+ });
+
+ // in
+ inText.append('tspan')
+ .attrs({
+ 'class': 'ports'
+ });
+
+ // read/write value
+ processGroupStatsValue.append('text')
+ .attrs({
+ 'width': 180,
+ 'height': 10,
+ 'x': 4,
+ 'y': 42,
+ 'class': 'process-group-read-write stats-value'
+ });
+
+ // out value
+ var outText = processGroupStatsValue.append('text')
+ .attrs({
+ 'width': 180,
+ 'height': 10,
+ 'x': 4,
+ 'y': 60,
+ 'class': 'process-group-out stats-value'
+ });
+
+ // out ports
+ outText.append('tspan')
+ .attrs({
+ 'class': 'ports'
+ });
+
+ // out count
+ outText.append('tspan')
+ .attrs({
+ 'class': 'count'
+ });
+
+ // out size
+ outText.append('tspan')
+ .attrs({
+ 'class': 'size'
+ });
+
+ // stats value container
+ var processGroupStatsInfo = details.append('g')
+ .attrs({
+ 'transform': 'translate(335, 75)'
+ });
+
+ // in info
+ processGroupStatsInfo.append('text')
+ .attrs({
+ 'width': 25,
+ 'height': 10,
+ 'x': 4,
+ 'y': 24,
+ 'class': 'stats-info'
+ })
+ .text('5 min');
+
+ // read/write info
+ processGroupStatsInfo.append('text')
+ .attrs({
+ 'width': 25,
+ 'height': 10,
+ 'x': 4,
+ 'y': 42,
+ 'class': 'stats-info'
+ })
+ .text('5 min');
+
+ // out info
+ processGroupStatsInfo.append('text')
+ .attrs({
+ 'width': 25,
+ 'height': 10,
+ 'x': 4,
+ 'y': 60,
+ 'class': 'stats-info'
+ })
+ .text('5 min');
+
+ // --------
+ // comments
+ // --------
+
+ details.append('path')
+ .attrs({
+ 'class': 'component-comments',
+ 'transform': 'translate(' + (processGroupData.dimensions.width - 2) + ', ' + (processGroupData.dimensions.height - 10) + ')',
+ 'd': 'm0,0 l0,8 l-8,0 z'
+ });
+
+ // -------------------
+ // active thread count
+ // -------------------
+
+ // active thread count
+ details.append('text')
+ .attrs({
+ 'class': 'active-thread-count-icon',
+ 'y': 20
+ })
+ .text('\ue83f');
+
+ // active thread icon
+ details.append('text')
+ .attrs({
+ 'class': 'active-thread-count',
+ 'y': 20
+ });
+
+ // ---------
+ // bulletins
+ // ---------
+
+ // bulletin background
+ details.append('rect')
+ .attrs({
+ 'class': 'bulletin-background',
+ 'x': function () {
+ return processGroupData.dimensions.width - 24;
+ },
+ 'y': 32,
+ 'width': 24,
+ 'height': 24
+ });
+
+ // bulletin icon
+ details.append('text')
+ .attrs({
+ 'class': 'bulletin-icon',
+ 'x': function () {
+ return processGroupData.dimensions.width - 17;
+ },
+ 'y': 49
+ })
+ .text('\uf24a');
+ }
+
+ // update transmitting
+ var transmitting = details.select('text.process-group-transmitting')
+ .classed('transmitting', function (d) {
+ return d.permissions.canRead && d.activeRemotePortCount > 0;
+ })
+ .classed('zero', function (d) {
+ return d.permissions.canRead && d.activeRemotePortCount === 0;
+ });
+ var transmittingCount = details.select('text.process-group-transmitting-count')
+ .attr('x', function () {
+ var transmittingCountX = parseInt(transmitting.attr('x'), 10);
+ return transmittingCountX + Math.round(transmitting.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+ })
+ .text(function (d) {
+ return d.activeRemotePortCount;
+ });
+ transmittingCount.append("title").text("Transmitting Remote Process Groups");
+
+ // update not transmitting
+ var notTransmitting = details.select('text.process-group-not-transmitting')
+ .classed('not-transmitting', function (d) {
+ return d.permissions.canRead && d.inactiveRemotePortCount > 0;
+ })
+ .classed('zero', function (d) {
+ return d.permissions.canRead && d.inactiveRemotePortCount === 0;
+ })
+ .attr('x', function () {
+ var transmittingX = parseInt(transmittingCount.attr('x'), 10);
+ return transmittingX + Math.round(transmittingCount.node().getComputedTextLength()) + CONTENTS_SPACER;
+ });
+ var notTransmittingCount = details.select('text.process-group-not-transmitting-count')
+ .attr('x', function () {
+ var notTransmittingCountX = parseInt(notTransmitting.attr('x'), 10);
+ return notTransmittingCountX + Math.round(notTransmitting.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+ })
+ .text(function (d) {
+ return d.inactiveRemotePortCount;
+ });
+ notTransmittingCount.append("title").text("Not transmitting Remote Process Groups")
+
+ // update running
+ var running = details.select('text.process-group-running')
+ .classed('running', function (d) {
+ return d.permissions.canRead && d.component.runningCount > 0;
+ })
+ .classed('zero', function (d) {
+ return d.permissions.canRead && d.component.runningCount === 0;
+ })
+ .attr('x', function () {
+ var notTransmittingX = parseInt(notTransmittingCount.attr('x'), 10);
+ return notTransmittingX + Math.round(notTransmittingCount.node().getComputedTextLength()) + CONTENTS_SPACER;
+ });
+ var runningCount = details.select('text.process-group-running-count')
+ .attr('x', function () {
+ var runningCountX = parseInt(running.attr('x'), 10);
+ return runningCountX + Math.round(running.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+ })
+ .text(function (d) {
+ return d.runningCount;
+ });
+ runningCount.append("title").text("Running Components");
+
+ // update stopped
+ var stopped = details.select('text.process-group-stopped')
+ .classed('stopped', function (d) {
+ return d.permissions.canRead && d.component.stoppedCount > 0;
+ })
+ .classed('zero', function (d) {
+ return d.permissions.canRead && d.component.stoppedCount === 0;
+ })
+ .attr('x', function () {
+ var runningX = parseInt(runningCount.attr('x'), 10);
+ return runningX + Math.round(runningCount.node().getComputedTextLength()) + CONTENTS_SPACER;
+ });
+ var stoppedCount = details.select('text.process-group-stopped-count')
+ .attr('x', function () {
+ var stoppedCountX = parseInt(stopped.attr('x'), 10);
+ return stoppedCountX + Math.round(stopped.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+ })
+ .text(function (d) {
+ return d.stoppedCount;
+ });
+ stoppedCount.append("title").text("Stopped Components");
+
+ // update invalid
+ var invalid = details.select('text.process-group-invalid')
+ .classed('invalid', function (d) {
+ return d.permissions.canRead && d.component.invalidCount > 0;
+ })
+ .classed('zero', function (d) {
+ return d.permissions.canRead && d.component.invalidCount === 0;
+ })
+ .attr('x', function () {
+ var stoppedX = parseInt(stoppedCount.attr('x'), 10);
+ return stoppedX + Math.round(stoppedCount.node().getComputedTextLength()) + CONTENTS_SPACER;
+ });
+ var invalidCount = details.select('text.process-group-invalid-count')
+ .attr('x', function () {
+ var invalidCountX = parseInt(invalid.attr('x'), 10);
+ return invalidCountX + Math.round(invalid.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+ })
+ .text(function (d) {
+ return d.invalidCount;
+ });
+ invalidCount.append("title").text("Invalid Components");
+
+ // update disabled
+ var disabled = details.select('text.process-group-disabled')
+ .classed('disabled', function (d) {
+ return d.permissions.canRead && d.component.disabledCount > 0;
+ })
+ .classed('zero', function (d) {
+ return d.permissions.canRead && d.component.disabledCount === 0;
+ })
+ .attr('x', function () {
+ var invalidX = parseInt(invalidCount.attr('x'), 10);
+ return invalidX + Math.round(invalidCount.node().getComputedTextLength()) + CONTENTS_SPACER;
+ });
+ var disabledCount = details.select('text.process-group-disabled-count')
+ .attr('x', function () {
+ var disabledCountX = parseInt(disabled.attr('x'), 10);
+ return disabledCountX + Math.round(disabled.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+ })
+ .text(function (d) {
+ return d.disabledCount;
+ });
+ disabledCount.append("title").text("Disabled Components");
+
+ // up to date current
+ var upToDate = details.select('text.process-group-up-to-date')
+ .classed('up-to-date', function (d) {
+ return d.permissions.canRead && d.component.upToDateCount > 0;
+ })
+ .classed('zero', function (d) {
+ return d.permissions.canRead && d.component.upToDateCount === 0;
+ });
+ var upToDateCount = details.select('text.process-group-up-to-date-count')
+ .attr('x', function () {
+ var updateToDateCountX = parseInt(upToDate.attr('x'), 10);
+ return updateToDateCountX + Math.round(upToDate.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+ })
+ .text(function (d) {
+ return d.upToDateCount;
+ });
+ upToDateCount.append("title").text("Up to date Versioned Process Groups");
+
+ // update locally modified
+ var locallyModified = details.select('text.process-group-locally-modified')
+ .classed('locally-modified', function (d) {
+ return d.permissions.canRead && d.component.locallyModifiedCount > 0;
+ })
+ .classed('zero', function (d) {
+ return d.permissions.canRead && d.component.locallyModifiedCount === 0;
+ })
+ .attr('x', function () {
+ var upToDateX = parseInt(upToDateCount.attr('x'), 10);
+ return upToDateX + Math.round(upToDateCount.node().getComputedTextLength()) + CONTENTS_SPACER;
+ });
+ var locallyModifiedCount = details.select('text.process-group-locally-modified-count')
+ .attr('x', function () {
+ var locallyModifiedCountX = parseInt(locallyModified.attr('x'), 10);
+ return locallyModifiedCountX + Math.round(locallyModified.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+ })
+ .text(function (d) {
+ return d.locallyModifiedCount;
+ });
+ locallyModifiedCount.append("title").text("Locally modified Versioned Process Groups");
+
+ // update stale
+ var stale = details.select('text.process-group-stale')
+ .classed('stale', function (d) {
+ return d.permissions.canRead && d.component.staleCount > 0;
+ })
+ .classed('zero', function (d) {
+ return d.permissions.canRead && d.component.staleCount === 0;
+ })
+ .attr('x', function () {
+ var locallyModifiedX = parseInt(locallyModifiedCount.attr('x'), 10);
+ return locallyModifiedX + Math.round(locallyModifiedCount.node().getComputedTextLength()) + CONTENTS_SPACER;
+ });
+ var staleCount = details.select('text.process-group-stale-count')
+ .attr('x', function () {
+ var staleCountX = parseInt(stale.attr('x'), 10);
+ return staleCountX + Math.round(stale.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+ })
+ .text(function (d) {
+ return d.staleCount;
+ });
+ staleCount.append("title").text("Stale Versioned Process Groups");
+
+ // update locally modified and stale
+ var locallyModifiedAndStale = details.select('text.process-group-locally-modified-and-stale')
+ .classed('locally-modified-and-stale', function (d) {
+ return d.permissions.canRead && d.component.locallyModifiedAndStaleCount > 0;
+ })
+ .classed('zero', function (d) {
+ return d.permissions.canRead && d.component.locallyModifiedAndStaleCount === 0;
+ })
+ .attr('x', function () {
+ var staleX = parseInt(staleCount.attr('x'), 10);
+ return staleX + Math.round(staleCount.node().getComputedTextLength()) + CONTENTS_SPACER;
+ });
+ var locallyModifiedAndStaleCount = details.select('text.process-group-locally-modified-and-stale-count')
+ .attr('x', function () {
+ var locallyModifiedAndStaleCountX = parseInt(locallyModifiedAndStale.attr('x'), 10);
+ return locallyModifiedAndStaleCountX + Math.round(locallyModifiedAndStale.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+ })
+ .text(function (d) {
+ return d.locallyModifiedAndStaleCount;
+ });
+ locallyModifiedAndStaleCount.append("title").text("Locally modified and stale Versioned Process Groups");
+
+ // update sync failure
+ var syncFailure = details.select('text.process-group-sync-failure')
+ .classed('sync-failure', function (d) {
+ return d.permissions.canRead && d.component.syncFailureCount > 0;
+ })
+ .classed('zero', function (d) {
+ return d.permissions.canRead && d.component.syncFailureCount === 0;
+ })
+ .attr('x', function () {
+ var syncFailureX = parseInt(locallyModifiedAndStaleCount.attr('x'), 10);
+ return syncFailureX + Math.round(locallyModifiedAndStaleCount.node().getComputedTextLength()) + CONTENTS_SPACER - 2;
+ });
+ var syncFailureCount = details.select('text.process-group-sync-failure-count')
+ .attr('x', function () {
+ var syncFailureCountX = parseInt(syncFailure.attr('x'), 10);
+ return syncFailureCountX + Math.round(syncFailure.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+ })
+ .text(function (d) {
+ return d.syncFailureCount;
+ });
+ syncFailureCount.append("title").text("Sync failure Versioned Process Groups");
+
+ /**
+ * update version control information
+ * @author: Renu
+ * @desc for lines 1110-1201: based on state of the process group, environment selection and submit button enable/disable
+ */
+ var versionControl = processGroup.select('text.version-control')
+ .styles({
+ 'visibility': isUnderVersionControl(processGroupData) ? 'visible' : 'hidden',
+ 'fill': function () {
+ if (isUnderVersionControl(processGroupData)) {
+ var vciState = processGroupData.versionedFlowState;
+ if (vciState === 'SYNC_FAILURE') {
+ $('#environmentType').prop('disabled', true);
+ if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
+ $('#operate-submit-btn').prop('disabled', false);
+ }else{$('#operate-submit-btn').prop('disabled', true);}
+
+ return '#666666';
+ } else if (vciState === 'LOCALLY_MODIFIED_AND_STALE') {
+ console.log("locally but stale in style");
+ $('#environmentType').prop('disabled', true);
+ if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
+ $('#operate-submit-btn').prop('disabled', false);
+ }else{$('#operate-submit-btn').prop('disabled', true);}
+
+ return '#BA554A';
+ } else if (vciState === 'STALE') {
+ console.log("stale in style");
+ $('#environmentType').prop('disabled', true);
+ if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
+ $('#operate-submit-btn').prop('disabled', false);
+ }else{$('#operate-submit-btn').prop('disabled', true);}
+ return '#BA554A';
+ } else if (vciState === 'LOCALLY_MODIFIED') {
+ console.log("locally modified in style");
+ $('#environmentType').prop('disabled', true);
+ if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
+ $('#operate-submit-btn').prop('disabled', false);
+ }else{$('#operate-submit-btn').prop('disabled', true);}
+
+ return '#666666';
+ } else {
+ return '#1A9964';
+ $('#environmentType').prop('disabled', false);
+ if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
+ $('#operate-submit-btn').prop('disabled', false);
+ }else{$('#operate-submit-btn').prop('disabled', true);}
+ }
+ } else {
+ $('#environmentType').prop('disabled', true);
+ return '#000';
+ }
+ }
+ })
+ .text(function () {
+ if (isUnderVersionControl(processGroupData)) {
+ var vciState = processGroupData.versionedFlowState;
+ if (vciState === 'SYNC_FAILURE') {
+ $('#environmentType').prop('disabled', true);
+ if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
+ $('#operate-submit-btn').prop('disabled', false);
+ }else{$('#operate-submit-btn').prop('disabled', true);}
+
+ return '\uf128'
+ } else if (vciState === 'LOCALLY_MODIFIED_AND_STALE') {
+ console.log("locally but stale in text");
+ $('#environmentType').prop('disabled', true);
+ if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
+ $('#operate-submit-btn').prop('disabled', false);
+ }else{$('#operate-submit-btn').prop('disabled', true);}
+
+ return '\uf06a';
+ } else if (vciState === 'STALE') {
+ console.log("stale in text");
+ $('#environmentType').prop('disabled', true);
+ if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
+ $('#operate-submit-btn').prop('disabled', false);
+ }else{$('#operate-submit-btn').prop('disabled', true);}
+ return '\uf0aa';
+ } else if (vciState === 'LOCALLY_MODIFIED') {
+ console.log("locally modified in text");
+ $('#environmentType').prop('disabled', true);
+ if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
+ $('#operate-submit-btn').prop('disabled', false);
+ }else{$('#operate-submit-btn').prop('disabled', true);}
+ return '\uf069';
+ } else {
+ return '\uf00c';
+ $('#environmentType').prop('disabled', false);
+ if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
+ $('#operate-submit-btn').prop('disabled', false);
+ }else{$('#operate-submit-btn').prop('disabled', true);}
+ }
+ } else {
+ $('#environmentType').prop('disabled', true);
+ return '';
+ }
+ });
+
+ if (processGroupData.permissions.canRead) {
+ // version control tooltip
+ versionControl.each(function () {
+ // get the tip
+ var tip = d3.select('#version-control-tip-' + processGroupData.id);
+
+ // if there are validation errors generate a tooltip
+ if (isUnderVersionControl(processGroupData)) {
+ // create the tip if necessary
+ if (tip.empty()) {
+ tip = d3.select('#process-group-tooltips').append('div')
+ .attr('id', function () {
+ return 'version-control-tip-' + processGroupData.id;
+ })
+ .attr('class', 'tooltip nifi-tooltip');
+ }
+
+ // update the tip
+ tip.html(function () {
+ var vci = processGroupData.component.versionControlInformation;
+ var versionControlTip = $('<div></div>').text('Tracking to "' + vci.flowName + '" version ' + vci.version + ' in "' + vci.registryName + ' - ' + vci.bucketName + '"');
+ var versionControlStateTip = $('<div></div>').text(nfCommon.getVersionControlTooltip(vci));
+ return $('<div></div>').append(versionControlTip).append('<br/>').append(versionControlStateTip).html();
+ });
+
+ // add the tooltip
+ nfCanvasUtils.canvasTooltip(tip, d3.select(this));
+ } else {
+ // remove the tip if necessary
+ if (!tip.empty()) {
+ tip.remove();
+ }
+ }
+ });
+
+ // update the process group comments
+ processGroup.select('path.component-comments')
+ .style('visibility', nfCommon.isBlank(processGroupData.component.comments) ? 'hidden' : 'visible')
+ .each(function () {
+ // get the tip
+ var tip = d3.select('#comments-tip-' + processGroupData.id);
+
+ // if there are validation errors generate a tooltip
+ if (nfCommon.isBlank(processGroupData.component.comments)) {
+ // remove the tip if necessary
+ if (!tip.empty()) {
+ tip.remove();
+ }
+ } else {
+ // create the tip if necessary
+ if (tip.empty()) {
+ tip = d3.select('#process-group-tooltips').append('div')
+ .attr('id', function () {
+ return 'comments-tip-' + processGroupData.id;
+ })
+ .attr('class', 'tooltip nifi-tooltip');
+ }
+
+ // update the tip
+ tip.text(processGroupData.component.comments);
+
+ // add the tooltip
+ nfCanvasUtils.canvasTooltip(tip, d3.select(this));
+ }
+ });
+
+ // update the process group name
+ processGroup.select('text.process-group-name')
+ .attrs({
+ 'x': function () {
+ if (isUnderVersionControl(processGroupData)) {
+ var versionControlX = parseInt(versionControl.attr('x'), 10);
+ return versionControlX + Math.round(versionControl.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+ } else {
+ return 10;
+ }
+ },
+ 'width': function () {
+ if (isUnderVersionControl(processGroupData)) {
+ var versionControlX = parseInt(versionControl.attr('x'), 10);
+ var processGroupNameX = parseInt(d3.select(this).attr('x'), 10);
+ return 300 - (processGroupNameX - versionControlX);
+ } else {
+ return 300;
+ }
+ }
+ })
+ .each(function (d) {
+ var processGroupName = d3.select(this);
+
+ // reset the process group name to handle any previous state
+ processGroupName.text(null).selectAll('title').remove();
+
+ // apply ellipsis to the process group name as necessary
+ nfCanvasUtils.ellipsis(processGroupName, d.component.name);
+ })
+ .append('title')
+ .text(function (d) {
+ return d.component.name;
+ });
+ } else {
+ // clear the process group comments
+ processGroup.select('path.component-comments').style('visibility', 'hidden');
+
+ // clear the process group name
+ processGroup.select('text.process-group-name')
+ .attrs({
+ 'x': 10,
+ 'width': 316
+ })
+ .text(null);
+
+ // clear tooltips
+ processGroup.call(removeTooltips);
+ }
+
+ // populate the stats
+ processGroup.call(updateProcessGroupStatus);
+ } else {
+ if (processGroupData.permissions.canRead) {
+ // update the process group name
+ processGroup.select('text.process-group-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;
+ }
+ });
+ } else {
+ // clear the process group name
+ processGroup.select('text.process-group-name').text(null);
+ }
+
+ // remove the tooltips
+ processGroup.call(removeTooltips);
+
+ // remove the details if necessary
+ if (!details.empty()) {
+ details.remove();
+ }
+ }
+ });
+ };
+
+ /**
+ * Updates the process group status.
+ *
+ * @param {selection} updated The process groups to be updated
+ */
+ var updateProcessGroupStatus = function (updated) {
+ if (updated.empty()) {
+ return;
+ }
+
+ // queued count value
+ updated.select('text.process-group-queued tspan.count')
+ .text(function (d) {
+ return nfCommon.substringBeforeFirst(d.status.aggregateSnapshot.queued, ' ');
+ });
+
+ // queued size value
+ updated.select('text.process-group-queued tspan.size')
+ .text(function (d) {
+ return ' ' + nfCommon.substringAfterFirst(d.status.aggregateSnapshot.queued, ' ');
+ });
+
+ // in count value
+ updated.select('text.process-group-in tspan.count')
+ .text(function (d) {
+ return nfCommon.substringBeforeFirst(d.status.aggregateSnapshot.input, ' ');
+ });
+
+ // in size value
+ updated.select('text.process-group-in tspan.size')
+ .text(function (d) {
+ return ' ' + nfCommon.substringAfterFirst(d.status.aggregateSnapshot.input, ' ');
+ });
+
+ // in ports value
+ updated.select('text.process-group-in tspan.ports')
+ .text(function (d) {
+ return ' ' + String.fromCharCode(8594) + ' ' + d.inputPortCount;
+ });
+
+ // read/write value
+ updated.select('text.process-group-read-write')
+ .text(function (d) {
+ return d.status.aggregateSnapshot.read + ' / ' + d.status.aggregateSnapshot.written;
+ });
+
+ // out ports value
+ updated.select('text.process-group-out tspan.ports')
+ .text(function (d) {
+ return d.outputPortCount + ' ' + String.fromCharCode(8594) + ' ';
+ });
+
+ // out count value
+ updated.select('text.process-group-out tspan.count')
+ .text(function (d) {
+ return nfCommon.substringBeforeFirst(d.status.aggregateSnapshot.output, ' ');
+ });
+
+ // out size value
+ updated.select('text.process-group-out tspan.size')
+ .text(function (d) {
+ return ' ' + nfCommon.substringAfterFirst(d.status.aggregateSnapshot.output, ' ');
+ });
+
+ updated.each(function (d) {
+ var processGroup = d3.select(this);
+ var offset = 0;
+
+ // -------------------
+ // active thread count
+ // -------------------
+
+ nfCanvasUtils.activeThreadCount(processGroup, d, function (off) {
+ offset = off;
+ });
+
+ // ---------
+ // bulletins
+ // ---------
+
+ processGroup.select('rect.bulletin-background').classed('has-bulletins', function () {
+ return !nfCommon.isEmpty(d.status.aggregateSnapshot.bulletins);
+ });
+
+ nfCanvasUtils.bulletins(processGroup, d, function () {
+ return d3.select('#process-group-tooltips');
+ }, offset);
+ });
+ };
+
+ /**
+ * Removes the process groups in the specified selection.
+ *
+ * @param {selection} removed The process groups to be removed
+ */
+ var removeProcessGroups = function (removed) {
+ if (removed.empty()) {
+ return;
+ }
+
+ removed.call(removeTooltips).remove();
+ };
+
+ /**
+ * Removes the tooltips for the process groups in the specified selection.
+ *
+ * @param {selection} removed
+ */
+ var removeTooltips = function (removed) {
+ removed.each(function (d) {
+ // remove any associated tooltips
+ $('#bulletin-tip-' + d.id).remove();
+ $('#version-control-tip-' + d.id).remove();
+ $('#comments-tip-' + d.id).remove();
+ });
+ };
+
+ var nfProcessGroup = {
+ /**
+ * Initializes of the Process Group handler.
+ *
+ * @param nfConnectableRef The nfConnectable module.
+ * @param nfDraggableRef The nfDraggable module.
+ * @param nfSelectableRef The nfSelectable module.
+ * @param nfContextMenuRef The nfContextMenu module.
+ */
+ init: function (nfConnectableRef, nfDraggableRef, nfSelectableRef, nfContextMenuRef) {
+ nfConnectable = nfConnectableRef;
+ nfDraggable = nfDraggableRef;
+ nfSelectable = nfSelectableRef;
+ nfContextMenu = nfContextMenuRef;
+
+ processGroupMap = d3.map();
+ removedCache = d3.map();
+ addedCache = d3.map();
+
+ // create the process group container
+ processGroupContainer = d3.select('#canvas').append('g')
+ .attrs({
+ 'pointer-events': 'all',
+ 'class': 'process-groups'
+ });
+ },
+
+ /**
+ * Adds the specified process group entity.
+ *
+ * @param processGroupEntities The process group
+ * @param options Configuration options
+ */
+ add: function (processGroupEntities, options) {
+ var selectAll = false;
+ if (nfCommon.isDefinedAndNotNull(options)) {
+ selectAll = nfCommon.isDefinedAndNotNull(options.selectAll) ? options.selectAll : selectAll;
+ }
+
+ // get the current time
+ var now = new Date().getTime();
+
+ var add = function (processGroupEntity) {
+ addedCache.set(processGroupEntity.id, now);
+
+ // add the process group
+ processGroupMap.set(processGroupEntity.id, $.extend({
+ type: 'ProcessGroup',
+ dimensions: dimensions
+ }, processGroupEntity));
+ };
+
+ // determine how to handle the specified process groups
+ if ($.isArray(processGroupEntities)) {
+ $.each(processGroupEntities, function (_, processGroupEntity) {
+ add(processGroupEntity);
+ });
+ } else if (nfCommon.isDefinedAndNotNull(processGroupEntities)) {
+ add(processGroupEntities);
+ }
+
+ // select
+ var selection = select();
+
+ // enter
+ var entered = renderProcessGroups(selection.enter(), selectAll);
+
+ // update
+ updateProcessGroups(selection.merge(entered));
+ },
+
+ /**
+ * Populates the graph with the specified process groups.
+ *
+ * @argument {object | array} processGroupEntities The process groups to add
+ * @argument {object} options Configuration options
+ */
+ set: function (processGroupEntities, options) {
+ var selectAll = false;
+ var transition = false;
+ var overrideRevisionCheck = false;
+ if (nfCommon.isDefinedAndNotNull(options)) {
+ selectAll = nfCommon.isDefinedAndNotNull(options.selectAll) ? options.selectAll : selectAll;
+ transition = nfCommon.isDefinedAndNotNull(options.transition) ? options.transition : transition;
+ overrideRevisionCheck = nfCommon.isDefinedAndNotNull(options.overrideRevisionCheck) ? options.overrideRevisionCheck : overrideRevisionCheck;
+ }
+
+ var set = function (proposedProcessGroupEntity) {
+ var currentProcessGroupEntity = processGroupMap.get(proposedProcessGroupEntity.id);
+
+ // set the process group if appropriate due to revision and wasn't previously removed
+ if ((nfClient.isNewerRevision(currentProcessGroupEntity, proposedProcessGroupEntity) && !removedCache.has(proposedProcessGroupEntity.id)) || overrideRevisionCheck === true) {
+ processGroupMap.set(proposedProcessGroupEntity.id, $.extend({
+ type: 'ProcessGroup',
+ dimensions: dimensions
+ }, proposedProcessGroupEntity));
+ }
+ };
+
+ // determine how to handle the specified process groups
+ if ($.isArray(processGroupEntities)) {
+ $.each(processGroupMap.keys(), function (_, key) {
+ var currentProcessGroupEntity = processGroupMap.get(key);
+ var isPresent = $.grep(processGroupEntities, function (proposedProcessGroupEntity) {
+ return proposedProcessGroupEntity.id === currentProcessGroupEntity.id;
+ });
+
+ // if the current process group is not present and was not recently added, remove it
+ if (isPresent.length === 0 && !addedCache.has(key)) {
+ processGroupMap.remove(key);
+ }
+ });
+ $.each(processGroupEntities, function (_, processGroupEntity) {
+ set(processGroupEntity);
+ });
+ } else if (nfCommon.isDefinedAndNotNull(processGroupEntities)) {
+ set(processGroupEntities);
+ }
+
+ // select
+ var selection = select();
+
+ // enter
+ var entered = renderProcessGroups(selection.enter(), selectAll);
+
+ // update
+ var updated = selection.merge(entered);
+ updated.call(updateProcessGroups).call(nfCanvasUtils.position, transition);
+
+ // exit
+ selection.exit().call(removeProcessGroups);
+ },
+
+ /**
+ * If the process group id is specified it is returned. If no process group id
+ * specified, all process groups are returned.
+ *
+ * @param {string} id
+ */
+ get: function (id) {
+ if (nfCommon.isUndefined(id)) {
+ return processGroupMap.values();
+ } else {
+ return processGroupMap.get(id);
+ }
+ },
+
+ /**
+ * If the process group id is specified it is refresh according to the current
+ * state. If no process group id is specified, all process groups are refreshed.
+ *
+ * @param {string} id Optional
+ */
+ refresh: function (id) {
+ if (nfCommon.isDefinedAndNotNull(id)) {
+ d3.select('#id-' + id).call(updateProcessGroups);
+ } else {
+ d3.selectAll('g.process-group').call(updateProcessGroups);
+ }
+ },
+
+ /**
+ * Refreshes the components necessary after a pan event.
+ */
+ pan: function () {
+ d3.selectAll('g.process-group.entering, g.process-group.leaving').call(updateProcessGroups);
+ },
+
+ /**
+ * Reloads the process group state from the server and refreshes the UI.
+ * If the process group is currently unknown, this function reloads the canvas.
+ *
+ * @param {string} id The process group id
+ */
+ reload: function (id) {
+ if (processGroupMap.has(id)) {
+ var processGroupEntity = processGroupMap.get(id);
+ return $.ajax({
+ type: 'GET',
+ url: processGroupEntity.uri,
+ dataType: 'json'
+ }).done(function (response) {
+ nfProcessGroup.set(response);
+ });
+ }
+ },
+
+ /**
+ * Positions the component.
+ *
+ * @param {string} id The id
+ */
+ position: function (id) {
+ d3.select('#id-' + id).call(nfCanvasUtils.position);
+ },
+
+ /**
+ * Removes the specified process group.
+ *
+ * @param {string} processGroupIds The process group id(s)
+ */
+ remove: function (processGroupIds) {
+ var now = new Date().getTime();
+
+ if ($.isArray(processGroupIds)) {
+ $.each(processGroupIds, function (_, processGroupId) {
+ removedCache.set(processGroupId, now);
+ processGroupMap.remove(processGroupId);
+ });
+ } else {
+ removedCache.set(processGroupIds, now);
+ processGroupMap.remove(processGroupIds);
+ }
+
+ // apply the selection and handle all removed process groups
+ select().exit().call(removeProcessGroups);
+ },
+
+ /**
+ * Removes all process groups.
+ */
+ removeAll: function () {
+ nfProcessGroup.remove(processGroupMap.keys());
+ },
+
+ /**
+ * Expires the caches up to the specified timestamp.
+ *
+ * @param timestamp
+ */
+ expireCaches: function (timestamp) {
+ var expire = function (cache) {
+ cache.each(function (entryTimestamp, id) {
+ if (timestamp > entryTimestamp) {
+ cache.remove(id);
+ }
+ });
+ };
+
+ expire(addedCache);
+ expire(removedCache);
+ },
+
+ /**
+ * Enters the specified group.
+ *
+ * @param {string} groupId
+ */
+ enterGroup: function (groupId) {
+
+ // hide the context menu
+ nfContextMenu.hide();
+
+ // set the new group id
+ nfCanvasUtils.setGroupId(groupId);
+
+ // reload the graph
+ return nfCanvasUtils.reload().done(function () {
+
+ // attempt to restore the view
+ var viewRestored = nfCanvasUtils.restoreUserView();
+
+ // if the view was not restore attempt to fit
+ if (viewRestored === false) {
+ nfCanvasUtils.fitCanvas();
+ }
+
+ // update URL deep linking params
+ nfCanvasUtils.setURLParameters(groupId, d3.select());
+
+ }).fail(function () {
+ nfDialog.showOkDialog({
+ headerText: 'Process Group',
+ dialogContent: 'Unable to enter the selected group.'
+ });
+ });
+ }
+ };
+
+ return nfProcessGroup;
+}));