Fixed serveral bugs including credential prompting. Added logic to bring links/nodes to the forefront when you hover over them in case there's some overlap

This commit is contained in:
mabashian
2018-11-09 09:15:42 -05:00
committed by chris meyers
parent 4917046e47
commit d97a0e415f
5 changed files with 215 additions and 209 deletions

View File

@@ -9,7 +9,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
return { return {
scope: { scope: {
treeState: '=', graphState: '=',
readOnly: '<', readOnly: '<',
addNodeWithoutChild: '&', addNodeWithoutChild: '&',
addNodeWithChild: '&', addNodeWithChild: '&',
@@ -55,6 +55,11 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
function init() { function init() {
force = d3.layout.force() force = d3.layout.force()
// .gravity(0)
// .linkStrength(2)
// .friction(0.4)
// .charge(-4000)
// .linkDistance(300)
.gravity(0) .gravity(0)
.charge(-300) .charge(-300)
.linkDistance(300) .linkDistance(300)
@@ -206,7 +211,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
function update() { function update() {
if(scope.dimensionsSet) { if(scope.dimensionsSet) {
let links = svgGroup.selectAll(".WorkflowChart-link") let links = svgGroup.selectAll(".WorkflowChart-link")
.data(scope.treeState.arrayOfLinksForChart, function(d) { return `${d.source.id}-${d.target.id}`; }); .data(scope.graphState.arrayOfLinksForChart, function(d) { return `${d.source.id}-${d.target.id}`; });
// Remove any stale links // Remove any stale links
links.exit().remove(); links.exit().remove();
@@ -217,7 +222,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
baseSvg.selectAll(".WorkflowChart-linkPath") baseSvg.selectAll(".WorkflowChart-linkPath")
.attr("class", function(d) { .attr("class", function(d) {
return (d.source.isNodeBeingAdded || d.target.isNodeBeingAdded) ? "WorkflowChart-linkPath WorkflowChart-isNodeBeingAdded" : "WorkflowChart-linkPath"; return (d.source.id === scope.graphState.nodeBeingAdded || d.target.id === scope.graphState.nodeBeingAdded) ? "WorkflowChart-linkPath WorkflowChart-isNodeBeingAdded" : "WorkflowChart-linkPath";
}) })
.attr('stroke', function(d) { .attr('stroke', function(d) {
let edgeType = d.edgeType; let edgeType = d.edgeType;
@@ -241,7 +246,11 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.attr("id", function(d){return "link-" + d.source.id + "-" + d.target.id + "-overlay";}) .attr("id", function(d){return "link-" + d.source.id + "-" + d.target.id + "-overlay";})
.attr("class", function(d) { .attr("class", function(d) {
let linkClasses = ["WorkflowChart-linkOverlay"]; let linkClasses = ["WorkflowChart-linkOverlay"];
if (d.isLinkBeingEdited) { if (
scope.graphState.linkBeingEdited &&
d.source.id === scope.graphState.linkBeingEdited.source &&
d.target.id === scope.graphState.linkBeingEdited.target
) {
linkClasses.push("WorkflowChart-link--active"); linkClasses.push("WorkflowChart-link--active");
} }
return linkClasses.join(' '); return linkClasses.join(' ');
@@ -249,10 +258,10 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
baseSvg.selectAll(".WorkflowChart-circleBetweenNodes") baseSvg.selectAll(".WorkflowChart-circleBetweenNodes")
.attr("id", function(d){return "link-" + d.source.id + "-" + d.target.id + "-add";}) .attr("id", function(d){return "link-" + d.source.id + "-" + d.target.id + "-add";})
.style("display", function(d) { return (scope.treeState.isLinkMode || d.source.isNodeBeingAdded || d.target.isNodeBeingAdded || scope.readOnly) ? "none" : null; }); .style("display", function(d) { return (scope.graphState.isLinkMode || d.source.id === scope.graphState.nodeBeingAdded || d.target.id === scope.graphState.nodeBeingAdded || scope.readOnly) ? "none" : null; });
baseSvg.selectAll(".WorkflowChart-betweenNodesIcon") baseSvg.selectAll(".WorkflowChart-betweenNodesIcon")
.style("display", function(d) { return (scope.treeState.isLinkMode || d.source.isNodeBeingAdded || d.target.isNodeBeingAdded || scope.readOnly) ? "none" : null; }); .style("display", function(d) { return (scope.graphState.isLinkMode || d.source.id === scope.graphState.nodeBeingAdded || d.target.id === scope.graphState.nodeBeingAdded || scope.readOnly) ? "none" : null; });
// Add any new links // Add any new links
@@ -263,7 +272,11 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
linkEnter.append("polygon", "g") linkEnter.append("polygon", "g")
.attr("class", function(d) { .attr("class", function(d) {
let linkClasses = ["WorkflowChart-linkOverlay"]; let linkClasses = ["WorkflowChart-linkOverlay"];
if (d.isLinkBeingEdited) { if (
scope.graphState.linkBeingEdited &&
d.source.id === scope.graphState.linkBeingEdited.source &&
d.target.id === scope.graphState.linkBeingEdited.target
) {
linkClasses.push("WorkflowChart-link--active"); linkClasses.push("WorkflowChart-link--active");
} }
return linkClasses.join(' '); return linkClasses.join(' ');
@@ -271,8 +284,9 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.attr("id", function(d){return "link-" + d.source.id + "-" + d.target.id + "-overlay";}) .attr("id", function(d){return "link-" + d.source.id + "-" + d.target.id + "-overlay";})
.call(edit_link) .call(edit_link)
.on("mouseover", function(d) { .on("mouseover", function(d) {
if(!scope.treeState.isLinkMode && !d.source.isStartNode && !d.source.isNodeBeingAdded && !d.target.isNodeBeingAdded && scope.mode !== 'details') { if(!scope.graphState.isLinkMode && !d.source.isStartNode && d.source.id !== scope.graphState.nodeBeingAdded && d.target.id !== scope.graphState.nodeBeingAdded && scope.mode !== 'details') {
d3.select("#link-" + d.source.id + "-" + d.target.id) $(`#link-${d.source.id}-${d.target.id}`).appendTo(`#aw-workflow-chart-g`);
d3.select(`#link-${d.source.id}-${d.target.id}`)
.classed("WorkflowChart-linkHovering", true); .classed("WorkflowChart-linkHovering", true);
let xPos, yPos, arrowClass; let xPos, yPos, arrowClass;
@@ -314,7 +328,8 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
}) })
.on("mouseout", function(d){ .on("mouseout", function(d){
if(!d.source.isStartNode && !d.target.isNodeBeingAdded && scope.mode !== 'details') { if(!d.source.isStartNode && d.target.id !== scope.graphState.nodeBeingAdded && scope.mode !== 'details') {
$(`#aw-workflow-chart-g`).prepend($(`#link-${d.source.id}-${d.target.id}`));
d3.select("#link-" + d.source.id + "-" + d.target.id) d3.select("#link-" + d.source.id + "-" + d.target.id)
.classed("WorkflowChart-linkHovering", false); .classed("WorkflowChart-linkHovering", false);
} }
@@ -324,11 +339,12 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
// Add entering links in the parents old position. // Add entering links in the parents old position.
linkEnter.append("line") linkEnter.append("line")
.attr("class", function(d) { .attr("class", function(d) {
return (d.source.isNodeBeingAdded || d.target.isNodeBeingAdded) ? "WorkflowChart-linkPath WorkflowChart-isNodeBeingAdded" : "WorkflowChart-linkPath"; return (d.source.id === scope.graphState.nodeBeingAdded || d.target.id === scope.graphState.nodeBeingAdded) ? "WorkflowChart-linkPath WorkflowChart-isNodeBeingAdded" : "WorkflowChart-linkPath";
}) })
.call(edit_link) .call(edit_link)
.on("mouseenter", function(d) { .on("mouseenter", function(d) {
if(!scope.treeState.isLinkMode && !d.source.isStartNode && !d.source.isNodeBeingAdded && !d.target.isNodeBeingAdded && scope.mode !== 'details') { if(!scope.graphState.isLinkMode && !d.source.isStartNode && d.source.id !== scope.graphState.nodeBeingAdded && d.target.id !== scope.graphState.nodeBeingAdded && scope.mode !== 'details') {
$(`#link-${d.source.id}-${d.target.id}`).appendTo(`#aw-workflow-chart-g`);
d3.select("#link-" + d.source.id + "-" + d.target.id) d3.select("#link-" + d.source.id + "-" + d.target.id)
.classed("WorkflowChart-linkHovering", true); .classed("WorkflowChart-linkHovering", true);
@@ -370,7 +386,8 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
} }
}) })
.on("mouseleave", function(d){ .on("mouseleave", function(d){
if(!d.source.isStartNode && !d.target.isNodeBeingAdded && scope.mode !== 'details') { if(!d.source.isStartNode && d.target.id !== scope.graphState.nodeBeingAdded && scope.mode !== 'details') {
$(`#aw-workflow-chart-g`).prepend($(`#link-${d.source.id}-${d.target.id}`));
d3.select("#link-" + d.source.id + "-" + d.target.id) d3.select("#link-" + d.source.id + "-" + d.target.id)
.classed("WorkflowChart-linkHovering", false); .classed("WorkflowChart-linkHovering", false);
} }
@@ -398,13 +415,15 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.attr("id", function(d){return "link-" + d.source.id + "-" + d.target.id + "-add";}) .attr("id", function(d){return "link-" + d.source.id + "-" + d.target.id + "-add";})
.attr("r", 10) .attr("r", 10)
.attr("class", "WorkflowChart-addCircle WorkflowChart-circleBetweenNodes") .attr("class", "WorkflowChart-addCircle WorkflowChart-circleBetweenNodes")
.style("display", function(d) { return (scope.treeState.isLinkMode || d.source.isNodeBeingAdded || d.target.isNodeBeingAdded || scope.readOnly) ? "none" : null; }) .style("display", function(d) { return (scope.graphState.isLinkMode || d.source.id === scope.graphState.nodeBeingAdded || d.target.id === scope.graphState.nodeBeingAdded || scope.readOnly) ? "none" : null; })
.call(add_node_with_child) .call(add_node_with_child)
.on("mouseover", function(d) { .on("mouseover", function(d) {
$(`#link-${d.source.id}-${d.target.id}`).appendTo(`#aw-workflow-chart-g`);
d3.select("#link-" + d.source.id + "-" + d.target.id) d3.select("#link-" + d.source.id + "-" + d.target.id)
.classed("WorkflowChart-addHovering", true); .classed("WorkflowChart-addHovering", true);
}) })
.on("mouseout", function(d){ .on("mouseout", function(d){
$(`#aw-workflow-chart-g`).prepend($(`#link-${d.source.id}-${d.target.id}`));
d3.select("#link-" + d.source.id + "-" + d.target.id) d3.select("#link-" + d.source.id + "-" + d.target.id)
.classed("WorkflowChart-addHovering", false); .classed("WorkflowChart-addHovering", false);
}); });
@@ -416,13 +435,15 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.size(60) .size(60)
.type("cross") .type("cross")
) )
.style("display", function(d) { return (scope.treeState.isLinkMode || d.source.isNodeBeingAdded || d.target.isNodeBeingAdded || scope.readOnly) ? "none" : null; }) .style("display", function(d) { return (scope.graphState.isLinkMode || d.source.id === scope.graphState.nodeBeingAdded || d.target.id === scope.graphState.nodeBeingAdded || scope.readOnly) ? "none" : null; })
.call(add_node_with_child) .call(add_node_with_child)
.on("mouseover", function(d) { .on("mouseover", function(d) {
$(`#link-${d.source.id}-${d.target.id}`).appendTo(`#aw-workflow-chart-g`);
d3.select("#link-" + d.source.id + "-" + d.target.id) d3.select("#link-" + d.source.id + "-" + d.target.id)
.classed("WorkflowChart-addHovering", true); .classed("WorkflowChart-addHovering", true);
}) })
.on("mouseout", function(d){ .on("mouseout", function(d){
$(`#aw-workflow-chart-g`).prepend($(`#link-${d.source.id}-${d.target.id}`));
d3.select("#link-" + d.source.id + "-" + d.target.id) d3.select("#link-" + d.source.id + "-" + d.target.id)
.classed("WorkflowChart-addHovering", false); .classed("WorkflowChart-addHovering", false);
}); });
@@ -435,29 +456,29 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
let linkAddBetweenIcon = svgGroup.selectAll(".WorkflowChart-betweenNodesIcon"); let linkAddBetweenIcon = svgGroup.selectAll(".WorkflowChart-betweenNodesIcon");
let nodes = svgGroup.selectAll('.WorkflowChart-node') let nodes = svgGroup.selectAll('.WorkflowChart-node')
.data(scope.treeState.arrayOfNodesForChart, function(d) { return d.id; }); .data(scope.graphState.arrayOfNodesForChart, function(d) { return d.id; });
// Remove any stale nodes // Remove any stale nodes
nodes.exit().remove(); nodes.exit().remove();
// Update existing nodes // Update existing nodes
baseSvg.selectAll(".WorkflowChart-nodeAddCircle") baseSvg.selectAll(".WorkflowChart-nodeAddCircle")
.style("display", function(d) { return scope.treeState.isLinkMode || d.isNodeBeingAdded || scope.readOnly ? "none" : null; }); .style("display", function(d) { return scope.graphState.isLinkMode || d.id === scope.graphState.nodeBeingAdded || scope.readOnly ? "none" : null; });
baseSvg.selectAll(".WorkflowChart-nodeAddIcon") baseSvg.selectAll(".WorkflowChart-nodeAddIcon")
.style("display", function(d) { return scope.treeState.isLinkMode || d.isNodeBeingAdded || scope.readOnly ? "none" : null; }); .style("display", function(d) { return scope.graphState.isLinkMode || d.id === scope.graphState.nodeBeingAdded || scope.readOnly ? "none" : null; });
baseSvg.selectAll(".WorkflowChart-linkCircle") baseSvg.selectAll(".WorkflowChart-linkCircle")
.style("display", function(d) { return scope.treeState.isLinkMode || d.isNodeBeingAdded || scope.readOnly ? "none" : null; }); .style("display", function(d) { return scope.graphState.isLinkMode || d.id === scope.graphState.nodeBeingAdded || scope.readOnly ? "none" : null; });
baseSvg.selectAll(".WorkflowChart-nodeLinkIcon") baseSvg.selectAll(".WorkflowChart-nodeLinkIcon")
.style("display", function(d) { return scope.treeState.isLinkMode || d.isNodeBeingAdded || scope.readOnly ? "none" : null; }); .style("display", function(d) { return scope.graphState.isLinkMode || d.id === scope.graphState.nodeBeingAdded || scope.readOnly ? "none" : null; });
baseSvg.selectAll(".WorkflowChart-nodeRemoveCircle") baseSvg.selectAll(".WorkflowChart-nodeRemoveCircle")
.style("display", function(d) { return scope.treeState.isLinkMode || d.isNodeBeingAdded || scope.readOnly ? "none" : null; }); .style("display", function(d) { return scope.graphState.isLinkMode || d.id === scope.graphState.nodeBeingAdded || scope.readOnly ? "none" : null; });
baseSvg.selectAll(".WorkflowChart-nodeRemoveIcon") baseSvg.selectAll(".WorkflowChart-nodeRemoveIcon")
.style("display", function(d) { return scope.treeState.isLinkMode || d.isNodeBeingAdded || scope.readOnly ? "none" : null; }); .style("display", function(d) { return scope.graphState.isLinkMode || d.id === scope.graphState.nodeBeingAdded || scope.readOnly ? "none" : null; });
baseSvg.selectAll(".WorkflowChart-rect") baseSvg.selectAll(".WorkflowChart-rect")
.attr('stroke', function(d) { .attr('stroke', function(d) {
@@ -477,7 +498,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
} }
}) })
.attr("class", function(d) { .attr("class", function(d) {
let classString = d.isNodeBeingAdded ? "WorkflowChart-rect WorkflowChart-isNodeBeingAdded" : "WorkflowChart-rect"; let classString = d.id === scope.graphState.nodeBeingAdded ? "WorkflowChart-rect WorkflowChart-isNodeBeingAdded" : "WorkflowChart-rect";
classString += !d.unifiedJobTemplate ? " WorkflowChart-dashedNode" : ""; classString += !d.unifiedJobTemplate ? " WorkflowChart-dashedNode" : "";
return classString; return classString;
}); });
@@ -561,17 +582,17 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.style("display", function(d){ return d.job && d.job.status && d.job.id ? null : "none"; }); .style("display", function(d){ return d.job && d.job.status && d.job.id ? null : "none"; });
baseSvg.selectAll(".WorkflowChart-deletedText") baseSvg.selectAll(".WorkflowChart-deletedText")
.style("display", function(d){ return d.unifiedJobTemplate || d.isNodeBeingAdded ? "none" : null; }); .style("display", function(d){ return d.unifiedJobTemplate || d.id === scope.graphState.nodeBeingAdded ? "none" : null; });
baseSvg.selectAll(".WorkflowChart-activeNode") baseSvg.selectAll(".WorkflowChart-activeNode")
.style("display", function(d) { return d.isNodeBeingEdited ? null : "none"; }); .style("display", function(d) { return d.id === scope.graphState.nodeBeingEdited ? null : "none"; });
baseSvg.selectAll(".WorkflowChart-elapsed") baseSvg.selectAll(".WorkflowChart-elapsed")
.style("display", function(d) { return (d.job && d.job.elapsed) ? null : "none"; }); .style("display", function(d) { return (d.job && d.job.elapsed) ? null : "none"; });
baseSvg.selectAll(".WorkflowChart-addLinkCircle") baseSvg.selectAll(".WorkflowChart-addLinkCircle")
.attr("fill", function(d) { return scope.treeState.addLinkSource === d.id ? "#337AB7" : "#D7D7D7"; }) .attr("fill", function(d) { return scope.graphState.addLinkSource === d.id ? "#337AB7" : "#D7D7D7"; })
.style("display", function(d) { return scope.treeState.isLinkMode && !d.isInvalidLinkTarget ? null : "none"; }); .style("display", function(d) { return scope.graphState.isLinkMode && !d.isInvalidLinkTarget ? null : "none"; });
// Add new nodes // Add new nodes
const nodeEnter = nodes const nodeEnter = nodes
@@ -619,7 +640,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.attr("cx", nodeW) .attr("cx", nodeW)
.attr("r", 8) .attr("r", 8)
.attr("class", "WorkflowChart-addLinkCircle") .attr("class", "WorkflowChart-addLinkCircle")
.style("display", function() { return scope.treeState.isLinkMode ? null : "none"; }); .style("display", function() { return scope.graphState.isLinkMode ? null : "none"; });
thisNode.append("rect") thisNode.append("rect")
.attr("width", nodeW) .attr("width", nodeW)
.attr("height", nodeH) .attr("height", nodeH)
@@ -643,7 +664,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
}) })
.attr('stroke-width', "2px") .attr('stroke-width', "2px")
.attr("class", function(d) { .attr("class", function(d) {
let classString = d.isNodeBeingAdded ? "WorkflowChart-rect WorkflowChart-isNodeBeingAdded" : "WorkflowChart-rect"; let classString = d.id === scope.graphState.nodeBeingAdded ? "WorkflowChart-rect WorkflowChart-isNodeBeingAdded" : "WorkflowChart-rect";
classString += !_.get(d, 'unifiedJobTemplate.name') ? " WorkflowChart-dashedNode" : ""; classString += !_.get(d, 'unifiedJobTemplate.name') ? " WorkflowChart-dashedNode" : "";
return classString; return classString;
}); });
@@ -651,7 +672,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
thisNode.append("path") thisNode.append("path")
.attr("d", rounded_rect(1, 0, 5, nodeH, 5, 1, 0, 1, 0)) .attr("d", rounded_rect(1, 0, 5, nodeH, 5, 1, 0, 1, 0))
.attr("class", "WorkflowChart-activeNode") .attr("class", "WorkflowChart-activeNode")
.style("display", function(d) { return d.isNodeBeingEdited ? null : "none"; }); .style("display", function(d) { return d.id === scope.graphState.nodeBeingEdited ? null : "none"; });
thisNode.append("text") thisNode.append("text")
.attr("x", function(d){ return (scope.mode === 'details' && d.job && d.job.status) ? 20 : nodeW / 2; }) .attr("x", function(d){ return (scope.mode === 'details' && d.job && d.job.status) ? 20 : nodeW / 2; })
@@ -673,7 +694,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.html(function () { .html(function () {
return `<span>${TemplatesStrings.get('workflow_maker.DELETED')}</span>`; return `<span>${TemplatesStrings.get('workflow_maker.DELETED')}</span>`;
}) })
.style("display", function(d) { return d.unifiedJobTemplate || d.isNodeBeingAdded ? "none" : null; }); .style("display", function(d) { return d.unifiedJobTemplate || d.id === scope.graphState.nodeBeingAdded ? "none" : null; });
thisNode.append("circle") thisNode.append("circle")
.attr("cy", nodeH) .attr("cy", nodeH)
@@ -738,6 +759,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.call(node_click) .call(node_click)
.on("mouseover", function(d) { .on("mouseover", function(d) {
if(!d.isStartNode) { if(!d.isStartNode) {
$(`#node-${d.id}`).appendTo(`#aw-workflow-chart-g`);
let resourceName = (d.unifiedJobTemplate && d.unifiedJobTemplate.name) ? d.unifiedJobTemplate.name : ""; let resourceName = (d.unifiedJobTemplate && d.unifiedJobTemplate.name) ? d.unifiedJobTemplate.name : "";
if(resourceName && resourceName.length > maxNodeTextLength) { if(resourceName && resourceName.length > maxNodeTextLength) {
// When the graph is initially rendered all the links come after the nodes (when you look at the dom). // When the graph is initially rendered all the links come after the nodes (when you look at the dom).
@@ -773,8 +795,8 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
}); });
} }
if (scope.treeState.isLinkMode && !d.isInvalidLinkTarget && scope.treeState.addLinkSource !== d.id) { if (scope.graphState.isLinkMode && !d.isInvalidLinkTarget && scope.graphState.addLinkSource !== d.id) {
let sourceNode = d3.select(`#node-${scope.treeState.addLinkSource}`); let sourceNode = d3.select(`#node-${scope.graphState.addLinkSource}`);
const sourceNodeX = d3.transform(sourceNode.attr("transform")).translate[0]; const sourceNodeX = d3.transform(sourceNode.attr("transform")).translate[0];
const sourceNodeY = d3.transform(sourceNode.attr("transform")).translate[1]; const sourceNodeY = d3.transform(sourceNode.attr("transform")).translate[1];
@@ -821,7 +843,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.attr("cx", nodeW) .attr("cx", nodeW)
.attr("r", 10) .attr("r", 10)
.attr("class", "WorkflowChart-addCircle WorkflowChart-nodeAddCircle") .attr("class", "WorkflowChart-addCircle WorkflowChart-nodeAddCircle")
.style("display", function(d) { return d.isNodeBeingAdded || scope.readOnly ? "none" : null; }) .style("display", function(d) { return d.id === scope.graphState.nodeBeingAdded || scope.readOnly ? "none" : null; })
.call(add_node_without_child) .call(add_node_without_child)
.on("mouseover", function(d) { .on("mouseover", function(d) {
d3.select("#node-" + d.id) d3.select("#node-" + d.id)
@@ -843,7 +865,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.size(60) .size(60)
.type("cross") .type("cross")
) )
.style("display", function(d) { return d.isNodeBeingAdded || scope.readOnly ? "none" : null; }) .style("display", function(d) { return d.id === scope.graphState.nodeBeingAdded || scope.readOnly ? "none" : null; })
.call(add_node_without_child) .call(add_node_without_child)
.on("mouseover", function(d) { .on("mouseover", function(d) {
d3.select("#node-" + d.id) d3.select("#node-" + d.id)
@@ -863,7 +885,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.attr("cy", nodeH/2) .attr("cy", nodeH/2)
.attr("r", 10) .attr("r", 10)
.attr("class", "WorkflowChart-linkCircle") .attr("class", "WorkflowChart-linkCircle")
.style("display", function(d) { return d.isNodeBeingAdded || scope.readOnly ? "none" : null; }) .style("display", function(d) { return d.id === scope.graphState.nodeBeingAdded || scope.readOnly ? "none" : null; })
.call(add_link) .call(add_link)
.on("mouseover", function(d) { .on("mouseover", function(d) {
d3.select("#node-" + d.id) d3.select("#node-" + d.id)
@@ -877,8 +899,6 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
d3.select("#node-" + d.id + "-link") d3.select("#node-" + d.id + "-link")
.classed("WorkflowChart-linkButtonHovering", false); .classed("WorkflowChart-linkButtonHovering", false);
}); });
// TODO: clean up the placement of this icon... this works but it's not
// clean
thisNode.append("foreignObject") thisNode.append("foreignObject")
.attr("x", nodeW - 6) .attr("x", nodeW - 6)
.attr("y", nodeH/2 - 9) .attr("y", nodeH/2 - 9)
@@ -887,7 +907,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
return `<span class="fa fa-link" />`; return `<span class="fa fa-link" />`;
}) })
.attr("class", "WorkflowChart-nodeLinkIcon") .attr("class", "WorkflowChart-nodeLinkIcon")
.style("display", function(d) { return d.isNodeBeingAdded || scope.readOnly ? "none" : null; }) .style("display", function(d) { return d.id === scope.graphState.nodeBeingAdded || scope.readOnly ? "none" : null; })
.call(add_link) .call(add_link)
.on("mouseover", function(d) { .on("mouseover", function(d) {
d3.select("#node-" + d.id) d3.select("#node-" + d.id)
@@ -907,7 +927,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.attr("cy", nodeH) .attr("cy", nodeH)
.attr("r", 10) .attr("r", 10)
.attr("class", "WorkflowChart-nodeRemoveCircle") .attr("class", "WorkflowChart-nodeRemoveCircle")
.style("display", function(d) { return (d.isStartNode || d.isNodeBeingAdded || scope.readOnly) ? "none" : null; }) .style("display", function(d) { return (d.isStartNode || d.id === scope.graphState.nodeBeingAdded || scope.readOnly) ? "none" : null; })
.call(remove_node) .call(remove_node)
.on("mouseover", function(d) { .on("mouseover", function(d) {
d3.select("#node-" + d.id) d3.select("#node-" + d.id)
@@ -929,7 +949,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.size(60) .size(60)
.type("cross") .type("cross")
) )
.style("display", function(d) { return (d.isStartNode || d.isNodeBeingAdded || scope.readOnly) ? "none" : null; }) .style("display", function(d) { return (d.isStartNode || d.id === scope.graphState.nodeBeingAdded || scope.readOnly) ? "none" : null; })
.call(remove_node) .call(remove_node)
.on("mouseover", function(d) { .on("mouseover", function(d) {
d3.select("#node-" + d.id) d3.select("#node-" + d.id)
@@ -1004,7 +1024,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
}); });
// TODO: this // TODO: this
// if(scope.treeState.arrayOfNodesForChart && scope.treeState.arrayOfNodesForChart > 1 && !graphLoaded) { // if(scope.graphState.arrayOfNodesForChart && scope.graphState.arrayOfNodesForChart > 1 && !graphLoaded) {
// zoomToFitChart(); // zoomToFitChart();
// } // }
@@ -1017,7 +1037,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
let tick = () => { let tick = () => {
linkLines linkLines
.each(function(d) { .each(function(d) {
d.target.y = scope.treeState.depthMap[d.target.id] * 300; d.target.y = scope.graphState.depthMap[d.target.id] * 300;
}) })
.attr("x1", function(d) { return d.target.y; }) .attr("x1", function(d) { return d.target.y; })
.attr("y1", function(d) { return d.target.x + (nodeH/2); }) .attr("y1", function(d) { return d.target.x + (nodeH/2); })
@@ -1068,8 +1088,8 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
}; };
force force
.nodes(scope.treeState.arrayOfNodesForChart) .nodes(scope.graphState.arrayOfNodesForChart)
.links(scope.treeState.arrayOfLinksForChart) .links(scope.graphState.arrayOfLinksForChart)
.on("tick", tick) .on("tick", tick)
.start(); .start();
} }
@@ -1086,7 +1106,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
function add_node_without_child() { function add_node_without_child() {
this.on("click", function(d) { this.on("click", function(d) {
if(!scope.readOnly && !scope.treeState.isLinkMode) { if(!scope.readOnly && !scope.graphState.isLinkMode) {
scope.addNodeWithoutChild({ scope.addNodeWithoutChild({
parent: d parent: d
}); });
@@ -1096,7 +1116,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
function add_node_with_child() { function add_node_with_child() {
this.on("click", function(d) { this.on("click", function(d) {
if(!scope.readOnly && !scope.treeState.isLinkMode) { if(!scope.readOnly && !scope.graphState.isLinkMode) {
scope.addNodeWithChild({ scope.addNodeWithChild({
link: d link: d
}); });
@@ -1106,7 +1126,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
function remove_node() { function remove_node() {
this.on("click", function(d) { this.on("click", function(d) {
if(!d.isStartNode && !scope.readOnly && !scope.treeState.isLinkMode) { if(!d.isStartNode && !scope.readOnly && !scope.graphState.isLinkMode) {
scope.deleteNode({ scope.deleteNode({
nodeToDelete: d nodeToDelete: d
}); });
@@ -1116,13 +1136,13 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
function node_click() { function node_click() {
this.on("click", function(d) { this.on("click", function(d) {
if(!d.isStartNode && !scope.readOnly){ if(d.id !== scope.graphState.nodeBeingAdded && !scope.readOnly){
if(scope.treeState.isLinkMode && !d.isInvalidLinkTarget) { if(scope.graphState.isLinkMode && !d.isInvalidLinkTarget) {
$('.WorkflowChart-potentialLink').remove(); $('.WorkflowChart-potentialLink').remove();
scope.selectNodeForLinking({ scope.selectNodeForLinking({
nodeToStartLink: d nodeToStartLink: d
}); });
} else if(!scope.treeState.isLinkMode) { } else if(!scope.graphState.isLinkMode) {
scope.editNode({ scope.editNode({
nodeToEdit: d nodeToEdit: d
}); });
@@ -1134,7 +1154,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
function edit_link() { function edit_link() {
this.on("click", function(d) { this.on("click", function(d) {
if(!scope.treeState.isLinkMode && !d.source.isStartNode && !d.source.isNodeBeingAdded && !d.target.isNodeBeingAdded && scope.mode !== 'details'){ if(!scope.graphState.isLinkMode && !d.source.isStartNode && d.source.id !== scope.graphState.nodeBeingAdded && d.target.id !== scope.graphState.nodeBeingAdded && scope.mode !== 'details'){
scope.editLink({ scope.editLink({
linkToEdit: d linkToEdit: d
}); });
@@ -1144,7 +1164,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
function add_link() { function add_link() {
this.on("click", function(d) { this.on("click", function(d) {
if (!scope.readOnly && !scope.treeState.isLinkMode) { if (!scope.readOnly && !scope.graphState.isLinkMode) {
scope.selectNodeForLinking({ scope.selectNodeForLinking({
nodeToStartLink: d nodeToStartLink: d
}); });
@@ -1198,7 +1218,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
} }
scope.$on('refreshWorkflowChart', function(){ scope.$on('refreshWorkflowChart', function(){
if(scope.treeState) { if(scope.graphState) {
update(); update();
} }
}); });
@@ -1219,12 +1239,12 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
zoomToFitChart(); zoomToFitChart();
}); });
let clearWatchTreeState = scope.$watch('treeState.arrayOfNodesForChart', function(newVal) { let clearWatchgraphState = scope.$watch('graphState.arrayOfNodesForChart', function(newVal) {
if(newVal) { if(newVal) {
// scope.treeState.arrayOfNodesForChart // scope.graphState.arrayOfNodesForChart
update(); update();
clearWatchTreeState(); clearWatchgraphState();
} }
}); });

View File

@@ -12,11 +12,8 @@ export default ['$scope', 'TemplatesService',
Empty, PromptService, Rest, TemplatesStrings, WorkflowChartService) { Empty, PromptService, Rest, TemplatesStrings, WorkflowChartService) {
$scope.strings = TemplatesStrings; $scope.strings = TemplatesStrings;
// TODO: I don't think this needs to be on scope but changing it will require changes to
// all the prompt places
$scope.preventCredsWithPasswords = true; $scope.preventCredsWithPasswords = true;
let credentialRequests = [];
let deletedNodeIds = []; let deletedNodeIds = [];
let workflowMakerNodeIdCounter = 1; let workflowMakerNodeIdCounter = 1;
let nodeIdToChartNodeIdMapping = {}; let nodeIdToChartNodeIdMapping = {};
@@ -87,15 +84,16 @@ export default ['$scope', 'TemplatesService',
$scope.closeWorkflowMaker = function() { $scope.closeWorkflowMaker = function() {
// Revert the data to the master which was created when the dialog was opened // Revert the data to the master which was created when the dialog was opened
$scope.treeState.nodeTree = angular.copy($scope.treeStateMaster); $scope.graphState.nodeTree = angular.copy($scope.graphStateMaster);
$scope.closeDialog(); $scope.closeDialog();
}; };
$scope.saveWorkflowMaker = function () { $scope.saveWorkflowMaker = function () {
if ($scope.treeState.arrayOfNodesForChart.length > 1) { if ($scope.graphState.arrayOfNodesForChart.length > 1) {
let addPromises = []; let addPromises = [];
let editPromises = []; let editPromises = [];
let credentialsToPost = [];
Object.keys(nodeRef).map((workflowMakerNodeId) => { Object.keys(nodeRef).map((workflowMakerNodeId) => {
if (nodeRef[workflowMakerNodeId].isNew) { if (nodeRef[workflowMakerNodeId].isNew) {
@@ -104,27 +102,26 @@ export default ['$scope', 'TemplatesService',
data: buildSendableNodeData(nodeRef[workflowMakerNodeId]) data: buildSendableNodeData(nodeRef[workflowMakerNodeId])
}).then(({data}) => { }).then(({data}) => {
nodeRef[workflowMakerNodeId].originalNodeObject = data; nodeRef[workflowMakerNodeId].originalNodeObject = data;
// TODO: do we need this?
nodeIdToChartNodeIdMapping[data.id] = parseInt(workflowMakerNodeId); nodeIdToChartNodeIdMapping[data.id] = parseInt(workflowMakerNodeId);
// if (_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')) { if (_.get(nodeRef[workflowMakerNodeId], 'promptData.launchConf.ask_credential_on_launch')) {
// // This finds the credentials that were selected in the prompt but don't occur // This finds the credentials that were selected in the prompt but don't occur
// // in the template defaults // in the template defaults
// let credentialsToPost = params.node.promptData.prompts.credentials.value.filter(function (credFromPrompt) { let credentialIdsToPost = nodeRef[workflowMakerNodeId].promptData.prompts.credentials.value.filter(function (credFromPrompt) {
// let defaultCreds = _.get(params, 'node.promptData.launchConf.defaults.credentials', []); let defaultCreds = _.get(nodeRef[workflowMakerNodeId], 'promptData.launchConf.defaults.credentials', []);
// return !defaultCreds.some(function (defaultCred) { return !defaultCreds.some(function (defaultCred) {
// return credFromPrompt.id === defaultCred.id; return credFromPrompt.id === defaultCred.id;
// }); });
// }); });
//
// credentialsToPost.forEach((credentialToPost) => { credentialIdsToPost.forEach((credentialToPost) => {
// credentialRequests.push({ credentialsToPost.push({
// id: data.data.id, id: data.id,
// data: { data: {
// id: credentialToPost.id id: credentialToPost.id
// } }
// }); });
// }); });
// } }
})); }));
} else if (nodeRef[workflowMakerNodeId].isEdited) { } else if (nodeRef[workflowMakerNodeId].isEdited) {
editPromises.push(TemplatesService.editWorkflowNode({ editPromises.push(TemplatesService.editWorkflowNode({
@@ -146,9 +143,9 @@ export default ['$scope', 'TemplatesService',
let linkMap = {}; let linkMap = {};
// Build a link map for easy access // Build a link map for easy access
$scope.treeState.arrayOfLinksForChart.forEach(link => { $scope.graphState.arrayOfLinksForChart.forEach(link => {
// link.source.index of 0 is our artificial start node // link.source.id of 1 is our artificial start node
if (link.source.index !== 0) { if (link.source.id !== 1) {
const sourceNodeId = nodeRef[link.source.id].originalNodeObject.id; const sourceNodeId = nodeRef[link.source.id].originalNodeObject.id;
const targetNodeId = nodeRef[link.target.id].originalNodeObject.id; const targetNodeId = nodeRef[link.target.id].originalNodeObject.id;
if (!linkMap[sourceNodeId]) { if (!linkMap[sourceNodeId]) {
@@ -219,13 +216,13 @@ export default ['$scope', 'TemplatesService',
Object.keys(linkMap).map((sourceNodeId) => { Object.keys(linkMap).map((sourceNodeId) => {
Object.keys(linkMap[sourceNodeId]).map((targetNodeId) => { Object.keys(linkMap[sourceNodeId]).map((targetNodeId) => {
const foo = nodeIdToChartNodeIdMapping[sourceNodeId]; const sourceChartNodeId = nodeIdToChartNodeIdMapping[sourceNodeId];
const bar = nodeIdToChartNodeIdMapping[targetNodeId]; const targetChartNodeId = nodeIdToChartNodeIdMapping[targetNodeId];
switch(linkMap[sourceNodeId][targetNodeId]) { switch(linkMap[sourceNodeId][targetNodeId]) {
case "success": case "success":
if ( if (
!nodeRef[foo].originalNodeObject.success_nodes || !nodeRef[sourceChartNodeId].originalNodeObject.success_nodes ||
!nodeRef[foo].originalNodeObject.success_nodes.includes(nodeRef[bar].id) !nodeRef[sourceChartNodeId].originalNodeObject.success_nodes.includes(nodeRef[targetChartNodeId].id)
) { ) {
associatePromises.push( associatePromises.push(
TemplatesService.associateWorkflowNode({ TemplatesService.associateWorkflowNode({
@@ -238,8 +235,8 @@ export default ['$scope', 'TemplatesService',
break; break;
case "failure": case "failure":
if ( if (
!nodeRef[foo].originalNodeObject.failure_nodes || !nodeRef[sourceChartNodeId].originalNodeObject.failure_nodes ||
!nodeRef[foo].originalNodeObject.failure_nodes.includes(nodeRef[bar].id) !nodeRef[sourceChartNodeId].originalNodeObject.failure_nodes.includes(nodeRef[targetChartNodeId].id)
) { ) {
associatePromises.push( associatePromises.push(
TemplatesService.associateWorkflowNode({ TemplatesService.associateWorkflowNode({
@@ -252,8 +249,8 @@ export default ['$scope', 'TemplatesService',
break; break;
case "always": case "always":
if ( if (
!nodeRef[foo].originalNodeObject.always_nodes || !nodeRef[sourceChartNodeId].originalNodeObject.always_nodes ||
!nodeRef[foo].originalNodeObject.always_nodes.includes(nodeRef[bar].id) !nodeRef[sourceChartNodeId].originalNodeObject.always_nodes.includes(nodeRef[targetChartNodeId].id)
) { ) {
associatePromises.push( associatePromises.push(
TemplatesService.associateWorkflowNode({ TemplatesService.associateWorkflowNode({
@@ -270,9 +267,7 @@ export default ['$scope', 'TemplatesService',
$q.all(disassociatePromises) $q.all(disassociatePromises)
.then(function () { .then(function () {
let credentialPromises = credentialsToPost.map(function (request) {
// TODO: don't forget about this....
let credentialPromises = credentialRequests.map(function (request) {
return TemplatesService.postWorkflowNodeCredential({ return TemplatesService.postWorkflowNodeCredential({
id: request.id, id: request.id,
data: request.data data: request.data
@@ -291,8 +286,6 @@ export default ['$scope', 'TemplatesService',
}); });
}); });
// TODO: handle the case where the user deletes all the nodes
} else { } else {
let deletePromises = deletedNodeIds.map(function (nodeId) { let deletePromises = deletedNodeIds.map(function (nodeId) {
@@ -313,18 +306,19 @@ export default ['$scope', 'TemplatesService',
$scope.cancelNodeForm(); $scope.cancelNodeForm();
} }
$scope.treeState.arrayOfNodesForChart.push({ $scope.graphState.arrayOfNodesForChart.push({
index: $scope.treeState.arrayOfNodesForChart.length, index: $scope.graphState.arrayOfNodesForChart.length,
id: workflowMakerNodeIdCounter, id: workflowMakerNodeIdCounter,
isNodeBeingAdded: true,
unifiedJobTemplate: null unifiedJobTemplate: null
}); });
chartNodeIdToIndexMapping[workflowMakerNodeIdCounter] = $scope.treeState.arrayOfNodesForChart.length - 1; $scope.graphState.nodeBeingAdded = workflowMakerNodeIdCounter;
$scope.treeState.arrayOfLinksForChart.push({ chartNodeIdToIndexMapping[workflowMakerNodeIdCounter] = $scope.graphState.arrayOfNodesForChart.length - 1;
source: $scope.treeState.arrayOfNodesForChart[parent.index],
target: $scope.treeState.arrayOfNodesForChart[chartNodeIdToIndexMapping[workflowMakerNodeIdCounter]], $scope.graphState.arrayOfLinksForChart.push({
source: $scope.graphState.arrayOfNodesForChart[parent.index],
target: $scope.graphState.arrayOfNodesForChart[chartNodeIdToIndexMapping[workflowMakerNodeIdCounter]],
edgeType: "placeholder" edgeType: "placeholder"
}); });
@@ -336,7 +330,7 @@ export default ['$scope', 'TemplatesService',
workflowMakerNodeIdCounter++; workflowMakerNodeIdCounter++;
$scope.treeState.depthMap = WorkflowChartService.generateDepthMap($scope.treeState.arrayOfLinksForChart); $scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
$scope.$broadcast("refreshWorkflowChart"); $scope.$broadcast("refreshWorkflowChart");
@@ -348,38 +342,39 @@ export default ['$scope', 'TemplatesService',
$scope.cancelNodeForm(); $scope.cancelNodeForm();
} }
$scope.treeState.arrayOfNodesForChart.push({ $scope.graphState.arrayOfNodesForChart.push({
index: $scope.treeState.arrayOfNodesForChart.length, index: $scope.graphState.arrayOfNodesForChart.length,
id: workflowMakerNodeIdCounter, id: workflowMakerNodeIdCounter,
isNodeBeingAdded: true,
unifiedJobTemplate: null unifiedJobTemplate: null
}); });
chartNodeIdToIndexMapping[workflowMakerNodeIdCounter] = $scope.treeState.arrayOfNodesForChart.length - 1; $scope.graphState.nodeBeingAdded = workflowMakerNodeIdCounter;
$scope.treeState.arrayOfLinksForChart.push({ chartNodeIdToIndexMapping[workflowMakerNodeIdCounter] = $scope.graphState.arrayOfNodesForChart.length - 1;
source: $scope.treeState.arrayOfNodesForChart[link.source.index],
target: $scope.treeState.arrayOfNodesForChart[chartNodeIdToIndexMapping[workflowMakerNodeIdCounter]], $scope.graphState.arrayOfLinksForChart.push({
source: $scope.graphState.arrayOfNodesForChart[link.source.index],
target: $scope.graphState.arrayOfNodesForChart[chartNodeIdToIndexMapping[workflowMakerNodeIdCounter]],
edgeType: "placeholder" edgeType: "placeholder"
}); });
$scope.nodeConfig = { $scope.nodeConfig = {
mode: "add", mode: "add",
nodeId: workflowMakerNodeIdCounter, nodeId: workflowMakerNodeIdCounter,
newNodeIsRoot: link.source.index === 0 newNodeIsRoot: link.source.id === 1
}; };
// Search for the link that used to exist between source and target and shift it to // Search for the link that used to exist between source and target and shift it to
// go from our new node to the target // go from our new node to the target
$scope.treeState.arrayOfLinksForChart.forEach((foo) => { $scope.graphState.arrayOfLinksForChart.forEach((foo) => {
if (foo.source.id === link.source.id && foo.target.id === link.target.id) { if (foo.source.id === link.source.id && foo.target.id === link.target.id) {
foo.source = $scope.treeState.arrayOfNodesForChart[chartNodeIdToIndexMapping[workflowMakerNodeIdCounter]]; foo.source = $scope.graphState.arrayOfNodesForChart[chartNodeIdToIndexMapping[workflowMakerNodeIdCounter]];
} }
}); });
workflowMakerNodeIdCounter++; workflowMakerNodeIdCounter++;
$scope.treeState.depthMap = WorkflowChartService.generateDepthMap($scope.treeState.arrayOfLinksForChart); $scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
$scope.$broadcast("refreshWorkflowChart"); $scope.$broadcast("refreshWorkflowChart");
@@ -390,17 +385,16 @@ export default ['$scope', 'TemplatesService',
const nodeIndex = chartNodeIdToIndexMapping[$scope.nodeConfig.nodeId]; const nodeIndex = chartNodeIdToIndexMapping[$scope.nodeConfig.nodeId];
if ($scope.nodeConfig.mode === "add") { if ($scope.nodeConfig.mode === "add") {
if (selectedTemplate && edgeType && edgeType.value) { if (selectedTemplate && edgeType && edgeType.value) {
// TODO: do we need to clone prompt data?
nodeRef[$scope.nodeConfig.nodeId] = { nodeRef[$scope.nodeConfig.nodeId] = {
fullUnifiedJobTemplateObject: selectedTemplate, fullUnifiedJobTemplateObject: selectedTemplate,
promptData: _.cloneDeep(promptData), promptData,
isNew: true isNew: true
}; };
$scope.treeState.arrayOfNodesForChart[nodeIndex].unifiedJobTemplate = selectedTemplate; $scope.graphState.arrayOfNodesForChart[nodeIndex].unifiedJobTemplate = selectedTemplate;
$scope.treeState.arrayOfNodesForChart[nodeIndex].isNodeBeingAdded = false; $scope.graphState.nodeBeingAdded = null;
$scope.treeState.arrayOfLinksForChart.map( (link) => { $scope.graphState.arrayOfLinksForChart.map( (link) => {
if (link.target.index === nodeIndex) { if (link.target.index === nodeIndex) {
link.edgeType = edgeType.value; link.edgeType = edgeType.value;
} }
@@ -411,8 +405,8 @@ export default ['$scope', 'TemplatesService',
nodeRef[$scope.nodeConfig.nodeId].fullUnifiedJobTemplateObject = selectedTemplate; nodeRef[$scope.nodeConfig.nodeId].fullUnifiedJobTemplateObject = selectedTemplate;
nodeRef[$scope.nodeConfig.nodeId].promptData = _.cloneDeep(promptData); nodeRef[$scope.nodeConfig.nodeId].promptData = _.cloneDeep(promptData);
nodeRef[$scope.nodeConfig.nodeId].isEdited = true; nodeRef[$scope.nodeConfig.nodeId].isEdited = true;
$scope.treeState.arrayOfNodesForChart[nodeIndex].unifiedJobTemplate = selectedTemplate; $scope.graphState.arrayOfNodesForChart[nodeIndex].unifiedJobTemplate = selectedTemplate;
$scope.treeState.arrayOfNodesForChart[nodeIndex].isNodeBeingEdited = false; $scope.graphState.nodeBeingEdited = null;
} }
} }
@@ -426,15 +420,15 @@ export default ['$scope', 'TemplatesService',
const nodeIndex = chartNodeIdToIndexMapping[$scope.nodeConfig.nodeId]; const nodeIndex = chartNodeIdToIndexMapping[$scope.nodeConfig.nodeId];
if ($scope.nodeConfig.mode === "add") { if ($scope.nodeConfig.mode === "add") {
// Remove the placeholder node from the array // Remove the placeholder node from the array
$scope.treeState.arrayOfNodesForChart.splice(nodeIndex, 1); $scope.graphState.arrayOfNodesForChart.splice(nodeIndex, 1);
// Update the links // Update the links
let parents = []; let parents = [];
let children = []; let children = [];
// Remove any links that reference this node // Remove any links that reference this node
for( let i = $scope.treeState.arrayOfLinksForChart.length; i--; ){ for( let i = $scope.graphState.arrayOfLinksForChart.length; i--; ){
const link = $scope.treeState.arrayOfLinksForChart[i]; const link = $scope.graphState.arrayOfLinksForChart[i];
if (link.source.index === nodeIndex || link.target.index === nodeIndex) { if (link.source.index === nodeIndex || link.target.index === nodeIndex) {
if (link.source.index === nodeIndex) { if (link.source.index === nodeIndex) {
@@ -444,7 +438,7 @@ export default ['$scope', 'TemplatesService',
const sourceIndex = link.source.index < nodeIndex ? link.source.index : link.source.index - 1; const sourceIndex = link.source.index < nodeIndex ? link.source.index : link.source.index - 1;
parents.push(sourceIndex); parents.push(sourceIndex);
} }
$scope.treeState.arrayOfLinksForChart.splice(i, 1); $scope.graphState.arrayOfLinksForChart.splice(i, 1);
} else { } else {
if (link.source.index > nodeIndex) { if (link.source.index > nodeIndex) {
link.source.index--; link.source.index--;
@@ -461,9 +455,9 @@ export default ['$scope', 'TemplatesService',
if (parentIndex === 0) { if (parentIndex === 0) {
child.edgeType = "always"; child.edgeType = "always";
} }
$scope.treeState.arrayOfLinksForChart.push({ $scope.graphState.arrayOfLinksForChart.push({
source: $scope.treeState.arrayOfNodesForChart[parentIndex], source: $scope.graphState.arrayOfNodesForChart[parentIndex],
target: $scope.treeState.arrayOfNodesForChart[child.index], target: $scope.graphState.arrayOfNodesForChart[child.index],
edgeType: child.edgeType edgeType: child.edgeType
}); });
}); });
@@ -477,13 +471,9 @@ export default ['$scope', 'TemplatesService',
} }
} }
$scope.treeState.depthMap = WorkflowChartService.generateDepthMap($scope.treeState.arrayOfLinksForChart); $scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
} else if ($scope.nodeConfig.mode === "edit") { } else if ($scope.nodeConfig.mode === "edit") {
$scope.treeState.arrayOfNodesForChart.map( (node) => { $scope.graphState.nodeBeingEdited = null;
if (node.index === $scope.nodeConfig.nodeId) {
node.isNodeBeingEdited = false;
}
});
} }
$scope.formState.showNodeForm = false; $scope.formState.showNodeForm = false;
$scope.nodeConfig = null; $scope.nodeConfig = null;
@@ -508,11 +498,7 @@ export default ['$scope', 'TemplatesService',
node: nodeRef[nodeToEdit.id] node: nodeRef[nodeToEdit.id]
}; };
$scope.treeState.arrayOfNodesForChart.map( (node) => { $scope.graphState.nodeBeingEdited = nodeToEdit.id;
if (node.index === nodeToEdit.index) {
node.isNodeBeingEdited = true;
}
});
$scope.formState.showNodeForm = true; $scope.formState.showNodeForm = true;
} }
@@ -525,18 +511,19 @@ export default ['$scope', 'TemplatesService',
$scope.startEditLink = (linkToEdit) => { $scope.startEditLink = (linkToEdit) => {
const setupLinkEdit = () => { const setupLinkEdit = () => {
linkToEdit.isLinkBeingEdited = true;
// Determine whether or not this link can be removed // Determine whether or not this link can be removed
// TODO: we already (potentially) loop across this array below
// and we should combine
let numberOfParents = 0; let numberOfParents = 0;
$scope.treeState.arrayOfLinksForChart.forEach((link) => { $scope.graphState.arrayOfLinksForChart.forEach((link) => {
if (link.target.id === linkToEdit.target.id) { if (link.target.id === linkToEdit.target.id) {
numberOfParents++; numberOfParents++;
} }
}); });
$scope.graphState.linkBeingEdited = {
source: linkToEdit.source.id,
target: linkToEdit.target.id
};
$scope.linkConfig = { $scope.linkConfig = {
mode: "edit", mode: "edit",
parent: { parent: {
@@ -563,12 +550,9 @@ export default ['$scope', 'TemplatesService',
if ($scope.linkConfig.parent.id !== linkToEdit.source.id || $scope.linkConfig.child.id !== linkToEdit.target.id) { if ($scope.linkConfig.parent.id !== linkToEdit.source.id || $scope.linkConfig.child.id !== linkToEdit.target.id) {
// User is going from editing one link to editing another // User is going from editing one link to editing another
if ($scope.linkConfig.mode === "add") { if ($scope.linkConfig.mode === "add") {
$scope.treeState.arrayOfLinksForChart.splice($scope.treeState.arrayOfLinksForChart.length-1, 1); $scope.graphState.arrayOfLinksForChart.splice($scope.graphState.arrayOfLinksForChart.length-1, 1);
$scope.treeState.depthMap = WorkflowChartService.generateDepthMap($scope.treeState.arrayOfLinksForChart); $scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
} }
$scope.treeState.arrayOfLinksForChart.forEach((link) => {
link.isLinkBeingEdited = false;
});
setupLinkEdit(); setupLinkEdit();
} }
} else { } else {
@@ -586,29 +570,33 @@ export default ['$scope', 'TemplatesService',
}; };
$scope.linkConfig.edgeType = "success"; $scope.linkConfig.edgeType = "success";
$scope.treeState.arrayOfNodesForChart.forEach((node) => { $scope.graphState.arrayOfNodesForChart.forEach((node) => {
node.isInvalidLinkTarget = false; node.isInvalidLinkTarget = false;
}); });
$scope.treeState.arrayOfLinksForChart.push({ $scope.graphState.arrayOfLinksForChart.push({
target: $scope.treeState.arrayOfNodesForChart[node.index], target: $scope.graphState.arrayOfNodesForChart[node.index],
source: $scope.treeState.arrayOfNodesForChart[chartNodeIdToIndexMapping[$scope.linkConfig.parent.id]], source: $scope.graphState.arrayOfNodesForChart[chartNodeIdToIndexMapping[$scope.linkConfig.parent.id]],
edgeType: "placeholder", edgeType: "placeholder"
isLinkBeingEdited: true
}); });
$scope.treeState.arrayOfLinksForChart.forEach((link, index) => { $scope.graphState.linkBeingEdited = {
source: $scope.graphState.arrayOfNodesForChart[node.index].id,
target: $scope.graphState.arrayOfNodesForChart[chartNodeIdToIndexMapping[$scope.linkConfig.parent.id]].id
};
$scope.graphState.arrayOfLinksForChart.forEach((link, index) => {
if (link.source.id === 1 && link.target.id === node.id) { if (link.source.id === 1 && link.target.id === node.id) {
$scope.treeState.arrayOfLinksForChart.splice(index, 1); $scope.graphState.arrayOfLinksForChart.splice(index, 1);
} }
}); });
$scope.treeState.depthMap = WorkflowChartService.generateDepthMap($scope.treeState.arrayOfLinksForChart); $scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
$scope.treeState.isLinkMode = false; $scope.graphState.isLinkMode = false;
} else { } else {
// This is the first node selected // This is the first node selected
$scope.treeState.addLinkSource = node.id; $scope.graphState.addLinkSource = node.id;
$scope.linkConfig = { $scope.linkConfig = {
mode: "add", mode: "add",
parent: { parent: {
@@ -621,7 +609,7 @@ export default ['$scope', 'TemplatesService',
let invalidLinkTargetIds = []; let invalidLinkTargetIds = [];
// Find and mark any ancestors as disabled to prevent cycles // Find and mark any ancestors as disabled to prevent cycles
$scope.treeState.arrayOfLinksForChart.forEach((link) => { $scope.graphState.arrayOfLinksForChart.forEach((link) => {
// id=1 is our artificial root node so we don't care about that // id=1 is our artificial root node so we don't care about that
if (link.source.id !== 1) { if (link.source.id !== 1) {
if (link.source.id === node.id) { if (link.source.id === node.id) {
@@ -648,10 +636,10 @@ export default ['$scope', 'TemplatesService',
// Filter out the duplicates // Filter out the duplicates
invalidLinkTargetIds.filter((element, index, array) => index === array.indexOf(element)).forEach((ancestorId) => { invalidLinkTargetIds.filter((element, index, array) => index === array.indexOf(element)).forEach((ancestorId) => {
$scope.treeState.arrayOfNodesForChart[chartNodeIdToIndexMapping[ancestorId]].isInvalidLinkTarget = true; $scope.graphState.arrayOfNodesForChart[chartNodeIdToIndexMapping[ancestorId]].isInvalidLinkTarget = true;
}); });
$scope.treeState.isLinkMode = true; $scope.graphState.isLinkMode = true;
$scope.formState.showLinkForm = true; $scope.formState.showLinkForm = true;
} }
@@ -660,22 +648,20 @@ export default ['$scope', 'TemplatesService',
}; };
$scope.confirmLinkForm = (newEdgeType) => { $scope.confirmLinkForm = (newEdgeType) => {
$scope.treeState.arrayOfLinksForChart.forEach((link) => { $scope.graphState.arrayOfLinksForChart.forEach((link) => {
if (link.source.id === $scope.linkConfig.parent.id && link.target.id === $scope.linkConfig.child.id) { if (link.source.id === $scope.linkConfig.parent.id && link.target.id === $scope.linkConfig.child.id) {
link.source.isLinkEditParent = false;
link.target.isLinkEditChild = false;
link.edgeType = newEdgeType; link.edgeType = newEdgeType;
link.isLinkBeingEdited = false;
} }
}); });
if ($scope.linkConfig.mode === "add") { if ($scope.linkConfig.mode === "add") {
$scope.treeState.arrayOfNodesForChart.forEach((node) => { $scope.graphState.arrayOfNodesForChart.forEach((node) => {
node.isInvalidLinkTarget = false; node.isInvalidLinkTarget = false;
}); });
} }
$scope.treeState.addLinkSource = null; $scope.graphState.linkBeingEdited = null;
$scope.graphState.addLinkSource = null;
$scope.formState.showLinkForm = false; $scope.formState.showLinkForm = false;
$scope.linkConfig = null; $scope.linkConfig = null;
$scope.$broadcast("refreshWorkflowChart"); $scope.$broadcast("refreshWorkflowChart");
@@ -683,15 +669,15 @@ export default ['$scope', 'TemplatesService',
$scope.unlink = () => { $scope.unlink = () => {
// Remove the link // Remove the link
for( let i = $scope.treeState.arrayOfLinksForChart.length; i--; ){ for( let i = $scope.graphState.arrayOfLinksForChart.length; i--; ){
const link = $scope.treeState.arrayOfLinksForChart[i]; const link = $scope.graphState.arrayOfLinksForChart[i];
if (link.source.id === $scope.linkConfig.parent.id && link.target.id === $scope.linkConfig.child.id) { if (link.source.id === $scope.linkConfig.parent.id && link.target.id === $scope.linkConfig.child.id) {
$scope.treeState.arrayOfLinksForChart.splice(i, 1); $scope.graphState.arrayOfLinksForChart.splice(i, 1);
} }
} }
$scope.treeState.depthMap = WorkflowChartService.generateDepthMap($scope.treeState.arrayOfLinksForChart); $scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
$scope.formState.showLinkForm = false; $scope.formState.showLinkForm = false;
$scope.linkConfig = null; $scope.linkConfig = null;
@@ -700,32 +686,30 @@ export default ['$scope', 'TemplatesService',
$scope.cancelLinkForm = () => { $scope.cancelLinkForm = () => {
if ($scope.linkConfig.mode === "add" && $scope.linkConfig.child) { if ($scope.linkConfig.mode === "add" && $scope.linkConfig.child) {
$scope.treeState.arrayOfLinksForChart.splice($scope.treeState.arrayOfLinksForChart.length-1, 1); $scope.graphState.arrayOfLinksForChart.splice($scope.graphState.arrayOfLinksForChart.length-1, 1);
let targetIsOrphaned = true; let targetIsOrphaned = true;
$scope.treeState.arrayOfLinksForChart.forEach((link) => { $scope.graphState.arrayOfLinksForChart.forEach((link) => {
if (link.target.id === $scope.linkConfig.child.id) { if (link.target.id === $scope.linkConfig.child.id) {
targetIsOrphaned = false; targetIsOrphaned = false;
} }
}); });
if (targetIsOrphaned) { if (targetIsOrphaned) {
// Link it to the start node // Link it to the start node
$scope.treeState.arrayOfLinksForChart.push({ $scope.graphState.arrayOfLinksForChart.push({
source: $scope.treeState.arrayOfNodesForChart[0], source: $scope.graphState.arrayOfNodesForChart[0],
target: $scope.treeState.arrayOfNodesForChart[chartNodeIdToIndexMapping[$scope.linkConfig.child.id]], target: $scope.graphState.arrayOfNodesForChart[chartNodeIdToIndexMapping[$scope.linkConfig.child.id]],
edgeType: "always" edgeType: "always"
}); });
} }
$scope.treeState.depthMap = WorkflowChartService.generateDepthMap($scope.treeState.arrayOfLinksForChart); $scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
} }
$scope.treeState.addLinkSource = null; $scope.graphState.linkBeingEdited = null;
$scope.treeState.isLinkMode = false; $scope.graphState.addLinkSource = null;
$scope.formState.showLinkForm = false; $scope.graphState.isLinkMode = false;
$scope.treeState.arrayOfNodesForChart.forEach((node) => { $scope.graphState.arrayOfNodesForChart.forEach((node) => {
node.isInvalidLinkTarget = false; node.isInvalidLinkTarget = false;
}); });
$scope.treeState.arrayOfLinksForChart.forEach((link) => { $scope.formState.showLinkForm = false;
link.isLinkBeingEdited = false;
});
$scope.linkConfig = null; $scope.linkConfig = null;
$scope.$broadcast("refreshWorkflowChart"); $scope.$broadcast("refreshWorkflowChart");
}; };
@@ -751,15 +735,15 @@ export default ['$scope', 'TemplatesService',
} }
// Remove the node from the array // Remove the node from the array
$scope.treeState.arrayOfNodesForChart.splice(nodeIndex, 1); $scope.graphState.arrayOfNodesForChart.splice(nodeIndex, 1);
// Update the links // Update the links
let parents = []; let parents = [];
let children = []; let children = [];
// Remove any links that reference this node // Remove any links that reference this node
for( let i = $scope.treeState.arrayOfLinksForChart.length; i--; ){ for( let i = $scope.graphState.arrayOfLinksForChart.length; i--; ){
const link = $scope.treeState.arrayOfLinksForChart[i]; const link = $scope.graphState.arrayOfLinksForChart[i];
if (link.source.index === nodeIndex || link.target.index === nodeIndex) { if (link.source.index === nodeIndex || link.target.index === nodeIndex) {
if (link.source.index === nodeIndex) { if (link.source.index === nodeIndex) {
@@ -769,14 +753,14 @@ export default ['$scope', 'TemplatesService',
const sourceIndex = link.source.index < nodeIndex ? link.source.index : link.source.index - 1; const sourceIndex = link.source.index < nodeIndex ? link.source.index : link.source.index - 1;
parents.push(sourceIndex); parents.push(sourceIndex);
} }
$scope.treeState.arrayOfLinksForChart.splice(i, 1); $scope.graphState.arrayOfLinksForChart.splice(i, 1);
} else { } else {
if (link.source.index > nodeIndex) { // if (link.source.index > nodeIndex) {
link.source = link.source.index - 1; // link.source.index = link.source.index - 1;
} // }
if (link.target.index > nodeIndex) { // if (link.target.index > nodeIndex) {
link.target = link.target.index - 1; // link.target.index = link.target.index - 1;
} // }
} }
} }
@@ -786,9 +770,9 @@ export default ['$scope', 'TemplatesService',
if (parentIndex === 0) { if (parentIndex === 0) {
child.edgeType = "always"; child.edgeType = "always";
} }
$scope.treeState.arrayOfLinksForChart.push({ $scope.graphState.arrayOfLinksForChart.push({
source: $scope.treeState.arrayOfNodesForChart[parentIndex], source: $scope.graphState.arrayOfNodesForChart[parentIndex],
target: $scope.treeState.arrayOfNodesForChart[child.index], target: $scope.graphState.arrayOfNodesForChart[child.index],
edgeType: child.edgeType edgeType: child.edgeType
}); });
}); });
@@ -806,7 +790,9 @@ export default ['$scope', 'TemplatesService',
} }
} }
$scope.treeState.depthMap = WorkflowChartService.generateDepthMap($scope.treeState.arrayOfLinksForChart); $scope.deleteOverlayVisible = false;
$scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
$scope.nodeToBeDeleted = null; $scope.nodeToBeDeleted = null;
$scope.deleteOverlayVisible = false; $scope.deleteOverlayVisible = false;
@@ -868,7 +854,7 @@ export default ['$scope', 'TemplatesService',
let depthMap = WorkflowChartService.generateDepthMap(arrayOfLinksForChart); let depthMap = WorkflowChartService.generateDepthMap(arrayOfLinksForChart);
$scope.treeState = { arrayOfNodesForChart, arrayOfLinksForChart, depthMap }; $scope.graphState = { arrayOfNodesForChart, arrayOfLinksForChart, depthMap };
} }
}, function ({ data, status, config }) { }, function ({ data, status, config }) {
ProcessErrors($scope, data, status, null, { ProcessErrors($scope, data, status, null, {

View File

@@ -74,7 +74,7 @@
</div> </div>
<div class="WorkflowLegend-maker--right"> <div class="WorkflowLegend-maker--right">
<span class="WorkflowMaker-totalJobs">{{strings.get('workflow_maker.TOTAL_TEMPLATES')}}</span> <span class="WorkflowMaker-totalJobs">{{strings.get('workflow_maker.TOTAL_TEMPLATES')}}</span>
<span class="badge List-titleBadge" ng-bind="treeState.arrayOfNodesForChart.length-1"></span> <span class="badge List-titleBadge" ng-bind="graphState.arrayOfNodesForChart.length-1"></span>
<i ng-class="{'WorkflowMaker-manualControlsIcon--active': showManualControls}" class="fa fa-cog WorkflowMaker-manualControlsIcon" aria-hidden="true" alt="Controls" ng-click="toggleManualControls()"></i> <i ng-class="{'WorkflowMaker-manualControlsIcon--active': showManualControls}" class="fa fa-cog WorkflowMaker-manualControlsIcon" aria-hidden="true" alt="Controls" ng-click="toggleManualControls()"></i>
<div ng-show="showManualControls" class="WorkflowMaker-manualControls noselect"> <div ng-show="showManualControls" class="WorkflowMaker-manualControls noselect">
<workflow-controls class="WorkflowControls" pan-chart="panChart(direction)" zoom-chart="zoomChart(zoom)" reset-chart="resetChart()" zoom-to-fit-chart="zoomToFitChart()"></workflow-controls> <workflow-controls class="WorkflowControls" pan-chart="panChart(direction)" zoom-chart="zoomChart(zoom)" reset-chart="resetChart()" zoom-to-fit-chart="zoomToFitChart()"></workflow-controls>
@@ -83,7 +83,7 @@
</div> </div>
<workflow-chart <workflow-chart
ng-if="modalOpen" ng-if="modalOpen"
tree-state="treeState" graph-state="graphState"
add-node-without-child="startAddNodeWithoutChild(parent)" add-node-without-child="startAddNodeWithoutChild(parent)"
add-node-with-child="startAddNodeWithChild(link)" add-node-with-child="startAddNodeWithChild(link)"
edit-node="startEditNode(nodeToEdit)" edit-node="startEditNode(nodeToEdit)"

View File

@@ -168,7 +168,7 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
let depthMap = WorkflowChartService.generateDepthMap(arrayOfLinksForChart); let depthMap = WorkflowChartService.generateDepthMap(arrayOfLinksForChart);
$scope.treeState = { arrayOfNodesForChart, arrayOfLinksForChart, depthMap }; $scope.graphState = { arrayOfNodesForChart, arrayOfLinksForChart, depthMap };
} }
$scope.toggleStdoutFullscreen = function() { $scope.toggleStdoutFullscreen = function() {
@@ -268,7 +268,7 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
runTimeElapsedTimer = workflowResultsService.createOneSecondTimer(moment(), updateWorkflowJobElapsedTimer); runTimeElapsedTimer = workflowResultsService.createOneSecondTimer(moment(), updateWorkflowJobElapsedTimer);
} }
$scope.treeState.arrayOfNodesForChart[chartNodeIdToIndexMapping[nodeIdToChartNodeIdMapping[data.workflow_node_id]]].job = { $scope.graphState.arrayOfNodesForChart[chartNodeIdToIndexMapping[nodeIdToChartNodeIdMapping[data.workflow_node_id]]].job = {
id: data.unified_job_id, id: data.unified_job_id,
status: data.status status: data.status
}; };

View File

@@ -349,7 +349,7 @@
</div> </div>
</div> </div>
<workflow-chart <workflow-chart
tree-state="treeState" graph-state="graphState"
workflow-zoomed="workflowZoomed(zoom)" workflow-zoomed="workflowZoomed(zoom)"
can-add-workflow-job-template="canAddWorkflowJobTemplate" can-add-workflow-job-template="canAddWorkflowJobTemplate"
mode="details" mode="details"