workflows are now loaded via server requests
This commit is contained in:
parent
dfb4a8f0bb
commit
c242c4a39a
635
editor.js
635
editor.js
@ -48,150 +48,29 @@ function openSearchMenu(menuitem) {
|
||||
|
||||
var workflow = {}
|
||||
|
||||
// fetch('./test.json')
|
||||
// .then((response) => response.json())
|
||||
// .then((data) => {
|
||||
// for (var key in data)
|
||||
// workflow[key] = data[key];
|
||||
// });
|
||||
|
||||
// Counters for placeholder IDs of states/actions added via GUI
|
||||
var stateIdCounter = workflow.states ? workflow.states.length : 0;
|
||||
var actionIdCounter = workflow.states ? workflow.actions.length : 0;
|
||||
|
||||
//Parse workflow
|
||||
|
||||
workflow.states.forEach(state => {
|
||||
var messages = [];
|
||||
state.stateData.messages.forEach(msg => messages.push(new Message(msg)));
|
||||
state.stateData.messages = messages;
|
||||
var viewers = [];
|
||||
state.stateData.viewers.forEach(v => viewers.push(new Role(v)));
|
||||
state.stateData.viewers = viewers;
|
||||
state.stateData.payload = new Payload(state.stateData.payload);
|
||||
})
|
||||
|
||||
workflow.actions.forEach(action => {
|
||||
var messages = [];
|
||||
action.actionData.messages.forEach(msg => messages.push(new Message(msg)));
|
||||
action.actionData.messages = messages;
|
||||
var viewers = [];
|
||||
action.actionData.viewers.forEach(v => viewers.push(new Role(v)));
|
||||
action.actionData.viewers = viewers;
|
||||
var actors = [];
|
||||
action.actionData.actors.forEach(v => actors.push(new Role(v)));
|
||||
action.actionData.actors = actors;
|
||||
var viewActors = [];
|
||||
action.actionData['actor Viewers'].forEach(v => viewActors.push(new Role(v)));
|
||||
action.actionData['actor Viewers'] = viewActors;
|
||||
action.actionData.form = new Payload(action.actionData.form);
|
||||
})
|
||||
fetch('http://localhost:8080/test.json')
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
for (var key in data)
|
||||
workflow[key] = data[key];
|
||||
runnn();
|
||||
});
|
||||
|
||||
//Actors of the workflow
|
||||
var actors = [];
|
||||
workflow.actions.forEach(act => act.actionData.actors.forEach(a => {
|
||||
var includes = false;
|
||||
actors.forEach(actor => includes = includes || equalRoles(a, actor));
|
||||
(!includes) && actors.push(a);
|
||||
(!act.actionData.actorNames) && (act.actionData.actorNames = []);
|
||||
act.actionData.actorNames.push(getRoleName(a));
|
||||
}));
|
||||
// console.log(actors);
|
||||
// workflow.actions.forEach(a => console.log(a.actionData.actorNames));
|
||||
|
||||
function getRoleName(role) {
|
||||
if (typeof role == 'string') {
|
||||
return role;
|
||||
} else if (role instanceof Role) {
|
||||
return role.name;
|
||||
} else {
|
||||
return JSON.stringify(role);
|
||||
}
|
||||
}
|
||||
|
||||
const NO_ACTOR = 'None';
|
||||
|
||||
//Prepare actor highlighting
|
||||
const selectedActor = document.getElementById('actor');
|
||||
var allActors = document.createElement('option');
|
||||
allActors.text = NO_ACTOR;
|
||||
selectedActor.add(allActors);
|
||||
actors.forEach(actor => {
|
||||
var option = document.createElement('option');
|
||||
option.text = getRoleName(actor);
|
||||
selectedActor.add(option);
|
||||
});
|
||||
|
||||
|
||||
//Viewers of the workflow
|
||||
var viewers = [];
|
||||
const selectedViewer = document.getElementById('viewer');
|
||||
//Actions/States with no explicit viewers
|
||||
var viewableByAll = []
|
||||
//Possible initiators
|
||||
var initiators = []
|
||||
//Implicit state from which initial actions can be selected
|
||||
var initState = null;
|
||||
//Identify all viewers of every action
|
||||
workflow.actions.forEach(act => {
|
||||
if (act.actionData.viewers.length === 0) {
|
||||
viewableByAll.push(act.actionData);
|
||||
} else {
|
||||
act.actionData.viewers.forEach(v => {
|
||||
var includes = false;
|
||||
viewers.forEach(viewer => includes = includes || equalRoles(v, viewer));
|
||||
(!includes) && viewers.push(v);
|
||||
(!act.actionData.viewerNames) && (act.actionData.viewerNames = []);
|
||||
act.actionData.viewerNames.push(getRoleName(v));
|
||||
})
|
||||
}
|
||||
if (act.actionData.mode === 'initial') {
|
||||
act.actionData.actorNames.forEach(an => !initiators.includes(an) && initiators.push(an));
|
||||
}
|
||||
});
|
||||
//Identify all viewers of every state
|
||||
workflow.states.forEach(st => {
|
||||
if (st.name === '@@INIT') {
|
||||
initState = st;
|
||||
} else if (st.stateData.viewers.length === 0) {
|
||||
viewableByAll.push(st.stateData);
|
||||
} else {
|
||||
st.stateData.viewers.forEach(v => {
|
||||
var includes = false;
|
||||
viewers.forEach(viewer => includes = includes || equalRoles(v, viewer));
|
||||
(!includes) && viewers.push(v);
|
||||
(!st.stateData.viewerNames) && (st.stateData.viewerNames = []);
|
||||
st.stateData.viewerNames.push(getRoleName(v));
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
initState.stateData.viewerNames = initiators;
|
||||
|
||||
const ALL_VIEW = "Not explicitly specified";
|
||||
if (viewableByAll.length > 0) {
|
||||
viewers.push(ALL_VIEW);
|
||||
var viewerNames = []
|
||||
viewers.forEach(viewer => viewerNames.push(getRoleName(viewer)));
|
||||
viewableByAll.forEach(data => {
|
||||
data.viewerNames = viewerNames;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const NO_ACTOR = 'None';
|
||||
const NO_VIEWER = NO_ACTOR;
|
||||
|
||||
//Prepare viewer highlighting
|
||||
const selectedViewer = document.getElementById('viewer');
|
||||
var allViewers = document.createElement('option');
|
||||
allViewers.text = NO_VIEWER;
|
||||
selectedViewer.add(allViewers);
|
||||
viewers.forEach(viewer => {
|
||||
var option = document.createElement('option');
|
||||
option.text = getRoleName(viewer);
|
||||
selectedViewer.add(option);
|
||||
});
|
||||
|
||||
|
||||
//source & target nodes of all currently highlighted actions
|
||||
var highlightedSources = [];
|
||||
var highlightedTargets = [];
|
||||
@ -223,12 +102,6 @@ function selectViewer() {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var selfLoops = {}; // All edges whose targets equal their sources.
|
||||
var overlappingEdges = {}; // All edges whose target and source are connected by further.
|
||||
const selfLoopCurvMin = 0.5; // Minimum curvature of a self loop.
|
||||
const curvatureMinMax = 0.2; // Minimum/maximum curvature (1 +/- x) of overlapping edges.
|
||||
|
||||
var selection = null; // The currently selected node/edge.
|
||||
var rightSelection = null; // The currently right clicked node/edge.
|
||||
var edgeTo = null; // Target of an edge to be created.
|
||||
@ -241,35 +114,179 @@ const sideButtons = document.getElementById('sidebuttons');
|
||||
const contextMenuBg = document.getElementById('ctmenubg'); //Click on background
|
||||
const contextMenuSt = document.getElementById('ctmenust'); //Click on state
|
||||
const contextMenuEd = document.getElementById('ctmenued'); //Click on edge
|
||||
// Counters for placeholder IDs of states/actions added via GUI
|
||||
var stateIdCounter = 0;
|
||||
var actionIdCounter = 0;
|
||||
var stateAbbreviations = [];
|
||||
var newStateCoords = {'x': 0, 'y': 0}; //Initial coordinates of the next new state
|
||||
|
||||
const edgeColourDefault = '#999999ff';
|
||||
const edgeColourSelected = '#000000ff';
|
||||
const edgeColourHighlightDefault = '#6ed4d4';
|
||||
const edgeColourHighlightSelected = 'magenta';
|
||||
const edgeColourSubtleDefault = '#99999955';
|
||||
const edgeColourSubtleSelected = '#00000055';
|
||||
/**
|
||||
* Marks the given item as selected.
|
||||
* @param {*} item The node or edge to select.
|
||||
*/
|
||||
function select(item) {
|
||||
contextMenuEd.style.display = contextMenuSt.style.display = contextMenuBg.style.display = 'none';
|
||||
edgeFrom = edgeTo = rightSelection = null;
|
||||
selection = selection === item ? null : item;
|
||||
if (selection === item) {
|
||||
while (sideContent.firstChild)
|
||||
sideContent.removeChild(sideContent.lastChild);
|
||||
sidePanel.style.display = 'block'
|
||||
sideHeading.innerHTML = item.name;
|
||||
var data = document.createElement('div');
|
||||
var content = generatePanelContent(selection);
|
||||
content.forEach(c => data.appendChild(c));
|
||||
sideContent.appendChild(data);
|
||||
var spStyle = window.getComputedStyle(sidePanel);
|
||||
var shStyle = window.getComputedStyle(sideHeading);
|
||||
sideContent.style.top = sideHeading.offsetHeight + parseFloat(spStyle.paddingTop) + parseFloat(shStyle.marginTop) + parseFloat(shStyle.marginBottom);
|
||||
var sbStyle = window.getComputedStyle(sideButtons);
|
||||
sideContent.style.bottom = sideButtons.offsetHeight + parseFloat(spStyle.paddingBottom) + parseFloat(sbStyle.marginTop) + parseFloat(sbStyle.marginBottom);
|
||||
// console.log(sideHeading.offsetHeight + shStyle.marginTop + shStyle.marginBottom);
|
||||
} else {
|
||||
sidePanel.style.display = 'none';
|
||||
}
|
||||
console.log(item);
|
||||
}
|
||||
|
||||
function deselect() {
|
||||
sidePanel.style.display = 'none';
|
||||
selection = null;
|
||||
}
|
||||
|
||||
|
||||
function rightSelect() {
|
||||
select(rightSelection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new state to the workflow and auto-selects it.
|
||||
*/
|
||||
function addState() {
|
||||
var nodeId = stateIdCounter ++;
|
||||
var x = newStateCoords.x;
|
||||
var y = newStateCoords.y;
|
||||
state = {id: nodeId, x: x, y: y, name: 'state_' + nodeId, fx: x, fy: y, val: 5};
|
||||
workflow.states.push(state);
|
||||
updateGraph();
|
||||
select(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new action between two states.
|
||||
* @param {*} source The source state.
|
||||
* @param {*} target The target state.
|
||||
*/
|
||||
function connect(source, target) {
|
||||
let linkId = actionIdCounter ++;
|
||||
action = {id: linkId, source: source, target: target, name: 'action_' + linkId, actionData: {
|
||||
viewerNames: [], actorNames: []
|
||||
}};
|
||||
workflow.actions.push(action);
|
||||
updateGraph();
|
||||
select(action);
|
||||
}
|
||||
|
||||
function markEdgeTo() {
|
||||
edgeTo = rightSelection;
|
||||
contextMenuSt.style.display = 'none';
|
||||
}
|
||||
|
||||
function markEdgeFrom() {
|
||||
edgeFrom = rightSelection;
|
||||
contextMenuSt.style.display = 'none';
|
||||
}
|
||||
|
||||
function removeSelection() {
|
||||
if (selection) {
|
||||
if (selection.actionData) removeAction(selection);
|
||||
else removeState(selection);
|
||||
deselect();
|
||||
edgeFrom = edgeTo = rightSelection = null;
|
||||
contextMenuEd.style.display = contextMenuSt.style.display = contextMenuBg.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function removeRightSelection() {
|
||||
if (rightSelection) {
|
||||
if (rightSelection.actionData) removeAction(rightSelection);
|
||||
else removeState(rightSelection);
|
||||
if (selection === rightSelection) deselect();
|
||||
edgeFrom = edgeTo = rightSelection = null;
|
||||
contextMenuEd.style.display = contextMenuSt.style.display = contextMenuBg.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function generatePanelContent(selection) {
|
||||
var children = [];
|
||||
var data = selection.stateData || selection.actionData
|
||||
for (var key in data) {
|
||||
if (key === 'viewerNames' || key === 'actorNames') continue;
|
||||
var h = document.createElement('h2');
|
||||
var heading = document.createTextNode(key.substring(0,1).toUpperCase() + key.substring(1));
|
||||
h.appendChild(heading);
|
||||
children.push(h);
|
||||
var content = data[key];
|
||||
if (content instanceof Array && content.length > 0 && content[0] instanceof Message) {
|
||||
content.forEach(msg => msg.format().forEach(child => children.push(child)));
|
||||
} else if (content instanceof Payload) {
|
||||
content.format().forEach(child => children.push(child));
|
||||
} else if (content instanceof Array && content.length > 0 && content[0] instanceof Role) {
|
||||
var viewerList = document.createElement('ul');
|
||||
content.forEach(viewer => {
|
||||
var v = document.createElement('li');
|
||||
v.appendChild(document.createTextNode(viewer.name));
|
||||
viewerList.appendChild(v);
|
||||
});
|
||||
children.push(viewerList);
|
||||
} else {
|
||||
var p = document.createElement('p');
|
||||
var text = document.createTextNode(JSON.stringify(data[key]));
|
||||
p.appendChild(text);
|
||||
children.push(p);
|
||||
}
|
||||
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an edge from the workflow.
|
||||
* @param {*} action The action to remove.
|
||||
*/
|
||||
function removeAction(action) {
|
||||
workflow.actions.splice(workflow.actions.indexOf(action), 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if two roles are equal.
|
||||
* @param {*} role1
|
||||
* @param {*} role2
|
||||
* @returns
|
||||
* Removes a state from the workflow.
|
||||
* @param {*} state The state to remove.
|
||||
*/
|
||||
function equalRoles(role1, role2) {
|
||||
role1 instanceof Role && (role1 = role1.json);
|
||||
role2 instanceof Role && (role2 = role2.json);
|
||||
var equal = role1.tag === role2.tag;
|
||||
if (role1.tag == 'payload-reference') {
|
||||
equal = equal && (role1['payload-label'] === role2['payload-label']);
|
||||
} else if (role1.tag == 'user') {
|
||||
equal = equal && (role1.user === role2.user);
|
||||
} else if (role1.tag == 'authorized') {
|
||||
equal = equal && (role1.authorized['dnf-terms'][0][0].var === role2.authorized['dnf-terms'][0][0].var);
|
||||
}
|
||||
return equal;
|
||||
function removeState(state) {
|
||||
workflow.actions
|
||||
.filter(edge => edge.source === state || edge.target === state)
|
||||
.forEach(edge => removeAction(edge));
|
||||
workflow.states.splice(workflow.states.indexOf(state), 1);
|
||||
var abbreviation = state.stateData && state.stateData.abbreviation;
|
||||
abbreviation && stateAbbreviations.splice(stateAbbreviations.indexOf(abbreviation), 1);
|
||||
}
|
||||
|
||||
var selfLoops = {}; // All edges whose targets equal their sources.
|
||||
var overlappingEdges = {}; // All edges whose target and source are connected by further.
|
||||
const selfLoopCurvMin = 0.5; // Minimum curvature of a self loop.
|
||||
const curvatureMinMax = 0.2; // Minimum/maximum curvature (1 +/- x) of overlapping edges.
|
||||
|
||||
const wfGraph = ForceGraph()
|
||||
|
||||
/**
|
||||
* Updates the nodes and edges of the workflow graph.
|
||||
*/
|
||||
function updateGraph() {
|
||||
identifyOverlappingEdges()
|
||||
computeCurvatures()
|
||||
wfGraph(document.getElementById('graph')).graphData({nodes: workflow.states, links: workflow.actions});
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies and stores self loops as well as overlapping edges (i.e. multiple edges sharing the
|
||||
@ -319,170 +336,153 @@ function computeCurvatures() {
|
||||
}
|
||||
|
||||
|
||||
function generatePanelContent(selection) {
|
||||
var children = [];
|
||||
var data = selection.stateData || selection.actionData
|
||||
for (var key in data) {
|
||||
if (key === 'viewerNames' || key === 'actorNames') continue;
|
||||
var h = document.createElement('h2');
|
||||
var heading = document.createTextNode(key.substring(0,1).toUpperCase() + key.substring(1));
|
||||
h.appendChild(heading);
|
||||
children.push(h);
|
||||
var content = data[key];
|
||||
if (content instanceof Array && content.length > 0 && content[0] instanceof Message) {
|
||||
content.forEach(msg => msg.format().forEach(child => children.push(child)));
|
||||
} else if (content instanceof Payload) {
|
||||
content.format().forEach(child => children.push(child));
|
||||
} else if (content instanceof Array && content.length > 0 && content[0] instanceof Role) {
|
||||
var viewerList = document.createElement('ul');
|
||||
content.forEach(viewer => {
|
||||
var v = document.createElement('li');
|
||||
v.appendChild(document.createTextNode(viewer.name));
|
||||
viewerList.appendChild(v);
|
||||
});
|
||||
children.push(viewerList);
|
||||
} else {
|
||||
var p = document.createElement('p');
|
||||
var text = document.createTextNode(JSON.stringify(data[key]));
|
||||
p.appendChild(text);
|
||||
children.push(p);
|
||||
}
|
||||
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Marks the given item as selected.
|
||||
* @param {*} item The node or edge to select.
|
||||
*/
|
||||
function select(item) {
|
||||
contextMenuEd.style.display = contextMenuSt.style.display = contextMenuBg.style.display = 'none';
|
||||
edgeFrom = edgeTo = rightSelection = null;
|
||||
selection = selection === item ? null : item;
|
||||
if (selection === item) {
|
||||
while (sideContent.firstChild)
|
||||
sideContent.removeChild(sideContent.lastChild);
|
||||
sidePanel.style.display = 'block'
|
||||
sideHeading.innerHTML = item.name;
|
||||
var data = document.createElement('div');
|
||||
var content = generatePanelContent(selection);
|
||||
content.forEach(c => data.appendChild(c));
|
||||
sideContent.appendChild(data);
|
||||
var spStyle = window.getComputedStyle(sidePanel);
|
||||
var shStyle = window.getComputedStyle(sideHeading);
|
||||
sideContent.style.top = sideHeading.offsetHeight + parseFloat(spStyle.paddingTop) + parseFloat(shStyle.marginTop) + parseFloat(shStyle.marginBottom);
|
||||
var sbStyle = window.getComputedStyle(sideButtons);
|
||||
sideContent.style.bottom = sideButtons.offsetHeight + parseFloat(spStyle.paddingBottom) + parseFloat(sbStyle.marginTop) + parseFloat(sbStyle.marginBottom);
|
||||
// console.log(sideHeading.offsetHeight + shStyle.marginTop + shStyle.marginBottom);
|
||||
function runnn() {
|
||||
|
||||
stateIdCounter = workflow.states ? workflow.states.length : 0;
|
||||
actionIdCounter = workflow.states ? workflow.actions.length : 0;
|
||||
|
||||
//Parse workflow
|
||||
|
||||
workflow.states.forEach(state => {
|
||||
var messages = [];
|
||||
state.stateData.messages.forEach(msg => messages.push(new Message(msg)));
|
||||
state.stateData.messages = messages;
|
||||
var viewers = [];
|
||||
state.stateData.viewers.forEach(v => viewers.push(new Role(v)));
|
||||
state.stateData.viewers = viewers;
|
||||
state.stateData.payload = new Payload(state.stateData.payload);
|
||||
})
|
||||
|
||||
workflow.actions.forEach(action => {
|
||||
var messages = [];
|
||||
action.actionData.messages.forEach(msg => messages.push(new Message(msg)));
|
||||
action.actionData.messages = messages;
|
||||
var viewers = [];
|
||||
action.actionData.viewers.forEach(v => viewers.push(new Role(v)));
|
||||
action.actionData.viewers = viewers;
|
||||
var actors = [];
|
||||
action.actionData.actors.forEach(v => actors.push(new Role(v)));
|
||||
action.actionData.actors = actors;
|
||||
var viewActors = [];
|
||||
action.actionData['actor Viewers'].forEach(v => viewActors.push(new Role(v)));
|
||||
action.actionData['actor Viewers'] = viewActors;
|
||||
action.actionData.form = new Payload(action.actionData.form);
|
||||
})
|
||||
|
||||
workflow.actions.forEach(act => act.actionData.actors.forEach(a => {
|
||||
var includes = false;
|
||||
actors.forEach(actor => includes = includes || equalRoles(a, actor));
|
||||
(!includes) && actors.push(a);
|
||||
(!act.actionData.actorNames) && (act.actionData.actorNames = []);
|
||||
act.actionData.actorNames.push(getRoleName(a));
|
||||
}));
|
||||
// console.log(actors);
|
||||
// workflow.actions.forEach(a => console.log(a.actionData.actorNames));
|
||||
|
||||
function getRoleName(role) {
|
||||
if (typeof role == 'string') {
|
||||
return role;
|
||||
} else if (role instanceof Role) {
|
||||
return role.name;
|
||||
} else {
|
||||
sidePanel.style.display = 'none';
|
||||
return JSON.stringify(role);
|
||||
}
|
||||
}
|
||||
|
||||
//Prepare actor highlighting
|
||||
var allActors = document.createElement('option');
|
||||
allActors.text = NO_ACTOR;
|
||||
selectedActor.add(allActors);
|
||||
actors.forEach(actor => {
|
||||
var option = document.createElement('option');
|
||||
option.text = getRoleName(actor);
|
||||
selectedActor.add(option);
|
||||
});
|
||||
|
||||
//Identify all viewers of every action
|
||||
workflow.actions.forEach(act => {
|
||||
if (act.actionData.viewers.length === 0) {
|
||||
viewableByAll.push(act.actionData);
|
||||
} else {
|
||||
act.actionData.viewers.forEach(v => {
|
||||
var includes = false;
|
||||
viewers.forEach(viewer => includes = includes || equalRoles(v, viewer));
|
||||
(!includes) && viewers.push(v);
|
||||
(!act.actionData.viewerNames) && (act.actionData.viewerNames = []);
|
||||
act.actionData.viewerNames.push(getRoleName(v));
|
||||
})
|
||||
}
|
||||
console.log(item);
|
||||
}
|
||||
|
||||
|
||||
function rightSelect() {
|
||||
select(rightSelection);
|
||||
}
|
||||
|
||||
function deselect() {
|
||||
sidePanel.style.display = 'none';
|
||||
selection = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the nodes and edges of the workflow graph.
|
||||
*/
|
||||
function updateGraph() {
|
||||
identifyOverlappingEdges()
|
||||
computeCurvatures()
|
||||
Graph.graphData({nodes: workflow.states, links: workflow.actions});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new action between two states.
|
||||
* @param {*} source The source state.
|
||||
* @param {*} target The target state.
|
||||
*/
|
||||
function connect(source, target) {
|
||||
let linkId = actionIdCounter ++;
|
||||
action = {id: linkId, source: source, target: target, name: 'action_' + linkId, actionData: {
|
||||
viewerNames: [], actorNames: []
|
||||
}};
|
||||
workflow.actions.push(action);
|
||||
updateGraph();
|
||||
select(action);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new state to the workflow and auto-selects it.
|
||||
*/
|
||||
function addState() {
|
||||
var nodeId = stateIdCounter ++;
|
||||
var x = newStateCoords.x;
|
||||
var y = newStateCoords.y;
|
||||
state = {id: nodeId, x: x, y: y, name: 'state_' + nodeId, fx: x, fy: y, val: 5};
|
||||
workflow.states.push(state);
|
||||
updateGraph();
|
||||
select(state);
|
||||
}
|
||||
|
||||
function markEdgeTo() {
|
||||
edgeTo = rightSelection;
|
||||
contextMenuSt.style.display = 'none';
|
||||
}
|
||||
|
||||
function markEdgeFrom() {
|
||||
edgeFrom = rightSelection;
|
||||
contextMenuSt.style.display = 'none';
|
||||
}
|
||||
|
||||
function removeSelection() {
|
||||
if (selection) {
|
||||
if (selection.actionData) removeAction(selection);
|
||||
else removeState(selection);
|
||||
deselect();
|
||||
edgeFrom = edgeTo = rightSelection = null;
|
||||
contextMenuEd.style.display = contextMenuSt.style.display = contextMenuBg.style.display = 'none';
|
||||
if (act.actionData.mode === 'initial') {
|
||||
act.actionData.actorNames.forEach(an => !initiators.includes(an) && initiators.push(an));
|
||||
}
|
||||
}
|
||||
|
||||
function removeRightSelection() {
|
||||
if (rightSelection) {
|
||||
if (rightSelection.actionData) removeAction(rightSelection);
|
||||
else removeState(rightSelection);
|
||||
if (selection === rightSelection) deselect();
|
||||
edgeFrom = edgeTo = rightSelection = null;
|
||||
contextMenuEd.style.display = contextMenuSt.style.display = contextMenuBg.style.display = 'none';
|
||||
});
|
||||
//Identify all viewers of every state
|
||||
workflow.states.forEach(st => {
|
||||
if (st.name === '@@INIT') {
|
||||
initState = st;
|
||||
} else if (st.stateData.viewers.length === 0) {
|
||||
viewableByAll.push(st.stateData);
|
||||
} else {
|
||||
st.stateData.viewers.forEach(v => {
|
||||
var includes = false;
|
||||
viewers.forEach(viewer => includes = includes || equalRoles(v, viewer));
|
||||
(!includes) && viewers.push(v);
|
||||
(!st.stateData.viewerNames) && (st.stateData.viewerNames = []);
|
||||
st.stateData.viewerNames.push(getRoleName(v));
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
initState.stateData.viewerNames = initiators;
|
||||
|
||||
const ALL_VIEW = "Not explicitly specified";
|
||||
if (viewableByAll.length > 0) {
|
||||
viewers.push(ALL_VIEW);
|
||||
var viewerNames = []
|
||||
viewers.forEach(viewer => viewerNames.push(getRoleName(viewer)));
|
||||
viewableByAll.forEach(data => {
|
||||
data.viewerNames = viewerNames;
|
||||
});
|
||||
}
|
||||
|
||||
//Prepare viewer highlighting
|
||||
var allViewers = document.createElement('option');
|
||||
allViewers.text = NO_VIEWER;
|
||||
selectedViewer.add(allViewers);
|
||||
viewers.forEach(viewer => {
|
||||
var option = document.createElement('option');
|
||||
option.text = getRoleName(viewer);
|
||||
selectedViewer.add(option);
|
||||
});
|
||||
|
||||
const edgeColourDefault = '#999999ff';
|
||||
const edgeColourSelected = '#000000ff';
|
||||
const edgeColourHighlightDefault = '#6ed4d4';
|
||||
const edgeColourHighlightSelected = 'magenta';
|
||||
const edgeColourSubtleDefault = '#99999955';
|
||||
const edgeColourSubtleSelected = '#00000055';
|
||||
|
||||
|
||||
/**
|
||||
* Removes an edge from the workflow.
|
||||
* @param {*} action The action to remove.
|
||||
* Checks if two roles are equal.
|
||||
* @param {*} role1
|
||||
* @param {*} role2
|
||||
* @returns
|
||||
*/
|
||||
function removeAction(action) {
|
||||
workflow.actions.splice(workflow.actions.indexOf(action), 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes a state from the workflow.
|
||||
* @param {*} state The state to remove.
|
||||
*/
|
||||
function removeState(state) {
|
||||
workflow.actions
|
||||
.filter(edge => edge.source === state || edge.target === state)
|
||||
.forEach(edge => removeAction(edge));
|
||||
workflow.states.splice(workflow.states.indexOf(state), 1);
|
||||
var abbreviation = state.stateData && state.stateData.abbreviation;
|
||||
abbreviation && stateAbbreviations.splice(stateAbbreviations.indexOf(abbreviation), 1);
|
||||
function equalRoles(role1, role2) {
|
||||
role1 instanceof Role && (role1 = role1.json);
|
||||
role2 instanceof Role && (role2 = role2.json);
|
||||
var equal = role1.tag === role2.tag;
|
||||
if (role1.tag == 'payload-reference') {
|
||||
equal = equal && (role1['payload-label'] === role2['payload-label']);
|
||||
} else if (role1.tag == 'user') {
|
||||
equal = equal && (role1.user === role2.user);
|
||||
} else if (role1.tag == 'authorized') {
|
||||
equal = equal && (role1.authorized['dnf-terms'][0][0].var === role2.authorized['dnf-terms'][0][0].var);
|
||||
}
|
||||
return equal;
|
||||
}
|
||||
|
||||
|
||||
@ -531,7 +531,6 @@ function getEdgeColour(edge) {
|
||||
}
|
||||
|
||||
//Compute abbreviations of the names of all states
|
||||
var stateAbbreviations = [];
|
||||
workflow.states.forEach(state => {
|
||||
// var label = node.name.substring(0, 5);
|
||||
var label = state.name.split(' '); // [node.name.substring(0, 6), node.name.substring(6, 12), node.name.substring(12, 18)];
|
||||
@ -563,10 +562,7 @@ function openContextMenu(x, y, menu) {
|
||||
edgeFrom = edgeTo = null;
|
||||
}
|
||||
|
||||
var newStateCoords = {'x': 0, 'y': 0}; //Initial coordinates of the next new state
|
||||
|
||||
const Graph = ForceGraph()
|
||||
(document.getElementById('graph'))
|
||||
wfGraph
|
||||
.linkDirectionalArrowLength(6)
|
||||
.linkDirectionalArrowRelPos(1)
|
||||
.linkColor(getEdgeColour)
|
||||
@ -574,7 +570,7 @@ const Graph = ForceGraph()
|
||||
.linkCanvasObjectMode(() => 'after')
|
||||
.linkCanvasObject((edge, context) => {
|
||||
const MAX_FONT_SIZE = 4;
|
||||
const LABEL_NODE_MARGIN = Graph.nodeRelSize() * edge.source.val * 1.5;
|
||||
const LABEL_NODE_MARGIN = wfGraph.nodeRelSize() * edge.source.val * 1.5;
|
||||
|
||||
const source = edge.source;
|
||||
const target = edge.target;
|
||||
@ -716,7 +712,7 @@ const Graph = ForceGraph()
|
||||
closeMenuItem();
|
||||
})
|
||||
.onBackgroundRightClick(event => {
|
||||
newStateCoords = Graph.screen2GraphCoords(event.layerX, event.layerY);
|
||||
newStateCoords = wfGraph.screen2GraphCoords(event.layerX, event.layerY);
|
||||
openContextMenu(event.layerX, event.layerY, contextMenuBg);
|
||||
contextMenuEd.style.display = contextMenuSt.style.display = 'none';
|
||||
edgeFrom = edgeTo = rightSelection = null;
|
||||
@ -724,4 +720,5 @@ const Graph = ForceGraph()
|
||||
})
|
||||
.autoPauseRedraw(false);
|
||||
|
||||
updateGraph();
|
||||
updateGraph();
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user