2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * Modifications to the original nifi code for the ONAP project are made
18 * available under the Apache License, Version 2.0
21 /* global d3, define, module, require, exports */
23 (function (root, factory) {
24 if (typeof define === 'function' && define.amd) {
32 function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog) {
33 return (nf.ProcessGroup = factory($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog));
35 } else if (typeof exports === 'object' && typeof module === 'object') {
36 module.exports = (nf.ProcessGroup =
37 factory(require('jquery'),
39 require('nf.Connection'),
42 require('nf.CanvasUtils'),
43 require('nf.Dialog')));
45 nf.ProcessGroup = factory(root.$,
54 (this, function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog) {
62 var PREVIEW_NAME_LENGTH = 30;
69 // ----------------------------
70 // process groups currently on the graph
71 // ----------------------------
75 // -----------------------------------------------------------
76 // cache for components that are added/removed from the canvas
77 // -----------------------------------------------------------
82 // --------------------
83 // component containers
84 // --------------------
86 var processGroupContainer;
88 // --------------------------
89 // privately scoped functions
90 // --------------------------
93 * Determines whether the specified process group is under version control.
97 var isUnderVersionControl = function (d) {
98 return nfCommon.isDefinedAndNotNull(d.versionedFlowState);
102 * Selects the process group elements against the current process group map.
104 var select = function () {
105 return processGroupContainer.selectAll('g.process-group').data(processGroupMap.values(), function (d) {
112 * Renders the process groups in the specified selection.
114 * @param {selection} entered The selection of process groups to be rendered
115 * @param {boolean} selected Whether the process group should be selected
116 * @return the entered selection
118 var renderProcessGroups = function (entered, selected) {
119 if (entered.empty()) {
123 var processGroup = entered.append('g')
128 'class': 'process-group component'
130 .classed('selected', selected)
131 .call(nfCanvasUtils.position);
137 // process group border
138 processGroup.append('rect')
141 'width': function (d) {
142 return d.dimensions.width;
144 'height': function (d) {
145 return d.dimensions.height;
147 'fill': 'transparent',
148 'stroke': 'transparent'
151 // process group body
152 processGroup.append('rect')
155 'width': function (d) {
156 return d.dimensions.width;
158 'height': function (d) {
159 return d.dimensions.height;
161 'filter': 'url(#component-drop-shadow)',
165 // process group name background
166 processGroup.append('rect')
168 'width': function (d) {
169 return d.dimensions.width;
175 // process group name
176 processGroup.append('text')
182 'class': 'process-group-name'
185 // process group name
186 processGroup.append('text')
190 'class': 'version-control'
193 console.log(processGroup);
196 // always support selecting and navigation
197 processGroup.on('dblclick', function (d) {
198 // enter this group on double click
199 nfProcessGroup.enterGroup(d.id);
201 .call(nfSelectable.activate).call(nfContextMenu.activate);
203 // only support dragging, connection, and drag and drop if appropriate
204 processGroup.filter(function (d) {
205 return d.permissions.canWrite && d.permissions.canRead;
207 .on('mouseover.drop', function (d) {
208 // Using mouseover/out to workaround chrome issue #122746
210 // get the target and ensure its not already been marked for drop
211 var target = d3.select(this);
212 if (!target.classed('drop')) {
213 var targetData = target.datum();
215 // see if there is a selection being dragged
216 var drag = d3.select('rect.drag-selection');
218 // filter the current selection by this group
219 var selection = nfCanvasUtils.getSelection().filter(function (d) {
220 return targetData.id === d.id;
223 // ensure this group isn't in the selection
224 if (selection.empty()) {
225 // mark that we are hovering over a drop area if appropriate
226 target.classed('drop', function () {
227 // get the current selection and ensure its disconnected
228 return nfConnection.isDisconnected(nfCanvasUtils.getSelection());
234 .on('mouseout.drop', function (d) {
235 // mark that we are no longer hovering over a drop area unconditionally
236 d3.select(this).classed('drop', false);
238 .call(nfDraggable.activate)
239 .call(nfConnectable.activate);
244 // attempt of space between component count and icon for process group contents
245 var CONTENTS_SPACER = 10;
246 var CONTENTS_VALUE_SPACER = 5;
249 * Updates the process groups in the specified selection.
251 * @param {selection} updated The process groups to be updated
253 var updateProcessGroups = function (updated) {
254 if (updated.empty()) {
258 // process group border authorization
259 updated.select('rect.border')
260 .classed('unauthorized', function (d) {
261 return d.permissions.canRead === false;
264 // process group body authorization
265 updated.select('rect.body')
266 .classed('unauthorized', function (d) {
267 return d.permissions.canRead === false;
270 updated.each(function (processGroupData) {
271 var processGroup = d3.select(this);
272 var details = processGroup.select('g.process-group-details');
274 // update the component behavior as appropriate
275 nfCanvasUtils.editable(processGroup, nfConnectable, nfDraggable);
277 // if this processor is visible, render everything
278 if (processGroup.classed('visible')) {
279 if (details.empty()) {
280 details = processGroup.append('g').attr('class', 'process-group-details');
282 // -------------------
283 // contents background
284 // -------------------
286 details.append('rect')
290 'width': function () {
291 return processGroupData.dimensions.width
297 details.append('rect')
301 return processGroupData.dimensions.height - 24;
303 'width': function () {
304 return processGroupData.dimensions.width;
315 // details.append('text')
319 // 'class': 'process-group-transmitting process-group-contents-icon',
320 // 'font-family': 'FontAwesome'
324 // .text("Transmitting Remote Process Groups");
327 // transmitting count
328 // details.append('text')
331 // 'class': 'process-group-transmitting-count process-group-contents-count'
334 // not transmitting icon
335 // details.append('text')
338 // 'class': 'process-group-not-transmitting process-group-contents-icon',
339 // 'font-family': 'flowfont'
343 // .text("Not Transmitting Remote Process Groups");
345 // not transmitting count
346 // details.append('text')
349 // 'class': 'process-group-not-transmitting-count process-group-contents-count'
353 // details.append('text')
356 // 'class': 'process-group-running process-group-contents-icon',
357 // 'font-family': 'FontAwesome'
361 // .text("Running Components");
364 // details.append('text')
367 // 'class': 'process-group-running-count process-group-contents-count'
371 // details.append('text')
374 // 'class': 'process-group-stopped process-group-contents-icon',
375 // 'font-family': 'FontAwesome'
379 // .text("Stopped Components");
382 // details.append('text')
385 // 'class': 'process-group-stopped-count process-group-contents-count'
389 // details.append('text')
392 // 'class': 'process-group-invalid process-group-contents-icon',
393 // 'font-family': 'FontAwesome'
397 // .text("Invalid Components");
400 // details.append('text')
403 // 'class': 'process-group-invalid-count process-group-contents-count'
407 // details.append('text')
410 // 'class': 'process-group-disabled process-group-contents-icon',
411 // 'font-family': 'flowfont'
415 // .text("Disabled Components");
418 // details.append('text')
421 // 'class': 'process-group-disabled-count process-group-contents-count'
425 details.append('text')
429 return processGroupData.dimensions.height - 7;
431 'class': 'process-group-up-to-date process-group-contents-icon',
432 'font-family': 'FontAwesome'
436 .text("Up to date Versioned Process Groups");
439 details.append('text')
442 return processGroupData.dimensions.height - 7;
444 'class': 'process-group-up-to-date-count process-group-contents-count'
447 // locally modified icon
448 details.append('text')
451 return processGroupData.dimensions.height - 7;
453 'class': 'process-group-locally-modified process-group-contents-icon',
454 'font-family': 'FontAwesome'
458 .text("Locally modified Versioned Process Groups");
460 // locally modified count
461 details.append('text')
464 return processGroupData.dimensions.height - 7;
466 'class': 'process-group-locally-modified-count process-group-contents-count'
470 details.append('text')
473 return processGroupData.dimensions.height - 7;
475 'class': 'process-group-stale process-group-contents-icon',
476 'font-family': 'FontAwesome'
480 .text("Stale Versioned Process Groups");
483 details.append('text')
486 return processGroupData.dimensions.height - 7;
488 'class': 'process-group-stale-count process-group-contents-count'
491 // locally modified and stale icon
492 details.append('text')
495 return processGroupData.dimensions.height - 7;
497 'class': 'process-group-locally-modified-and-stale process-group-contents-icon',
498 'font-family': 'FontAwesome'
502 .text("Locally modified and stale Versioned Process Groups");
504 // locally modified and stale count
505 details.append('text')
508 return processGroupData.dimensions.height - 7;
510 'class': 'process-group-locally-modified-and-stale-count process-group-contents-count'
514 details.append('text')
517 return processGroupData.dimensions.height - 7;
519 'class': 'process-group-sync-failure process-group-contents-icon',
520 'font-family': 'FontAwesome'
524 .text("Sync failure Versioned Process Groups");
526 // sync failure count
527 details.append('text')
530 return processGroupData.dimensions.height - 7;
532 'class': 'process-group-sync-failure-count process-group-contents-count'
540 details.append('rect')
542 'width': function () {
543 return processGroupData.dimensions.width;
552 details.append('rect')
554 'width': function () {
555 return processGroupData.dimensions.width;
564 details.append('rect')
566 'width': function () {
567 return processGroupData.dimensions.width;
576 details.append('rect')
578 'width': function () {
579 return processGroupData.dimensions.width;
588 details.append('rect')
590 'width': function () {
591 return processGroupData.dimensions.width;
600 details.append('rect')
602 'width': function () {
603 return processGroupData.dimensions.width;
612 details.append('rect')
614 'width': function () {
615 return processGroupData.dimensions.width;
627 // stats label container
628 var processGroupStatsLabel = details.append('g')
630 'transform': 'translate(6, 75)'
634 processGroupStatsLabel.append('text')
640 'class': 'stats-label'
645 processGroupStatsLabel.append('text')
651 'class': 'stats-label'
656 processGroupStatsLabel.append('text')
662 'class': 'stats-label'
667 processGroupStatsLabel.append('text')
673 'class': 'stats-label'
677 // stats value container
678 var processGroupStatsValue = details.append('g')
680 'transform': 'translate(95, 75)'
684 var queuedText = processGroupStatsValue.append('text')
690 'class': 'process-group-queued stats-value'
694 queuedText.append('tspan')
700 queuedText.append('tspan')
706 var inText = processGroupStatsValue.append('text')
712 'class': 'process-group-in stats-value'
716 inText.append('tspan')
722 inText.append('tspan')
728 inText.append('tspan')
734 processGroupStatsValue.append('text')
740 'class': 'process-group-read-write stats-value'
744 var outText = processGroupStatsValue.append('text')
750 'class': 'process-group-out stats-value'
754 outText.append('tspan')
760 outText.append('tspan')
766 outText.append('tspan')
771 // stats value container
772 var processGroupStatsInfo = details.append('g')
774 'transform': 'translate(335, 75)'
778 processGroupStatsInfo.append('text')
784 'class': 'stats-info'
789 processGroupStatsInfo.append('text')
795 'class': 'stats-info'
800 processGroupStatsInfo.append('text')
806 'class': 'stats-info'
814 details.append('path')
816 'class': 'component-comments',
817 'transform': 'translate(' + (processGroupData.dimensions.width - 2) + ', ' + (processGroupData.dimensions.height - 10) + ')',
818 'd': 'm0,0 l0,8 l-8,0 z'
821 // -------------------
822 // active thread count
823 // -------------------
825 // active thread count
826 details.append('text')
828 'class': 'active-thread-count-icon',
833 // active thread icon
834 details.append('text')
836 'class': 'active-thread-count',
844 // bulletin background
845 details.append('rect')
847 'class': 'bulletin-background',
849 return processGroupData.dimensions.width - 24;
857 details.append('text')
859 'class': 'bulletin-icon',
861 return processGroupData.dimensions.width - 17;
868 // update transmitting
869 var transmitting = details.select('text.process-group-transmitting')
870 .classed('transmitting', function (d) {
871 return d.permissions.canRead && d.activeRemotePortCount > 0;
873 .classed('zero', function (d) {
874 return d.permissions.canRead && d.activeRemotePortCount === 0;
876 var transmittingCount = details.select('text.process-group-transmitting-count')
877 .attr('x', function () {
878 var transmittingCountX = parseInt(transmitting.attr('x'), 10);
879 return transmittingCountX + Math.round(transmitting.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
882 return d.activeRemotePortCount;
884 transmittingCount.append("title").text("Transmitting Remote Process Groups");
886 // update not transmitting
887 var notTransmitting = details.select('text.process-group-not-transmitting')
888 .classed('not-transmitting', function (d) {
889 return d.permissions.canRead && d.inactiveRemotePortCount > 0;
891 .classed('zero', function (d) {
892 return d.permissions.canRead && d.inactiveRemotePortCount === 0;
894 .attr('x', function () {
895 var transmittingX = parseInt(transmittingCount.attr('x'), 10);
896 return transmittingX + Math.round(transmittingCount.node().getComputedTextLength()) + CONTENTS_SPACER;
898 var notTransmittingCount = details.select('text.process-group-not-transmitting-count')
899 .attr('x', function () {
900 var notTransmittingCountX = parseInt(notTransmitting.attr('x'), 10);
901 return notTransmittingCountX + Math.round(notTransmitting.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
904 return d.inactiveRemotePortCount;
906 notTransmittingCount.append("title").text("Not transmitting Remote Process Groups")
909 var running = details.select('text.process-group-running')
910 .classed('running', function (d) {
911 return d.permissions.canRead && d.component.runningCount > 0;
913 .classed('zero', function (d) {
914 return d.permissions.canRead && d.component.runningCount === 0;
916 .attr('x', function () {
917 var notTransmittingX = parseInt(notTransmittingCount.attr('x'), 10);
918 return notTransmittingX + Math.round(notTransmittingCount.node().getComputedTextLength()) + CONTENTS_SPACER;
920 var runningCount = details.select('text.process-group-running-count')
921 .attr('x', function () {
922 var runningCountX = parseInt(running.attr('x'), 10);
923 return runningCountX + Math.round(running.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
926 return d.runningCount;
928 runningCount.append("title").text("Running Components");
931 var stopped = details.select('text.process-group-stopped')
932 .classed('stopped', function (d) {
933 return d.permissions.canRead && d.component.stoppedCount > 0;
935 .classed('zero', function (d) {
936 return d.permissions.canRead && d.component.stoppedCount === 0;
938 .attr('x', function () {
939 var runningX = parseInt(runningCount.attr('x'), 10);
940 return runningX + Math.round(runningCount.node().getComputedTextLength()) + CONTENTS_SPACER;
942 var stoppedCount = details.select('text.process-group-stopped-count')
943 .attr('x', function () {
944 var stoppedCountX = parseInt(stopped.attr('x'), 10);
945 return stoppedCountX + Math.round(stopped.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
948 return d.stoppedCount;
950 stoppedCount.append("title").text("Stopped Components");
953 var invalid = details.select('text.process-group-invalid')
954 .classed('invalid', function (d) {
955 return d.permissions.canRead && d.component.invalidCount > 0;
957 .classed('zero', function (d) {
958 return d.permissions.canRead && d.component.invalidCount === 0;
960 .attr('x', function () {
961 var stoppedX = parseInt(stoppedCount.attr('x'), 10);
962 return stoppedX + Math.round(stoppedCount.node().getComputedTextLength()) + CONTENTS_SPACER;
964 var invalidCount = details.select('text.process-group-invalid-count')
965 .attr('x', function () {
966 var invalidCountX = parseInt(invalid.attr('x'), 10);
967 return invalidCountX + Math.round(invalid.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
970 return d.invalidCount;
972 invalidCount.append("title").text("Invalid Components");
975 var disabled = details.select('text.process-group-disabled')
976 .classed('disabled', function (d) {
977 return d.permissions.canRead && d.component.disabledCount > 0;
979 .classed('zero', function (d) {
980 return d.permissions.canRead && d.component.disabledCount === 0;
982 .attr('x', function () {
983 var invalidX = parseInt(invalidCount.attr('x'), 10);
984 return invalidX + Math.round(invalidCount.node().getComputedTextLength()) + CONTENTS_SPACER;
986 var disabledCount = details.select('text.process-group-disabled-count')
987 .attr('x', function () {
988 var disabledCountX = parseInt(disabled.attr('x'), 10);
989 return disabledCountX + Math.round(disabled.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
992 return d.disabledCount;
994 disabledCount.append("title").text("Disabled Components");
996 // up to date current
997 var upToDate = details.select('text.process-group-up-to-date')
998 .classed('up-to-date', function (d) {
999 return d.permissions.canRead && d.component.upToDateCount > 0;
1001 .classed('zero', function (d) {
1002 return d.permissions.canRead && d.component.upToDateCount === 0;
1004 var upToDateCount = details.select('text.process-group-up-to-date-count')
1005 .attr('x', function () {
1006 var updateToDateCountX = parseInt(upToDate.attr('x'), 10);
1007 return updateToDateCountX + Math.round(upToDate.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
1009 .text(function (d) {
1010 return d.upToDateCount;
1012 upToDateCount.append("title").text("Up to date Versioned Process Groups");
1014 // update locally modified
1015 var locallyModified = details.select('text.process-group-locally-modified')
1016 .classed('locally-modified', function (d) {
1017 return d.permissions.canRead && d.component.locallyModifiedCount > 0;
1019 .classed('zero', function (d) {
1020 return d.permissions.canRead && d.component.locallyModifiedCount === 0;
1022 .attr('x', function () {
1023 var upToDateX = parseInt(upToDateCount.attr('x'), 10);
1024 return upToDateX + Math.round(upToDateCount.node().getComputedTextLength()) + CONTENTS_SPACER;
1026 var locallyModifiedCount = details.select('text.process-group-locally-modified-count')
1027 .attr('x', function () {
1028 var locallyModifiedCountX = parseInt(locallyModified.attr('x'), 10);
1029 return locallyModifiedCountX + Math.round(locallyModified.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
1031 .text(function (d) {
1032 return d.locallyModifiedCount;
1034 locallyModifiedCount.append("title").text("Locally modified Versioned Process Groups");
1037 var stale = details.select('text.process-group-stale')
1038 .classed('stale', function (d) {
1039 return d.permissions.canRead && d.component.staleCount > 0;
1041 .classed('zero', function (d) {
1042 return d.permissions.canRead && d.component.staleCount === 0;
1044 .attr('x', function () {
1045 var locallyModifiedX = parseInt(locallyModifiedCount.attr('x'), 10);
1046 return locallyModifiedX + Math.round(locallyModifiedCount.node().getComputedTextLength()) + CONTENTS_SPACER;
1048 var staleCount = details.select('text.process-group-stale-count')
1049 .attr('x', function () {
1050 var staleCountX = parseInt(stale.attr('x'), 10);
1051 return staleCountX + Math.round(stale.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
1053 .text(function (d) {
1054 return d.staleCount;
1056 staleCount.append("title").text("Stale Versioned Process Groups");
1058 // update locally modified and stale
1059 var locallyModifiedAndStale = details.select('text.process-group-locally-modified-and-stale')
1060 .classed('locally-modified-and-stale', function (d) {
1061 return d.permissions.canRead && d.component.locallyModifiedAndStaleCount > 0;
1063 .classed('zero', function (d) {
1064 return d.permissions.canRead && d.component.locallyModifiedAndStaleCount === 0;
1066 .attr('x', function () {
1067 var staleX = parseInt(staleCount.attr('x'), 10);
1068 return staleX + Math.round(staleCount.node().getComputedTextLength()) + CONTENTS_SPACER;
1070 var locallyModifiedAndStaleCount = details.select('text.process-group-locally-modified-and-stale-count')
1071 .attr('x', function () {
1072 var locallyModifiedAndStaleCountX = parseInt(locallyModifiedAndStale.attr('x'), 10);
1073 return locallyModifiedAndStaleCountX + Math.round(locallyModifiedAndStale.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
1075 .text(function (d) {
1076 return d.locallyModifiedAndStaleCount;
1078 locallyModifiedAndStaleCount.append("title").text("Locally modified and stale Versioned Process Groups");
1080 // update sync failure
1081 var syncFailure = details.select('text.process-group-sync-failure')
1082 .classed('sync-failure', function (d) {
1083 return d.permissions.canRead && d.component.syncFailureCount > 0;
1085 .classed('zero', function (d) {
1086 return d.permissions.canRead && d.component.syncFailureCount === 0;
1088 .attr('x', function () {
1089 var syncFailureX = parseInt(locallyModifiedAndStaleCount.attr('x'), 10);
1090 return syncFailureX + Math.round(locallyModifiedAndStaleCount.node().getComputedTextLength()) + CONTENTS_SPACER - 2;
1092 var syncFailureCount = details.select('text.process-group-sync-failure-count')
1093 .attr('x', function () {
1094 var syncFailureCountX = parseInt(syncFailure.attr('x'), 10);
1095 return syncFailureCountX + Math.round(syncFailure.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
1097 .text(function (d) {
1098 return d.syncFailureCount;
1100 syncFailureCount.append("title").text("Sync failure Versioned Process Groups");
1103 * update version control information
1105 * @desc for lines 1110-1201: based on state of the process group, environment selection and submit button enable/disable
1107 var versionControl = processGroup.select('text.version-control')
1109 'visibility': isUnderVersionControl(processGroupData) ? 'visible' : 'hidden',
1110 'fill': function () {
1111 if (isUnderVersionControl(processGroupData)) {
1112 var vciState = processGroupData.versionedFlowState;
1113 if (vciState === 'SYNC_FAILURE') {
1114 $('#environmentType').prop('disabled', true);
1115 if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
1116 $('#operate-submit-btn').prop('disabled', false);
1117 }else{$('#operate-submit-btn').prop('disabled', true);}
1120 } else if (vciState === 'LOCALLY_MODIFIED_AND_STALE') {
1121 console.log("locally but stale in style");
1122 $('#environmentType').prop('disabled', true);
1123 if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
1124 $('#operate-submit-btn').prop('disabled', false);
1125 }else{$('#operate-submit-btn').prop('disabled', true);}
1128 } else if (vciState === 'STALE') {
1129 console.log("stale in style");
1130 $('#environmentType').prop('disabled', true);
1131 if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
1132 $('#operate-submit-btn').prop('disabled', false);
1133 }else{$('#operate-submit-btn').prop('disabled', true);}
1135 } else if (vciState === 'LOCALLY_MODIFIED') {
1136 console.log("locally modified in style");
1137 $('#environmentType').prop('disabled', true);
1138 if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
1139 $('#operate-submit-btn').prop('disabled', false);
1140 }else{$('#operate-submit-btn').prop('disabled', true);}
1145 $('#environmentType').prop('disabled', false);
1146 if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
1147 $('#operate-submit-btn').prop('disabled', false);
1148 }else{$('#operate-submit-btn').prop('disabled', true);}
1151 $('#environmentType').prop('disabled', true);
1157 if (isUnderVersionControl(processGroupData)) {
1158 var vciState = processGroupData.versionedFlowState;
1159 if (vciState === 'SYNC_FAILURE') {
1160 $('#environmentType').prop('disabled', true);
1161 if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
1162 $('#operate-submit-btn').prop('disabled', false);
1163 }else{$('#operate-submit-btn').prop('disabled', true);}
1166 } else if (vciState === 'LOCALLY_MODIFIED_AND_STALE') {
1167 console.log("locally but stale in text");
1168 $('#environmentType').prop('disabled', true);
1169 if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
1170 $('#operate-submit-btn').prop('disabled', false);
1171 }else{$('#operate-submit-btn').prop('disabled', true);}
1174 } else if (vciState === 'STALE') {
1175 console.log("stale in text");
1176 $('#environmentType').prop('disabled', true);
1177 if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
1178 $('#operate-submit-btn').prop('disabled', false);
1179 }else{$('#operate-submit-btn').prop('disabled', true);}
1181 } else if (vciState === 'LOCALLY_MODIFIED') {
1182 console.log("locally modified in text");
1183 $('#environmentType').prop('disabled', true);
1184 if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
1185 $('#operate-submit-btn').prop('disabled', false);
1186 }else{$('#operate-submit-btn').prop('disabled', true);}
1190 $('#environmentType').prop('disabled', false);
1191 if($('#environmentType').val() && !$('#environmentType').prop('disabled')){
1192 $('#operate-submit-btn').prop('disabled', false);
1193 }else{$('#operate-submit-btn').prop('disabled', true);}
1196 $('#environmentType').prop('disabled', true);
1201 if (processGroupData.permissions.canRead) {
1202 // version control tooltip
1203 versionControl.each(function () {
1205 var tip = d3.select('#version-control-tip-' + processGroupData.id);
1207 // if there are validation errors generate a tooltip
1208 if (isUnderVersionControl(processGroupData)) {
1209 // create the tip if necessary
1211 tip = d3.select('#process-group-tooltips').append('div')
1212 .attr('id', function () {
1213 return 'version-control-tip-' + processGroupData.id;
1215 .attr('class', 'tooltip nifi-tooltip');
1219 tip.html(function () {
1220 var vci = processGroupData.component.versionControlInformation;
1221 var versionControlTip = $('<div></div>').text('Tracking to "' + vci.flowName + '" version ' + vci.version + ' in "' + vci.registryName + ' - ' + vci.bucketName + '"');
1222 var versionControlStateTip = $('<div></div>').text(nfCommon.getVersionControlTooltip(vci));
1223 return $('<div></div>').append(versionControlTip).append('<br/>').append(versionControlStateTip).html();
1227 nfCanvasUtils.canvasTooltip(tip, d3.select(this));
1229 // remove the tip if necessary
1236 // update the process group comments
1237 processGroup.select('path.component-comments')
1238 .style('visibility', nfCommon.isBlank(processGroupData.component.comments) ? 'hidden' : 'visible')
1241 var tip = d3.select('#comments-tip-' + processGroupData.id);
1243 // if there are validation errors generate a tooltip
1244 if (nfCommon.isBlank(processGroupData.component.comments)) {
1245 // remove the tip if necessary
1250 // create the tip if necessary
1252 tip = d3.select('#process-group-tooltips').append('div')
1253 .attr('id', function () {
1254 return 'comments-tip-' + processGroupData.id;
1256 .attr('class', 'tooltip nifi-tooltip');
1260 tip.text(processGroupData.component.comments);
1263 nfCanvasUtils.canvasTooltip(tip, d3.select(this));
1267 // update the process group name
1268 processGroup.select('text.process-group-name')
1271 if (isUnderVersionControl(processGroupData)) {
1272 var versionControlX = parseInt(versionControl.attr('x'), 10);
1273 return versionControlX + Math.round(versionControl.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
1278 'width': function () {
1279 if (isUnderVersionControl(processGroupData)) {
1280 var versionControlX = parseInt(versionControl.attr('x'), 10);
1281 var processGroupNameX = parseInt(d3.select(this).attr('x'), 10);
1282 return 300 - (processGroupNameX - versionControlX);
1288 .each(function (d) {
1289 var processGroupName = d3.select(this);
1291 // reset the process group name to handle any previous state
1292 processGroupName.text(null).selectAll('title').remove();
1294 // apply ellipsis to the process group name as necessary
1295 nfCanvasUtils.ellipsis(processGroupName, d.component.name);
1298 .text(function (d) {
1299 return d.component.name;
1302 // clear the process group comments
1303 processGroup.select('path.component-comments').style('visibility', 'hidden');
1305 // clear the process group name
1306 processGroup.select('text.process-group-name')
1314 processGroup.call(removeTooltips);
1317 // populate the stats
1318 processGroup.call(updateProcessGroupStatus);
1320 if (processGroupData.permissions.canRead) {
1321 // update the process group name
1322 processGroup.select('text.process-group-name')
1323 .text(function (d) {
1324 var name = d.component.name;
1325 if (name.length > PREVIEW_NAME_LENGTH) {
1326 return name.substring(0, PREVIEW_NAME_LENGTH) + String.fromCharCode(8230);
1332 // clear the process group name
1333 processGroup.select('text.process-group-name').text(null);
1336 // remove the tooltips
1337 processGroup.call(removeTooltips);
1339 // remove the details if necessary
1340 if (!details.empty()) {
1348 * Updates the process group status.
1350 * @param {selection} updated The process groups to be updated
1352 var updateProcessGroupStatus = function (updated) {
1353 if (updated.empty()) {
1357 // queued count value
1358 updated.select('text.process-group-queued tspan.count')
1359 .text(function (d) {
1360 return nfCommon.substringBeforeFirst(d.status.aggregateSnapshot.queued, ' ');
1363 // queued size value
1364 updated.select('text.process-group-queued tspan.size')
1365 .text(function (d) {
1366 return ' ' + nfCommon.substringAfterFirst(d.status.aggregateSnapshot.queued, ' ');
1370 updated.select('text.process-group-in tspan.count')
1371 .text(function (d) {
1372 return nfCommon.substringBeforeFirst(d.status.aggregateSnapshot.input, ' ');
1376 updated.select('text.process-group-in tspan.size')
1377 .text(function (d) {
1378 return ' ' + nfCommon.substringAfterFirst(d.status.aggregateSnapshot.input, ' ');
1382 updated.select('text.process-group-in tspan.ports')
1383 .text(function (d) {
1384 return ' ' + String.fromCharCode(8594) + ' ' + d.inputPortCount;
1388 updated.select('text.process-group-read-write')
1389 .text(function (d) {
1390 return d.status.aggregateSnapshot.read + ' / ' + d.status.aggregateSnapshot.written;
1394 updated.select('text.process-group-out tspan.ports')
1395 .text(function (d) {
1396 return d.outputPortCount + ' ' + String.fromCharCode(8594) + ' ';
1400 updated.select('text.process-group-out tspan.count')
1401 .text(function (d) {
1402 return nfCommon.substringBeforeFirst(d.status.aggregateSnapshot.output, ' ');
1406 updated.select('text.process-group-out tspan.size')
1407 .text(function (d) {
1408 return ' ' + nfCommon.substringAfterFirst(d.status.aggregateSnapshot.output, ' ');
1411 updated.each(function (d) {
1412 var processGroup = d3.select(this);
1415 // -------------------
1416 // active thread count
1417 // -------------------
1419 nfCanvasUtils.activeThreadCount(processGroup, d, function (off) {
1427 processGroup.select('rect.bulletin-background').classed('has-bulletins', function () {
1428 return !nfCommon.isEmpty(d.status.aggregateSnapshot.bulletins);
1431 nfCanvasUtils.bulletins(processGroup, d, function () {
1432 return d3.select('#process-group-tooltips');
1438 * Removes the process groups in the specified selection.
1440 * @param {selection} removed The process groups to be removed
1442 var removeProcessGroups = function (removed) {
1443 if (removed.empty()) {
1447 removed.call(removeTooltips).remove();
1451 * Removes the tooltips for the process groups in the specified selection.
1453 * @param {selection} removed
1455 var removeTooltips = function (removed) {
1456 removed.each(function (d) {
1457 // remove any associated tooltips
1458 $('#bulletin-tip-' + d.id).remove();
1459 $('#version-control-tip-' + d.id).remove();
1460 $('#comments-tip-' + d.id).remove();
1464 var nfProcessGroup = {
1466 * Initializes of the Process Group handler.
1468 * @param nfConnectableRef The nfConnectable module.
1469 * @param nfDraggableRef The nfDraggable module.
1470 * @param nfSelectableRef The nfSelectable module.
1471 * @param nfContextMenuRef The nfContextMenu module.
1473 init: function (nfConnectableRef, nfDraggableRef, nfSelectableRef, nfContextMenuRef) {
1474 nfConnectable = nfConnectableRef;
1475 nfDraggable = nfDraggableRef;
1476 nfSelectable = nfSelectableRef;
1477 nfContextMenu = nfContextMenuRef;
1479 processGroupMap = d3.map();
1480 removedCache = d3.map();
1481 addedCache = d3.map();
1483 // create the process group container
1484 processGroupContainer = d3.select('#canvas').append('g')
1486 'pointer-events': 'all',
1487 'class': 'process-groups'
1492 * Adds the specified process group entity.
1494 * @param processGroupEntities The process group
1495 * @param options Configuration options
1497 add: function (processGroupEntities, options) {
1498 var selectAll = false;
1499 if (nfCommon.isDefinedAndNotNull(options)) {
1500 selectAll = nfCommon.isDefinedAndNotNull(options.selectAll) ? options.selectAll : selectAll;
1503 // get the current time
1504 var now = new Date().getTime();
1506 var add = function (processGroupEntity) {
1507 addedCache.set(processGroupEntity.id, now);
1509 // add the process group
1510 processGroupMap.set(processGroupEntity.id, $.extend({
1511 type: 'ProcessGroup',
1512 dimensions: dimensions
1513 }, processGroupEntity));
1516 // determine how to handle the specified process groups
1517 if ($.isArray(processGroupEntities)) {
1518 $.each(processGroupEntities, function (_, processGroupEntity) {
1519 add(processGroupEntity);
1521 } else if (nfCommon.isDefinedAndNotNull(processGroupEntities)) {
1522 add(processGroupEntities);
1526 var selection = select();
1529 var entered = renderProcessGroups(selection.enter(), selectAll);
1532 updateProcessGroups(selection.merge(entered));
1536 * Populates the graph with the specified process groups.
1538 * @argument {object | array} processGroupEntities The process groups to add
1539 * @argument {object} options Configuration options
1541 set: function (processGroupEntities, options) {
1542 var selectAll = false;
1543 var transition = false;
1544 var overrideRevisionCheck = false;
1545 if (nfCommon.isDefinedAndNotNull(options)) {
1546 selectAll = nfCommon.isDefinedAndNotNull(options.selectAll) ? options.selectAll : selectAll;
1547 transition = nfCommon.isDefinedAndNotNull(options.transition) ? options.transition : transition;
1548 overrideRevisionCheck = nfCommon.isDefinedAndNotNull(options.overrideRevisionCheck) ? options.overrideRevisionCheck : overrideRevisionCheck;
1551 var set = function (proposedProcessGroupEntity) {
1552 var currentProcessGroupEntity = processGroupMap.get(proposedProcessGroupEntity.id);
1554 // set the process group if appropriate due to revision and wasn't previously removed
1555 if ((nfClient.isNewerRevision(currentProcessGroupEntity, proposedProcessGroupEntity) && !removedCache.has(proposedProcessGroupEntity.id)) || overrideRevisionCheck === true) {
1556 processGroupMap.set(proposedProcessGroupEntity.id, $.extend({
1557 type: 'ProcessGroup',
1558 dimensions: dimensions
1559 }, proposedProcessGroupEntity));
1563 // determine how to handle the specified process groups
1564 if ($.isArray(processGroupEntities)) {
1565 $.each(processGroupMap.keys(), function (_, key) {
1566 var currentProcessGroupEntity = processGroupMap.get(key);
1567 var isPresent = $.grep(processGroupEntities, function (proposedProcessGroupEntity) {
1568 return proposedProcessGroupEntity.id === currentProcessGroupEntity.id;
1571 // if the current process group is not present and was not recently added, remove it
1572 if (isPresent.length === 0 && !addedCache.has(key)) {
1573 processGroupMap.remove(key);
1576 $.each(processGroupEntities, function (_, processGroupEntity) {
1577 set(processGroupEntity);
1579 } else if (nfCommon.isDefinedAndNotNull(processGroupEntities)) {
1580 set(processGroupEntities);
1584 var selection = select();
1587 var entered = renderProcessGroups(selection.enter(), selectAll);
1590 var updated = selection.merge(entered);
1591 updated.call(updateProcessGroups).call(nfCanvasUtils.position, transition);
1594 selection.exit().call(removeProcessGroups);
1598 * If the process group id is specified it is returned. If no process group id
1599 * specified, all process groups are returned.
1601 * @param {string} id
1603 get: function (id) {
1604 if (nfCommon.isUndefined(id)) {
1605 return processGroupMap.values();
1607 return processGroupMap.get(id);
1612 * If the process group id is specified it is refresh according to the current
1613 * state. If no process group id is specified, all process groups are refreshed.
1615 * @param {string} id Optional
1617 refresh: function (id) {
1618 if (nfCommon.isDefinedAndNotNull(id)) {
1619 d3.select('#id-' + id).call(updateProcessGroups);
1621 d3.selectAll('g.process-group').call(updateProcessGroups);
1626 * Refreshes the components necessary after a pan event.
1629 d3.selectAll('g.process-group.entering, g.process-group.leaving').call(updateProcessGroups);
1633 * Reloads the process group state from the server and refreshes the UI.
1634 * If the process group is currently unknown, this function reloads the canvas.
1636 * @param {string} id The process group id
1638 reload: function (id) {
1639 if (processGroupMap.has(id)) {
1640 var processGroupEntity = processGroupMap.get(id);
1643 url: processGroupEntity.uri,
1645 }).done(function (response) {
1646 nfProcessGroup.set(response);
1652 * Positions the component.
1654 * @param {string} id The id
1656 position: function (id) {
1657 d3.select('#id-' + id).call(nfCanvasUtils.position);
1661 * Removes the specified process group.
1663 * @param {string} processGroupIds The process group id(s)
1665 remove: function (processGroupIds) {
1666 var now = new Date().getTime();
1668 if ($.isArray(processGroupIds)) {
1669 $.each(processGroupIds, function (_, processGroupId) {
1670 removedCache.set(processGroupId, now);
1671 processGroupMap.remove(processGroupId);
1674 removedCache.set(processGroupIds, now);
1675 processGroupMap.remove(processGroupIds);
1678 // apply the selection and handle all removed process groups
1679 select().exit().call(removeProcessGroups);
1683 * Removes all process groups.
1685 removeAll: function () {
1686 nfProcessGroup.remove(processGroupMap.keys());
1690 * Expires the caches up to the specified timestamp.
1694 expireCaches: function (timestamp) {
1695 var expire = function (cache) {
1696 cache.each(function (entryTimestamp, id) {
1697 if (timestamp > entryTimestamp) {
1704 expire(removedCache);
1708 * Enters the specified group.
1710 * @param {string} groupId
1712 enterGroup: function (groupId) {
1714 // hide the context menu
1715 nfContextMenu.hide();
1717 // set the new group id
1718 nfCanvasUtils.setGroupId(groupId);
1721 return nfCanvasUtils.reload().done(function () {
1723 // attempt to restore the view
1724 var viewRestored = nfCanvasUtils.restoreUserView();
1726 // if the view was not restore attempt to fit
1727 if (viewRestored === false) {
1728 nfCanvasUtils.fitCanvas();
1731 // update URL deep linking params
1732 nfCanvasUtils.setURLParameters(groupId, d3.select());
1734 }).fail(function () {
1735 nfDialog.showOkDialog({
1736 headerText: 'Process Group',
1737 dialogContent: 'Unable to enter the selected group.'
1743 return nfProcessGroup;