diagram.lib.components.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of common-base Show documentation
Show all versions of common-base Show documentation
DSL based modeling framework - facilities common base
var graph;
function queryParameter(parameterName) {
let query = window.location.search.substring(1);
let parms = query.split('&');
for (let i = 0; i < parms.length; i++) {
let pos = parms[i].indexOf('=');
if (pos > 0 && parameterName == parms[i].substring(0, pos)) {
return parms[i].substring(pos + 1).replace(/%22/g, '');
}
}
return undefined;
}
function collectBases(currentObject, all, hierarchyObjects) {
let currentBase = currentObject.extends;
if (currentBase !== undefined) {
for (let object of all) {
if (object.name === currentBase) {
hierarchyObjects.push(object);
collectBases(object, all, hierarchyObjects);
return;
}
}
}
}
function determineProvide(component, featureId) {
if (component && component.provides && featureId) {
for (const provide of component.provides) {
if (provide.id === featureId) {
return provide
}
}
}
return null
}
function draw(graph, parent, layout, groupLayout, data, diagramCtx) {
let dependsOnVertexes = null
let focusedVertex = null
const focusedComponent = diagramCtx.focused ? findComponentByQualifiedName(data.components, diagramCtx.focused) : undefined
const objMap = {}
const objIdMap = {}
const groupMap = {}
if (data.scenarios) {
const ctx = {
top: 10
}
for (const scenario of data.scenarios) {
drawScenarioOverview(graph, layout, parent, scenario, ctx)
}
} else {
let sizing = {
currentX: 0,
currentY: 0
}
if (data.groups) {
for (const group of data.groups) {
const groupInfo = {
group: group,
members: []
}
groupMap[group.name] = groupInfo
}
}
const provideMap = {}
let componentMap = {}
let components = data.components
if (components) {
for (let component of components) {
componentMap[component.name] = component;
objIdMap[component.id] = component;
}
}
const currentLocation = window.location.href
const locationParts = currentLocation.split('?')
const currentPath = locationParts[0]
if (components) {
diagramCtx.consumingComponent = diagramCtx.consumer ? findComponentByQualifiedName(components, diagramCtx.consumer) : undefined
/* draw focused component first (if exists) */
if (focusedComponent) {
const componentStyle = addComponentFillColor('componentTitle', focusedComponent)
drawComponent(graph, layout, parent, parent, sizing, currentPath, diagramCtx, focusedComponent, true, true, groupMap, objMap, objIdMap, provideMap,null, componentStyle );
dependsOnVertexes = objMap[focusedComponent.id].dependsOnVertexes
focusedVertex = objMap[focusedComponent.id].vertex
}
for (let component of components) {
if (focusedComponent) {
if (focusedComponent !== component) {
if ((!diagramCtx.consumingComponent || diagramCtx.consumingComponent === component) && isDependentOn(component, focusedComponent)) {
const otherComponentStyle = addOtherComponentFillColor('otherComponent', component)
drawComponent(graph, layout, parent, parent, sizing, currentPath, diagramCtx, component, false, true, groupMap, objMap, objIdMap, provideMap, null, otherComponentStyle);
}
}
} else {
const componentStyle = addComponentFillColor('componentTitle', component)
drawComponent(graph, layout, parent, parent, sizing, currentPath, diagramCtx, component, false, false, groupMap, objMap, objIdMap, provideMap, null, componentStyle);
}
}
/* layout inside groups */
if (!diagramCtx.focused) {
layoutGroups(graph, groupMap)
}
for (let component of components) {
if (!focusedComponent || focusedComponent !== component) {
drawRelationOfComponent(graph, parent, parent, sizing, component, focusedComponent, objMap, diagramCtx);
}
}
}
}
// Executes the layout
layout.execute(parent);
/* draw relations from focused component to other components */
if (dependsOnVertexes) {
const ddd = new Set()
const eee = new Set()
for (const dependsOnId in dependsOnVertexes) {
const dependsOnVertex = dependsOnVertexes[dependsOnId]
ddd.add(dependsOnVertex)
const e1 = graph.insertEdge(parent, null, '', focusedVertex, dependsOnVertex,'treeConnectionStyle;entryX=0;entryY=0.5');
eee.add(e1)
}
const layout2 = new mxCompactTreeLayout(graph);
layout2.forceConstant = 220;
layout2.root = focusedVertex
layout2.useBoundingBox = false;
layout2.edgeRouting = false;
layout2.levelDistance = 200;
layout2.nodeDistance = 20;
layout2.maintainParentLocation = false;
layout2.isVertexIgnored = function(vertex) {
const tt= !ddd.has(vertex)
return vertex !== focusedVertex && tt
}
layout2.execute(parent,focusedVertex )
const deactiveRelationToEdgeInfos = [];
const activeRelationToEdgeInfos = [];
const alreadyConnectedMap = new Map()
const selfComponentInfo = objMap[focusedComponent.id]
const selfVertex = selfComponentInfo.vertex
if (focusedComponent.dependsOn) {
for (const dependsOnItem of focusedComponent.dependsOn) {
const targetComponent = dependsOnItem.component
const targetVertex = dependsOnVertexes[targetComponent]
for (const consume of dependsOnItem.consumes) {
if (consume.consumedBy) {
const operationId = consume.consumedBy
/* check if the operation is already connected to the component */
let connectedComponents = alreadyConnectedMap.get(operationId)
if (!connectedComponents) {
connectedComponents = new Set();
alreadyConnectedMap.set(operationId, connectedComponents)
}
if (connectedComponents.has(targetComponent)) {
continue
} else {
connectedComponents.add(targetComponent)
}
const operationInfo = selfComponentInfo.componentOperationMap[operationId]
if (operationInfo) {
const operationVertex = operationInfo.vertex
if (operationVertex) {
const description = createConsumeDescription(consume)
const tt = objIdMap[targetComponent]
const provide = determineProvide(tt, consume.featureId)
const provideName = provide.name
const doc = mxUtils.createXmlDocument()
const infoNode = doc.createElement('info')
infoNode.setAttribute('label', '');
infoNode.setAttribute('featureId', consume.featureId)
infoNode.setAttribute('operation', consume.operation)
if (consume.operationName) {
infoNode.setAttribute('operationName', consume.operationName)
}
if (provideName) {
infoNode.setAttribute('provideName', provideName)
}
if (description) {
infoNode.setAttribute('description', description);
}
const operationGeometry = operationVertex.geometry;
const componentGeometry = selfVertex.geometry;
const lineEndX = operationVertex.parent.parent.geometry.width - 35 // - operationGeometry.x
const rightLineVertex1 = graph.insertVertex(operationVertex.parent, null, null, lineEndX, operationGeometry.y + operationGeometry.height/2, 0, 0,'position:relative');
/* copy infos from operation vertex (to be able to access it on double-click of edge */
const operationDelegateInfo = copyInfoNode(operationVertex.getValue())
operationDelegateInfo.setAttribute('label', '')
operationDelegateInfo.setAttribute('operationDelegate', 'true');
const rightLineVertex2 = graph.insertVertex(operationVertex.parent, null, operationDelegateInfo, lineEndX-12, operationGeometry.y + operationGeometry.height/2, 0, 0,'position:relative');
graph.insertEdge(parent, null, null, operationVertex, rightLineVertex1, 'usesLineStyleFocused;exitX=1;exitY=0.5;entryX=0;entryY=0.5');
activeRelationToEdgeInfos.push({
infoNode: infoNode,
sourceVertx: rightLineVertex2,
targetVertex: targetVertex
})
} else {
console.log("no operation vertex")
}
} else {
const featureId = consume.consumedByFeature
const provideInfo = selfComponentInfo.componentProvideMap[featureId];
const featureVertex = provideInfo.vertex
if (featureVertex) {
const description = createConsumeDescription(consume)
const doc = mxUtils.createXmlDocument()
const infoNode = doc.createElement('info')
infoNode.setAttribute('label', '');
if (description) {
infoNode.setAttribute('description', description);
}
const featurePortVertex = graph.insertVertex(featureVertex, null, null, featureVertex.geometry.width, featureVertex.geometry.height/2, 0, 0,'position:relative');
if (diagramCtx.providingFeature) {
deactiveRelationToEdgeInfos.push({
infoNode: infoNode,
sourceVertx: featurePortVertex,
targetVertex: targetVertex
})
} else {
activeRelationToEdgeInfos.push({
infoNode: infoNode,
sourceVertx: featurePortVertex,
targetVertex: targetVertex
})
}
}
console.log("no operation info")
}
} else if (consume.consumedByFeature) {
/* fallback in case consumedBy is not defined, but consumedByFeature is defined */
const featureId = consume.consumedByFeature
const provideInfo = selfComponentInfo.componentProvideMap[featureId];
const featureVertex = provideInfo.vertex
if (featureVertex) {
const description = createConsumeDescription(consume)
const doc = mxUtils.createXmlDocument()
const infoNode = doc.createElement('info')
infoNode.setAttribute('label', '');
if (description) {
infoNode.setAttribute('description', description);
}
const featurePortVertex = graph.insertVertex(featureVertex, null, null, featureVertex.geometry.width, featureVertex.geometry.height/2, 0, 0,'position:relative');
if (diagramCtx.providingFeature) {
deactiveRelationToEdgeInfos.push({
infoNode: infoNode,
sourceVertx: featurePortVertex,
targetVertex: targetVertex
})
} else {
activeRelationToEdgeInfos.push({
infoNode: infoNode,
sourceVertx: featurePortVertex,
targetVertex: targetVertex
})
}
}
} else {
console.log("no consumedBy")
}
}
}
for (const edgeInfo of deactiveRelationToEdgeInfos) {
graph.insertEdge(parent, null, edgeInfo.infoNode, edgeInfo.sourceVertx, edgeInfo.targetVertex, 'usesStyleFocused;strokeColor=#b4c3d9;exitX=1;exitY=0.5;entryX=0;entryY=0.5');
}
for (const edgeInfo of activeRelationToEdgeInfos) {
graph.insertEdge(parent, null, edgeInfo.infoNode, edgeInfo.sourceVertx, edgeInfo.targetVertex, 'usesStyleFocused;exitX=1;exitY=0.5;entryX=0;entryY=0.5');
}
}
}
/* draw detail-relations to operations of focused component */
if (!data.scenarios && data.components) {
for (const component of data.components) {
if (focusedComponent && focusedComponent !== component) {
if (component.dependsOn) {
const selfComponentInfo = objMap[component.id]
if (!selfComponentInfo) {
continue
}
const selfVertex = selfComponentInfo.vertex
for (const dependsOnItem of component.dependsOn) {
const targetComponentInfo = objMap[dependsOnItem.component]
if (targetComponentInfo && dependsOnItem.component == focusedComponent.id && dependsOnItem.consumes) {
for (const consume of dependsOnItem.consumes) {
const featureId = consume.featureId
const provideInfo = targetComponentInfo.componentProvideMap[featureId];
const operationId = consume.operation
if (provideInfo && provideInfo.operationVertexes) {
const description = createConsumeDescription(consume)
const doc = mxUtils.createXmlDocument()
const infoNode = doc.createElement('info')
infoNode.setAttribute('label', '');
if (description) {
infoNode.setAttribute('description', description);
}
const operationVertex = provideInfo.operationVertexes[operationId]
graph.insertEdge(parent, null, infoNode, selfVertex, operationVertex, 'usesStyleFocused;exitX=1;exitY=0.5;entryX=0;entryY=0.5');
}
}
}
}
}
}
}
}
}
function layoutGroups(graph, groupMap) {
for (const groupName in groupMap) {
const groupInfo = groupMap[groupName]
const groupCell = graph.groupCells(groupInfo.vertex, 10, groupInfo.members)
groupCell.geometry.x = groupInfo.group.x
groupCell.geometry.y = groupInfo.group.y
}
for (const groupName in groupMap) {
const groupLayout2 = new mxFastOrganicLayout(graph);
groupLayout2.forceConstant = 120
groupLayout2.edgeRouting = false;
groupLayout2.maintainParentLocation = false;
groupLayout2.useBoundingBox = true
const groupInfo = groupMap[groupName]
const vertex = groupInfo.vertex
groupLayout2.execute(vertex)
}
}
function addComponentFillColor(style, component) {
if (component) {
if (component.color) {
return style + ';fillColor=' + component.color
}
if (component.stereotype == 'storage') {
return style + ';fillColor=#f58e29'
} else if (component.stereotype == 'ux') {
return style + ';fillColor=#5aa9f1'
} else if (component.stereotype == 'extern') {
//const c ='#bbbbbb'
return style + ';fillColor=#a8a8a8'
}
}
return style
}
function addOtherComponentFillColor(style, component) {
if (component) {
if (component.color) {
return style + ';fillColor=' + component.color
}
if (component.stereotype == 'storage') {
return style + ';fillColor=#f58e29'
} else if (component.stereotype == 'ux') {
return style + ';fillColor=#5aa9f1'
} else if (component.stereotype == 'extern') {
return style + ';fillColor=#a8a8a8'
}
}
return style
}
function addPortFillColor(style, component, providingFeatureName) {
if (component) {
if (component.color) {
return style + ';fillColor=' + component.color
}
if (component.stereotype == 'storage') {
return style + ';fillColor=#ffb267;fontColor=#835b36'
} else if (component.stereotype == 'ux') {
const c = '#295483'
return style + ';fillColor=#abd2ff;fontColor=#295483'
} else if (component.stereotype == 'extern') {
return style + ';fillColor=#d2d2d2;fontColor=#707070'
} else if (providingFeatureName) {
const c = '#94dcea'
return style + ';fillColor=' + c
}
}
return style
}
function getPortFontColor(component) {
if (component.stereotype == 'storage') {
return '#835b36'
} else if (component.stereotype == 'ux') {
return '#4376b7'
} else if (component.stereotype == 'extern') {
return '#707070'
} else {
} return '#497f81'
}
function findComponentById(components, id) {
if (components) {
for (let component of components) {
if (component.id == id) {
return component
}
}
}
return undefined
}
function qualifiedComponentName(component) {
return component.namespace + '.' + component.name
}
function findComponentByQualifiedName(components, qualifiedName) {
if (components) {
for (let component of components) {
if (qualifiedName == qualifiedComponentName(component)) {
return component
}
}
}
return undefined
}
function createVertexInfo(label, description) {
let doc = mxUtils.createXmlDocument();
let node = doc.createElement('info')
if (label) {
node.setAttribute('label', label);
}
if (description) {
node.setAttribute('description', description);
}
return node
}
function isDependentOn(component, otherComponent) {
const otherComponentId = otherComponent.id
if (component.dependsOn) {
for (const dependsOnItem of component.dependsOn) {
if (dependsOnItem.component == otherComponentId) {
return true
}
}
}
return false
}
function drawRelationOfComponent(graph, rootParent, parent, sizing, component, focusedComponent, objMap, diagramCtx) {
const componentInfo = objMap[component.id]
if (!componentInfo) {
return
}
const selfVertex = componentInfo.vertex
if (component.dependsOn) {
for (const dependsOnItem of component.dependsOn) {
const targetComponentInfo = objMap[dependsOnItem.component]
if (!targetComponentInfo) {
//console.log("Unexpected - target component info is not defined for component id: " + dependsOnItem.component)
continue
}
const dependsOnFeatures = collectDependsOnFeatureIds(dependsOnItem);
if (focusedComponent) {
for (const featureId of dependsOnFeatures) {
const provideInfo = targetComponentInfo.componentProvideMap[featureId];
const shouldBeDrawn = !focusedComponent || dependsOnItem.component === focusedComponent.id
// console.log("ProvideInfo for featureId "+ featureId + ": "+ provideInfo + " operationVertexes: "+provideInfo.operationVertexes)
if (provideInfo && shouldBeDrawn) {
const provideVertex = provideInfo.vertex
let doc = mxUtils.createXmlDocument()
let infoNode = doc.createElement('info')
infoNode.setAttribute('label', '');
infoNode.setAttribute('description', createConsumeDescription(dependsOnItem, featureId));
const style = provideInfo.provide.ui ? 'uiUsesStyleFocused' : 'usesStyleFocused'
const e0 = graph.insertEdge(parent, null, infoNode, targetComponentInfo.vertex, selfVertex, 'treeConnectionStyle');
/* just relations to provides but not to operations */
if (!provideInfo.operationVertexes) {
if (diagramCtx.providingFeature) {
let c = '#b4c3d9'
const e1 = graph.insertEdge(parent, null, infoNode, selfVertex, provideVertex, style +';strokeColor=#b4c3d9;exitX=1;exitY=0.5;entryX=0;entryY=0.5');
} else {
const e1 = graph.insertEdge(parent, null, infoNode, selfVertex, provideVertex, style +';exitX=1;exitY=0.5;entryX=0;entryY=0.5');
}
}
}
}
} else {
let hasFrontendConnection = false
let hasBackendConnection = false
const protocolConnections = {};
for (const dependsOnFeatureId of dependsOnFeatures) {
const provideInfo = targetComponentInfo.componentProvideMap[dependsOnFeatureId];
if (provideInfo) {
if (provideInfo.provide.type.endsWith('Page')) {
let pageProtocolConnection = protocolConnections['Page']
if (!pageProtocolConnection) {
pageProtocolConnection = {}
protocolConnections['Page'] = pageProtocolConnection
}
hasFrontendConnection = true
} else {
let protocol = provideInfo.provide.protocol
if (!protocol) {
protocol = ''
}
let protocolConnection = protocolConnections[protocol]
if (!protocolConnection) {
protocolConnection = {}
protocolConnections[protocol] = protocolConnection
}
hasBackendConnection = true
}
}
}
const selfGroupInfo = componentInfo.groupInfo
if (selfGroupInfo) {
graph.insertEdge(parent, null, '', selfGroupInfo.vertex, targetComponentInfo.vertex, 'treeConnectionStyle');
}
for (const protocol in protocolConnections) {
let doc = mxUtils.createXmlDocument()
let infoNode = doc.createElement('info')
infoNode.setAttribute('label', '');
infoNode.setAttribute('description', createDependencyDescription(dependsOnItem));
if (protocol == 'Page') {
infoNode.setAttribute('label', 'forward');
graph.insertEdge(parent, null, infoNode, selfVertex, targetComponentInfo.vertex, 'usesFrontendStyle;labelBackgroundColor=white');
} else {
if (protocol) {
infoNode.setAttribute('label', protocol);
} else {
//infoNode.setAttribute('label', 'https');
}
graph.insertEdge(parent, null, infoNode, selfVertex, targetComponentInfo.vertex, 'usesStyle');
}
}
// TODO consider also that target is grouped
}
}
}
}
function collectDependsOnFeatureIds(dependsOnItem) {
let distinctFeatureIds = new Set()
if (dependsOnItem.consumes) {
for (const consume of dependsOnItem.consumes) {
let featureId = consume.featureId
if (featureId) {
distinctFeatureIds.add(consume.featureId)
}
}
}
return distinctFeatureIds;
}
function createDependencyDescription(dependsOnItem) {
if (dependsOnItem.consumes) {
let distinctFeatureNames = new Set()
for (const consume of dependsOnItem.consumes) {
let text = consume.featureName
if (text) {
distinctFeatureNames.add(consume.featureName)
}
}
let desc = ''
for (const featureName of distinctFeatureNames) {
desc += featureName+ '
'
}
return desc
}
return '...'
}
function createConsumeDescription(dependsOnItem, featureId) {
if (dependsOnItem.consumes) {
let distinctFeatureNames = new Set()
for (const consume of dependsOnItem.consumes) {
if (consume.featureId === featureId) {
if (consume.requestLabel) {
distinctFeatureNames.add(consume.requestLabel)
} else {
distinctFeatureNames.add(consume.operationName)
}
}
}
let desc = ''
for (const featureName of distinctFeatureNames) {
desc += escapeString(featureName)+ '
'
}
return desc
}
return '...'
}
function createConsumeDescription(consume) {
if (consume) {
let desc = undefined
if (consume.requestLabel) {
desc = consume.requestLabel
} else {
desc = consume.operationName
}
if (desc) {
return escapeString(desc)
}
}
return undefined
}
function drawScenarioOverview(graph, layout, parent, scenario, ctx) {
const scenarioWidth = 370;
const scenarioHeight = 42;
const componentUrl = scenario.href
const labelText = ""+scenario.name+""
let doc = mxUtils.createXmlDocument()
let actorNode = doc.createElement('info')
actorNode.setAttribute('label', escapeString(labelText));
actorNode.setAttribute('description',scenario.description)
let vObj = graph.insertVertex(parent, null, actorNode, 0, ctx.top, scenarioWidth, scenarioHeight,'scenarioOverview');
ctx.top += scenarioHeight+ 20;
}
function createComponentInfoNode(component, currentPath) {
const doc = mxUtils.createXmlDocument()
const node = doc.createElement('info')
node.setAttribute('component',true)
node.setAttribute('label', '');
node.setAttribute('description',component.description)
node.setAttribute('name',component.name)
node.setAttribute('namespace',component.namespace)
node.setAttribute('focusedUrl', currentPath + "?focused=" + qualifiedComponentName(component))
if (component.x && component.y) {
node.setAttribute('fixed',true)
}
return node
}
function copyInfoNode(sourceNode) {
if (sourceNode) {
const doc = mxUtils.createXmlDocument()
const node = doc.createElement('info')
for (const attributeName of sourceNode.getAttributeNames()) {
const sourceValue = sourceNode.getAttribute(attributeName)
node.setAttribute(attributeName, sourceValue)
}
return node
}
return null
}
function createComponentTitleNode(component, currentPath) {
const componentUrl = currentPath + "?focused=" + qualifiedComponentName(component)
const escapedLabel = escapeString(component.label)
const labelText = ""+escapedLabel+""
const doc = mxUtils.createXmlDocument()
let node = doc.createElement('info');
node.setAttribute('label', labelText);
return node
}
function drawComponent(graph, layout, rootParent, parent, sizing, currentPath, diagramCtx, component, isFocused, hasFocused, groupMap, objMap, objIdMap, provideMap, parentComponentInfo, componentStyle) {
let objWidth = 180
const actorNode = createComponentInfoNode(component, currentPath)
let isGroup = component.components
let componentWidth = isGroup ? component.components.length * (objWidth +35): objWidth
let componentHeight = 50
let titleStyle = componentStyle
if (isFocused) {
componentWidth=300
}
let componentParent = parent
let vGroupInfo = null
if (!hasFocused && component.group) {
vGroupInfo = groupMap[component.group]
if (vGroupInfo && !vGroupInfo.vertex) {
let groupDoc = mxUtils.createXmlDocument()
let groupNode = groupDoc.createElement('info')
groupNode.setAttribute('description',component.description)
groupNode.setAttribute('name',component.group)
//groupNode.setAttribute('label',component.group)
groupNode.setAttribute('isGroup', true)
const group = vGroupInfo.group
let vGroupVertex
if (!hasFocused && group.x && group.y) {
groupNode.setAttribute('fixed', true)
vGroupVertex = graph.insertVertex(parent, null, groupNode, 10, 10, 50, 20, 'graphGroupStyle');
} else {
vGroupVertex = graph.insertVertex(parent, '', groupNode, 0, 0, 50, 20, 'graphGroupStyle');
}
vGroupInfo.vertex = vGroupVertex
}
if (vGroupInfo) {
componentParent=vGroupInfo.vertex
}
}
let vObj = null
if (!hasFocused && component.x && component.y) {
vObj = graph.insertVertex(componentParent, '', actorNode, component.x, component.y, componentWidth, componentHeight, componentStyle);
} else {
vObj = graph.insertVertex(componentParent, '', actorNode, 0, 0, componentWidth, componentHeight, componentStyle);
}
if (vGroupInfo) {
vGroupInfo.members.push(vObj)
vGroupInfo.count = vGroupInfo.count+1
}
if (isFocused && component.dependsOn) {
let consumeCount = 0
for (const dependsOnComponent of component.dependsOn) {
for (const consume of dependsOnComponent.consumes) {
consumeCount++
}
}
let consumeIndex = 0
for (const dependsOnComponent of component.dependsOn) {
for (const consume of dependsOnComponent.consumes) {
}
}
}
const titleNode = createComponentTitleNode(component, currentPath);
const componentUrl = currentPath + "?focused=" + qualifiedComponentName(component)
// const escapedLabel = escapeString(component.label)
// const labelText = ""+escapedLabel+""
//
// const doc = mxUtils.createXmlDocument()
// let titleNode = doc.createElement('info');
// titleNode.setAttribute('label', labelText);
let titleTop = isGroup ? 17 : 30
let vTitle = graph.insertVertex(vObj, null, titleNode, 0, 0, componentWidth - 40, titleTop, titleStyle);
vTitle.geometry.offset = new mxPoint(20, 10);
vTitle.geometry.relative = true
const componentProvideMap = {}
const componentOperationMap = {}
let participantInfo = {
type: 'component',
name: component.name,
label: component.label,
component: component,
vertex: vObj,
parent: parentComponentInfo,
componentProvideMap: componentProvideMap,
componentOperationMap: componentOperationMap,
groupInfo: vGroupInfo
}
let providesHeight = 0
if (component.provides) {
let operationVertexes = null
const providingFeatureName = diagramCtx.providingFeature
const componentNamespace = component.namespace
const componentName = component.name
let portY = 45;
for (const provide of component.provides) {
operationVertexes = null
const isProvidedFeature = providingFeatureName && diagramCtx.providingFeature == provide.name
let providePortVertex = null
if (isFocused) {
const portX=-15 ;
let portHeight = 35
const consumePortStyle = addPortFillColor('consumePort', component, providingFeatureName)
let provideDoc = mxUtils.createXmlDocument()
let provideNode = provideDoc.createElement('info')
provideNode.setAttribute('provide',true)
provideNode.setAttribute('name', provide.name)
const providesWith = isProvidedFeature ? 300 : 270
var v11 = graph.insertVertex(vObj, null, provideNode, 0, 0, providesWith, 25, consumePortStyle);
v11.geometry.offset = new mxPoint(portX, portY);
v11.geometry.relative = true;
const providingFeatureUrl = componentUrl + "&providingFeature=" + provide.name
const portFontColor = getPortFontColor(component)
const labelText = ""+provide.name+""
/* show controller */
let controllerHeight = 0
if (isProvidedFeature && provide.controller) {
const lastDotIndex = provide.controller.lastIndexOf('.')
const controllerShort = lastDotIndex > 0 ? provide.controller.substring(lastDotIndex + 1 ) : provide.controller
const controllerSize = mxUtils.getSizeForString(controllerShort)
controllerHeight = controllerSize.height
const controllerUrl = composeControllerUrl(componentNamespace,componentName, provide.controller)
const controllerColor = '#6199ab'
const controllerLabel = ""+controllerShort+""
graph.insertVertex(v11, null, controllerLabel, providesWith-controllerSize.width, 0, controllerSize.width+5, controllerHeight,';strokeWidth=0')
controllerHeight -=5
}
const resourceLabelStyle= isProvidedFeature ? consumePortStyle + ";fontStyle=1" : consumePortStyle
graph.insertVertex(v11, null, labelText, 7, 5 + controllerHeight, 265, 15, resourceLabelStyle, false);
providePortVertex = v11;
if (isProvidedFeature) {
operationVertexes = { }
let path = provide.name
let controller = provide.controller
let controllerTag = provide.controllerTag
let heightOfPathAndOperations = drawOperations(graph, v11, diagramCtx.applicationNamespace, componentNamespace, componentName, path, controllerTag, provide.operations, operationVertexes, 25 + controllerHeight)
if (provide.resources) {
const subResourceTop = 25
const subResourceCtx = {
xOffset: 20,
yOffset: controllerHeight,
height: controllerHeight,
applicationNamespace: diagramCtx.applicationNamespace
}
subResourceCtx.height+=heightOfPathAndOperations
subResourceCtx.yOffset += heightOfPathAndOperations
for (const subResource of provide.resources) {
drawSubResource(graph, v11, componentNamespace, componentName, path, controllerTag, subResource, operationVertexes, consumePortStyle, subResourceCtx)
subResourceCtx.yOffset = subResourceTop + subResourceCtx.height -25
}
portHeight = 10 + subResourceCtx.height
} else {
portHeight = 10 + heightOfPathAndOperations
}
v11.geometry.height = portHeight
portHeight = portHeight + 10
for (x in operationVertexes) {
componentOperationMap[x] = {
vertex: operationVertexes[x],
provideInfo: provide
}
}
}
providesHeight+=portHeight
portY += portHeight
}
let provideInfo = {
componentInfo: participantInfo,
provide: provide,
vertex: providePortVertex,
operationVertexes: operationVertexes
};
provideMap[provide.id] = provideInfo;
componentProvideMap[provide.id] = provideInfo;
}
}
if (isFocused) {
let geometry = vObj.geometry
geometry.height = 50 + providesHeight
/* additionally draw components on which the focused component depends on */
const dependsOnVertexes = {}
if (component.dependsOn) {
for (const dependsOnItem of component.dependsOn) {
const dependsOnComponent = objIdMap[dependsOnItem.component]
if (dependsOnComponent != component) {
const dependsOnComponentUrl = currentPath + "?focused=" + qualifiedComponentName(dependsOnComponent)
const dependsOnComponentNode = createComponentInfoNode(dependsOnComponent)
dependsOnComponentNode.setAttribute('isDependsOn', true)
const titleNode = createComponentTitleNode(dependsOnComponent, currentPath);
const dependsOnLabelText = ""+dependsOnComponent.label+""
const otherComponentStyle = addOtherComponentFillColor('otherComponent', dependsOnComponent)
//let vDepObj = graph.insertVertex(parent, null, dependsOnLabelText, 0, 0, 180, 50, otherComponentStyle);
let vDepObj = graph.insertVertex(parent, null, dependsOnComponentNode, 0, 0, 180, 50, otherComponentStyle);
let titleTop = isGroup ? 17 : 30
let vDepTitle = graph.insertVertex(vDepObj, null, titleNode, 0, 0, 180-40, titleTop, otherComponentStyle);
vDepTitle.geometry.offset = new mxPoint(20, 10);
vDepTitle.geometry.relative = true
dependsOnVertexes[dependsOnItem.component] = vDepObj
}
}
}
participantInfo.dependsOnVertexes= dependsOnVertexes
}
if (parentComponentInfo) {
participantInfo.top += parentComponentInfo.top
}
objMap[component.id] = participantInfo;
return null
}
function drawSubResource(graph, parent, componentNamespace, componentName, parentPath, controllerTag, resource, operationVertexes, consumePortStyle, subResourceCtx) {
//subResourceCtx.height+=20
const name = resource.path ? resource.path : resource.path + '...'
let doc = mxUtils.createXmlDocument()
let node = doc.createElement('info')
node.setAttribute('label','')
node.setAttribute('subResource', true);
const vSubResource = graph.insertVertex(parent, null, node, subResourceCtx.xOffset, subResourceCtx.yOffset, 210, 15, consumePortStyle)
const vSubResourceLabel = graph.insertVertex(vSubResource, null, '/' + name, 5,5, 230, 15, consumePortStyle+";fontStyle=1")
const path = parentPath + '/' + resource.path
let heightOfPathAndOperations = drawOperations(graph, vSubResource, subResourceCtx.applicationNamespace, componentNamespace, componentName, path, controllerTag, resource.operations, operationVertexes, 25)
vSubResource.geometry.height = heightOfPathAndOperations
subResourceCtx.height+=heightOfPathAndOperations
subResourceCtx.yOffset += heightOfPathAndOperations
if (resource.resources) {
const clonedSubResourceCtx = { ...subResourceCtx }
clonedSubResourceCtx.xOffset +=12
clonedSubResourceCtx.height = 0
for (const subResource of resource.resources ) {
drawSubResource(graph, parent, componentNamespace, componentName, path, controllerTag, subResource, operationVertexes, consumePortStyle, clonedSubResourceCtx)
clonedSubResourceCtx.yOffset = subResourceCtx.yOffset + clonedSubResourceCtx.height
}
subResourceCtx.height+=clonedSubResourceCtx.height
}
}
function composeOperationClickUrl(applicationNamespace, componentNamespace, componentName, controller, operation) {
if (operation.verb && !operation.hasDetails) {
return composeOpenApiUrl(componentNamespace, componentName, controller, operation)
}
return composeApiDiagramUrl(applicationNamespace, componentName, controller, operation)
}
/* compose url to open-api documentation of operation */
function composeOpenApiUrl(componentNamespace, componentName, controller, operation) {
const tag = controller ? controller : 'default'
return './api/' + componentNamespace + '_' + componentName.toLowerCase() +'.html#/'+tag+'/'+operation.operationId
}
/* compose url to sequence diagram of operation */
function composeApiDiagramUrl(applicationNamespace, componentName, controller, operation) {
const tag = controller ? controller : 'default'
return "components/" +componentName +"/"+ applicationNamespace +'_'+operation.operationId + '.html'
}
function drawOperations(graph, parent, applicationNamespace, componentNamespace, componentName, path, controller, operations, operationVertexes, operationsYOffset) {
let heightOfPathAndOperations = 20
if (operations) {
//let operationsYOffset = 25
let operationsHeight = 0
for (const operation of operations) {
//const operationUrl = composeOpenApiUrl(componentNamespace, componentName, controller, operation)
const operationUrl = composeOperationClickUrl(applicationNamespace, componentNamespace, componentName, controller, operation)
const labelText = ""+operation.name+""
let doc = mxUtils.createXmlDocument()
let node = doc.createElement('info')
node.setAttribute('description',operation.operationId)
node.setAttribute('label',labelText)
node.setAttribute('operation', true);
const operationWidth = mxUtils.getSizeForString(operation.name).width + 15;
const vOperation = graph.insertVertex(parent, null, node, 17, operationsYOffset, operationWidth, 18,addVerbColor('operation', operation.verb))
operationVertexes[operation.id] = vOperation
operationsYOffset +=20
operationsHeight +=20
}
heightOfPathAndOperations = 23 + operationsHeight
}
return heightOfPathAndOperations
}
function addVerbColor(style, verb) {
if (verb) {
if (verb == 'post') {
//const color = '#f85843'
return style + ';fillColor=#2ead29'
} else if (verb == 'get') {
return style + ';fillColor=#14a0fd'
} else if (verb == 'put') {
return style + ';fillColor=#f8b22f'
} else if (verb == 'delete') {
return style + ';fillColor=#f85843'
}
}
return style
}
function createLabel(graph, parent, interactionInfo, isName, x, rightSide, top) {
let text = isName ? interactionInfo.interaction.name : interactionInfo.interaction.result
let labelRect = isName ? interactionInfo.labelSize : interactionInfo.resultLabelSize
let doc = mxUtils.createXmlDocument();
let titleNode = doc.createElement('info');
titleNode.setAttribute('label', escapeString(text));
let labelWidth = labelRect.width;
let left = rightSide ? x + 15 : x-labelWidth-15
let labelStyle = rightSide ? 'leftLabel' : 'rightLabel'
let vertex = graph.insertVertex(parent, null, titleNode, 0, 0, 0, 0,labelStyle)
vertex.geometry.offset = new mxPoint(left, top);
vertex.geometry.width = labelRect.width
vertex.geometry.height = labelRect.height
vertex.geometry.relative = true;
//vertex.geometry.alternateBounds=new mxRectangle(left, top, labelRect.width, labelRect.height);
return vertex
}
function escapeString(str) {
if (str) {
/*
& &
' '
< <
> >
" "
Ä Ä
Ö Ö
Ü Ü
ä ä
ö ö
ü ü
ß ß
*/
let escaped = str.replace("ö","ö")
escaped = escaped.replace("ü","ü")
escaped = escaped.replace("ä","ä")
escaped = escaped.replace("ß","ß")
escaped = escaped.replace('"',""")
escaped = escaped.replace('Ä',"Ä")
escaped = escaped.replace('Ö',"Ö")
escaped = escaped.replace('Ü',"ß")
return escaped
}
return str
}
function downloadLayout(graph, parent, fileName) {
const originCoordinates = {}
const positions = []
for (const child of parent.children) {
addChildPosition(positions, child, originCoordinates, false)
}
const dx = 0 - originCoordinates.x +1
const dy = 0 - originCoordinates.y +1
for (const position of positions) {
if (!position.isGroupChild) {
position.x = position.x + dx
position.y = position.y + dy
}
}
const layoutPositions = {
positions: positions
}
save(fileName, JSON.stringify(layoutPositions))
}
function addChildPosition(positions, child, originCoordinates, isGroupChild) {
const name = child.getAttribute('name')
if (name) {
const x = child.geometry.x
const y = child.geometry.y
if (originCoordinates) {
if (!originCoordinates.x || x < originCoordinates.x) {
originCoordinates.x = x
}
if (!originCoordinates.y || y < originCoordinates.y) {
originCoordinates.y = y
}
}
positions.push({
name: name,
isGroupChild: isGroupChild,
x: x,
y: y
})
if (child.getAttribute('isGroup')) {
for (const groupChild of child.children) {
addChildPosition(positions, groupChild, null, true)
}
}
}
}
function downloadSVG(graph, fileName) {
let background = '#ffffff';
let scale = 1;
let border = 1;
let imgExport = new mxImageExport();
let bounds = graph.getGraphBounds();
let vs = graph.view.scale;
// Prepares SVG document that holds the output
let svgDoc = mxUtils.createXmlDocument();
let root = (svgDoc.createElementNS != null) ?
svgDoc.createElementNS(mxConstants.NS_SVG, 'svg') : svgDoc.createElement('svg');
if (background != null) {
if (root.style != null) {
root.style.backgroundColor = background;
} else {
root.setAttribute('style', 'background-color:' + background);
}
}
if (svgDoc.createElementNS == null) {
root.setAttribute('xmlns', mxConstants.NS_SVG);
root.setAttribute('xmlns:xlink', mxConstants.NS_XLINK);
} else {
// KNOWN: Ignored in IE9-11, adds namespace for each image element instead. No workaround.
root.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', mxConstants.NS_XLINK);
}
root.setAttribute('width', (Math.ceil(bounds.width * scale / vs) + 2 * border) + 'px');
root.setAttribute('height', (Math.ceil(bounds.height * scale / vs) + 2 * border) + 'px');
root.setAttribute('version', '1.1');
// Adds group for anti-aliasing via transform
let group = (svgDoc.createElementNS != null) ?
svgDoc.createElementNS(mxConstants.NS_SVG, 'g') : svgDoc.createElement('g');
group.setAttribute('transform', 'translate(0.5,0.5)');
root.appendChild(group);
svgDoc.appendChild(root);
// Renders graph. Offset will be multiplied with state's scale when painting state.
let svgCanvas = new mxSvgCanvas2D(group);
svgCanvas.translate(Math.floor((border / scale - bounds.x) / vs), Math.floor((border / scale - bounds.y) / vs));
svgCanvas.scale(scale / vs);
// Displayed if a viewer does not support foreignObjects (which is needed to HTML output)
svgCanvas.foAltText = '[Not supported by viewer]';
imgExport.drawState(graph.getView().getState(graph.model.root), svgCanvas);
save(fileName, mxUtils.getPrettyXml(root))
}
function save(filename, data) {
const blob = new Blob([data], {type: 'image/svg'});
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, filename);
} else {
const elem = window.document.createElement('a');
elem.href = window.URL.createObjectURL(blob, {oneTimeOnly: true});
elem.download = filename;
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
}
}
function determineOperationVertex(cell) {
if (cell) {
const isOperation = cell.getAttribute('operation')
if (isOperation) {
return cell
}
if (cell.parent) {
return determineOperationVertex(cell.parent)
}
return null
}
}
function determineProvideVertex(cell) {
if (cell) {
const isProvide = cell.getAttribute('provide')
if (isProvide) {
return cell
}
if (cell.parent) {
return determineProvideVertex(cell.parent)
}
return null
}
}
function determineComponentVertex(cell) {
if (cell) {
const isComponent = cell.getAttribute('component')
if (isComponent) {
return cell
}
if (cell.parent) {
return determineComponentVertex(cell.parent)
}
return null
}
}
function main(container, titleLink, diagramName) {
// Checks if browser is supported
if (!mxClient.isBrowserSupported()) {
// Displays an error message if the browser is
// not supported.
mxUtils.error('Browser is not supported!', 200, false);
} else {
// Disables the built-in context menu
mxEvent.disableContextMenu(container);
mxConstants.VERTEX_SELECTION_COLOR = '#15808c';
mxConstants.VERTEX_SELECTION_STROKEWIDTH = 2;
mxConstants.VERTEX_SELECTION_DASHED = true;
mxGraph.prototype.isHtmlLabel = function (cell) {
return true;
};
// Creates the graph inside the given container
let graph = new mxGraph(container);
graph.foldingEnabled = false;
graph.convertValueToString = function (cell) {
let label = cell.getAttribute('label');
if (label !== undefined) {
return label;
}
return cell.value;
};
const searchParamsOfPage = new URLSearchParams(window.location.search);
const diagramCtx = {
focused: searchParamsOfPage.get('focused'),
consumer: searchParamsOfPage.get('consumer'),
providingFeature: searchParamsOfPage.get('providingFeature')
};
// Prevent moving certain cells
// graph.isCellMovable = function (cell) {
// return cell.getAttribute("isClass") !== undefined;
// };
graph.isCellSelectable = function (cell) {
const isGroup = cell.getAttribute("isGroup") !== undefined
const isComponent = cell.getAttribute("component") !== undefined
return (isGroup || isComponent) && !diagramCtx.focused
}
graph.border = 80;
graph.getView().translate = new mxPoint(graph.border / 2, graph.border / 2);
function CollateShape()
{
mxEllipse.call(this);
};
mxUtils.extend(CollateShape, mxEllipse);
CollateShape.prototype.paintVertexShape = function(c, x, y, w, h)
{
c.begin();
c.moveTo(x, y);
c.lineTo(x, y+h);
c.lineTo(x + w - 8 , y+h);
c.lineTo(x + w , y+h - 8);
c.lineTo(x + w , y);
c.close();
c.fillAndStroke();
};
mxCellRenderer.registerShape('collate', CollateShape);
// Adds rubberband selection
new mxRubberband(graph);
// Installs a custom global tooltip
graph.setTooltips(true);
graph.getTooltip = function (state) {
let cell = state.cell;
let model = this.getModel();
if (model.isEdge(cell)) {
let description = cell.getAttribute('description', null)
if (description) {
return description
}
return "consumed info..."
} else {
if (mxUtils.isNode(cell.value)) {
let description = cell.getAttribute('description', null)
if (description) {
return description
}
}
return null;
}
};
// Changes the default vertex style in-place
var style = graph.getStylesheet().getDefaultVertexStyle();
style[mxConstants.STYLE_PERIMETER_SPACING] = 6;
// style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
// style[mxConstants.STYLE_GRADIENTCOLOR] = 'white';
style[mxConstants.STYLE_STROKEWIDTH] = 0;
style[mxConstants.STYLE_STROKECOLOR] = 'rgb(81,108,180)';
style[mxConstants.STYLE_RESIZABLE] = 0;
// style[mxConstants.STYLE_ROUNDED] = true;
//style[mxConstants.STYLE_SHADOW] = true;
style[mxConstants.STYLE_FILLCOLOR] = 'none';
style[mxConstants.STYLE_FOLDABLE] = 0;
style = [];
style[mxConstants.STYLE_STROKEWIDTH] = 0;
style[mxConstants.STYLE_STROKE_OPACITY] = 0;
style[mxConstants.STYLE_FONTCOLOR] = '#eea52b';
graph.getStylesheet().putCellStyle('actorTitle', style);
style = [];
style[mxConstants.STYLE_STROKEWIDTH] = 0;
style[mxConstants.STYLE_STROKE_OPACITY] = 0;
style[mxConstants.STYLE_FONTCOLOR] = '#4e9c9f';
graph.getStylesheet().putCellStyle('groupTitle', style);
style = [];
style[mxConstants.STYLE_STROKEWIDTH] = 0;
style[mxConstants.STYLE_STROKE_OPACITY] = 0;
style[mxConstants.STYLE_FONTCOLOR] = 'rgb(58,93,157)';
style[mxConstants.STYLE_ALIGN] = 'left';
graph.getStylesheet().putCellStyle('leftLabel', style);
style = [];
style[mxConstants.STYLE_STROKEWIDTH] = 0;
style[mxConstants.STYLE_STROKE_OPACITY] = 0;
style[mxConstants.STYLE_FONTCOLOR] = 'rgb(58,93,157)';
style[mxConstants.STYLE_ALIGN] = 'right';
graph.getStylesheet().putCellStyle('rightLabel', style);
style = [];
style[mxConstants.STYLE_STROKECOLOR] = 'rgb(210,161,75)';
style[mxConstants.STYLE_OPACITY] = 100;
style[mxConstants.STYLE_STROKEWIDTH] = 3;
style[mxConstants.STYLE_FILL_OPACITY] = 0;
style[mxConstants.STYLE_STROKE_OPACITY] = 100;
style[mxConstants.STYLE_ENDARROW] = mxConstants.NONE;
style[mxConstants.STYLE_PERIMETER_SPACING] = -6;
graph.getStylesheet().putCellStyle('noteLeftLineStyle', style);
style = [];
style[mxConstants.STYLE_STROKEWIDTH] = 0;
style[mxConstants.STYLE_STROKE_OPACITY] = 0;
style[mxConstants.STYLE_STROKECOLOR] = '#497f81';
style[mxConstants.STYLE_FONTCOLOR] = '#ffffff';
style[mxConstants.STYLE_FILLCOLOR] = '#5bbac7';
style[mxConstants.STYLE_ROUNDED] = true;
style[mxConstants.STYLE_ARCSIZE] = 6;
style[mxConstants.STYLE_ABSOLUTE_ARCSIZE] = 1;
graph.getStylesheet().putCellStyle('componentTitle', style);
style = [];
style[mxConstants.STYLE_STROKEWIDTH] = 0;
style[mxConstants.STYLE_STROKE_OPACITY] = 0;
style[mxConstants.STYLE_STROKECOLOR] = '#497f81';
style[mxConstants.STYLE_FONTCOLOR] = '#ffffff';
style[mxConstants.STYLE_FILLCOLOR] = 'rgb(133,139,248)';
style[mxConstants.STYLE_ROUNDED] = true;
style[mxConstants.STYLE_ARCSIZE] = 6;
style[mxConstants.STYLE_ABSOLUTE_ARCSIZE] = 1;
graph.getStylesheet().putCellStyle('scenarioOverview', style);
style = [];
style[mxConstants.STYLE_STROKEWIDTH] = 0;
style[mxConstants.STYLE_STROKE_OPACITY] = 0;
style[mxConstants.STYLE_STROKECOLOR] = '#7dcfde';
style[mxConstants.STYLE_FONTCOLOR] = '#497f81';
style[mxConstants.STYLE_FILLCOLOR] = '#94dcea';
style[mxConstants.STYLE_ROUNDED] = true;
style[mxConstants.STYLE_ARCSIZE] = 2;
style[mxConstants.STYLE_ALIGN] = 'left';
style[mxConstants.STYLE_ABSOLUTE_ARCSIZE] = 1;
graph.getStylesheet().putCellStyle('consumePort', style);
style = [];
style[mxConstants.STYLE_STROKEWIDTH] = 0;
style[mxConstants.STYLE_STROKE_OPACITY] = 0;
style[mxConstants.STYLE_FONTCOLOR] = '#ffffff';
style[mxConstants.STYLE_FILLCOLOR] = '#2c8593';
style[mxConstants.STYLE_ROUNDED] = true;
style[mxConstants.STYLE_ARCSIZE] = 4;
style[mxConstants.STYLE_ALIGN] = 'center';
style[mxConstants.STYLE_ABSOLUTE_ARCSIZE] = 1;
graph.getStylesheet().putCellStyle('operation', style);
style = [];
style[mxConstants.STYLE_STROKEWIDTH] = 0;
style[mxConstants.STYLE_STROKE_OPACITY] = 0;
style[mxConstants.STYLE_FONTCOLOR] = '#ffffff';
style[mxConstants.STYLE_FILLCOLOR] = '#5bbac7';
style[mxConstants.STYLE_ROUNDED] = true;
style[mxConstants.STYLE_ARCSIZE] = 6;
style[mxConstants.STYLE_ABSOLUTE_ARCSIZE] = 1;
graph.getStylesheet().putCellStyle('otherComponent', style);
style = [];
style[mxConstants.STYLE_STROKEWIDTH] = 0;
style[mxConstants.STYLE_STROKE_OPACITY] = 0;
style[mxConstants.STYLE_FILLCOLOR] = '#f5fafa';
style[mxConstants.STYLE_ROUNDED] = true;
style[mxConstants.STYLE_ARCSIZE] = 2;
style[mxConstants.STYLE_ABSOLUTE_ARCSIZE] = 1;
graph.getStylesheet().putCellStyle('groupStyle', style);
style = [];
style[mxConstants.STYLE_STROKEWIDTH] = 0;
style[mxConstants.STYLE_STROKE_OPACITY] = 0;
style[mxConstants.STYLE_FILLCOLOR] = '#f5fafa';
style[mxConstants.STYLE_ROUNDED] = true;
style[mxConstants.STYLE_ARCSIZE] = 2;
style[mxConstants.STYLE_ABSOLUTE_ARCSIZE] = 1;
graph.getStylesheet().putCellStyle('graphGroupStyle', style);
style = [];
style[mxConstants.STYLE_FONTCOLOR] = '#575757';
style[mxConstants.STYLE_STROKEWIDTH] = 0;
graph.getStylesheet().putCellStyle('title', style);
/* Edge styles */
style = graph.getStylesheet().getDefaultEdgeStyle();
style[mxConstants.STYLE_STROKECOLOR] = 'rgb(81,108,180)';
style[mxConstants.STYLE_ROUNDED] = true;
style = [];
style[mxConstants.STYLE_PERIMETER_SPACING] = -4;
style[mxConstants.STYLE_PERIMETER] = 0;
style[mxConstants.STYLE_STARTARROW] = mxConstants.NONE;
style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC;
style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = 'white';
//style[mxConstants.STYLE_TEXT_OPACITY] = 100;
style[mxConstants.STYLE_FONTCOLOR] = 'rgb(123,159,227)';
graph.getStylesheet().putCellStyle('usesStyle', style);
style = [];
style[mxConstants.STYLE_PERIMETER_SPACING] = -4;
style[mxConstants.STYLE_PERIMETER] = 0;
style[mxConstants.STYLE_STARTARROW] = mxConstants.NONE;
style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC;
style[mxConstants.STYLE_STROKECOLOR] = 'rgb(112,165,255)';
style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = 'white';
style[mxConstants.STYLE_FONTCOLOR] = 'rgb(134,179,252)';
graph.getStylesheet().putCellStyle('usesFrontendStyle', style);
style = [];
style[mxConstants.STYLE_PERIMETER_SPACING] = -4;
style[mxConstants.STYLE_PERIMETER] = 0;
style[mxConstants.STYLE_STARTARROW] = mxConstants.NONE;
style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC;
style[mxConstants.STYLE_EDGE] = mxEdgeStyle.EntityRelation;
//style[mxConstants.STYLE_CURVED] = 1;
style[mxConstants.STYLE_STROKECOLOR] = 'rgb(79,142,255)';
graph.getStylesheet().putCellStyle('uiUsesStyleFocused', style);
style = [];
style[mxConstants.STYLE_PERIMETER_SPACING] = -4;
style[mxConstants.STYLE_PERIMETER] = 0;
style[mxConstants.STYLE_STARTARROW] = mxConstants.NONE;
style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC;
style[mxConstants.STYLE_EDGE] = mxEdgeStyle.EntityRelation
//style[mxConstants.STYLE_CURVED] = 1;
graph.getStylesheet().putCellStyle('usesStyleFocused', style);
style = [];
style[mxConstants.STYLE_PERIMETER_SPACING] = -4;
style[mxConstants.STYLE_PERIMETER] = 0;
style[mxConstants.STYLE_STARTARROW] = mxConstants.NONE;
style[mxConstants.STYLE_ENDARROW] = mxConstants.NONE;
style[mxConstants.STYLE_EDGE] = mxEdgeStyle.EntityRelation
//style[mxConstants.STYLE_CURVED] = 1;
graph.getStylesheet().putCellStyle('usesLineStyleFocused', style);
style = [];
style[mxConstants.STYLE_PERIMETER_SPACING] = -4;
style[mxConstants.STYLE_PERIMETER] = 0;
style[mxConstants.STYLE_STARTARROW] = mxConstants.NONE;
style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC;
style[mxConstants.STYLE_EDGE] = mxEdgeStyle.EntityRelation
style[mxConstants.STYLE_STROKECOLOR] = 'rgb(187,187,187)';
//style[mxConstants.STYLE_CURVED] = 1;
graph.getStylesheet().putCellStyle('usesStyleFocusedOther', style);
style = [];
style[mxConstants.STYLE_PERIMETER_SPACING] = -4;
style[mxConstants.STYLE_PERIMETER] = 0;
style[mxConstants.STYLE_STARTARROW] = mxConstants.NONE;
style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC;
//style[mxConstants.STYLE_EDGE] = mxEdgeStyle.SideToSide;
//style[mxConstants.STYLE_CURVED] = 1;
graph.getStylesheet().putCellStyle('dependsOnStyleFocused', style);
style = [];
style[mxConstants.STYLE_PERIMETER_SPACING] = -4;
style[mxConstants.STYLE_STROKEWIDTH] = 0;
style[mxConstants.STYLE_STROKE_OPACITY] = 0;
style[mxConstants.STYLE_PERIMETER] = 0;
style[mxConstants.STYLE_STARTARROW] = mxConstants.NONE;
style[mxConstants.STYLE_ENDARROW] = mxConstants.NONE;
//style[mxConstants.STYLE_STROKECOLOR] = 'rgb(182,17,17)';
graph.getStylesheet().putCellStyle('treeConnectionStyle', style);
style = [];
style[mxConstants.STYLE_PERIMETER_SPACING] = -4;
style[mxConstants.STYLE_DASHED] = 1;
style[mxConstants.STYLE_DASH_PATTERN] = '4';
style[mxConstants.STYLE_PERIMETER] = 0;
style[mxConstants.STYLE_STARTARROW] = mxConstants.NONE;
style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC;
graph.getStylesheet().putCellStyle('interactionResponseLine', style);
style = [];
style[mxConstants.STYLE_PERIMETER_SPACING] = -6;
style[mxConstants.STYLE_PERIMETER] = 0;
style[mxConstants.STYLE_STARTARROW] = mxConstants.NONE;
style[mxConstants.STYLE_ENDARROW] = mxConstants.NONE;
graph.getStylesheet().putCellStyle('simpleLine', style);
// image string encoded with: https://jgraph.github.io/drawio-tools/tools/base64.html referenced in: https://stackoverflow.com/questions/51039801/how-to-add-a-svg-image-to-mxgraph
let actorImageBase64 = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+Cjxzdmcgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEyMiAyMjkiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgeG1sbnM6c2VyaWY9Imh0dHA6Ly93d3cuc2VyaWYuY29tLyIgc3R5bGU9ImZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxLjU7Ij4KICAgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDEuMTI5MDQsMCwwLDEuMjQwNzUsLTk3LjA3MDIsLTExNy4yMSkiPgogICAgICAgIDxlbGxpcHNlIGN4PSIxNDAuMDI0IiBjeT0iMTIzLjE2OCIgcng9IjI2LjE1MyIgcnk9IjIzLjc5OCIgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6cmdiKDIyNCwxNDksMCk7c3Ryb2tlLXdpZHRoOjcuODdweDsiLz4KICAgIDwvZz4KICAgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDAuOTk3OTkzLDAuMDUwODM1MywtMC4wNjMzMTczLDAuODAxMjU2LC0yNC41NDksLTgxLjY2NjEpIj4KICAgICAgICA8cGF0aCBkPSJNOTYuNjYxLDE3Mi4wODdMMTAzLjI5MywyNzYuNjExIiBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTpyZ2IoMjI0LDE0OSwwKTtzdHJva2Utd2lkdGg6MTAuNDRweDtzdHJva2UtbGluZWNhcDpidXR0OyIvPgogICAgPC9nPgogICAgPGcgdHJhbnNmb3JtPSJtYXRyaXgoLTAuMDYwNjMzOSwwLjk5Nzk5MywtMC45NTU2OTgsLTAuMDYzMzE3MywyODEuNzg4LDYuOTkzMDgpIj4KICAgICAgICA8cGF0aCBkPSJNOTYuNjYxLDE3Mi4wODdMMTAzLjI5MywyNzYuNjExIiBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTpyZ2IoMjI0LDE0OSwwKTtzdHJva2Utd2lkdGg6OS41NnB4OyIvPgogICAgPC9nPgogICAgPGcgdHJhbnNmb3JtPSJtYXRyaXgoLTAuNzgyNTA3LDAuNDk3MjA0LC0wLjQ3MjE4NywtMC43NTIyMDEsMjcyLjQ2MiwzMDEuOTMxKSI+CiAgICAgICAgPHBhdGggZD0iTTk2LjY2MSwxNzIuMDg3TDEwMy4yOTMsMjc2LjYxMSIgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6cmdiKDIyNCwxNDksMCk7c3Ryb2tlLXdpZHRoOjEwLjMxcHg7Ii8+CiAgICA8L2c+CiAgICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgwLjc4MjUwNywwLjQ5NzIwNCwwLjQ3MjE4NywtMC43NTIyMDEsLTE1MC41MzQsMzAxLjkzMSkiPgogICAgICAgIDxwYXRoIGQ9Ik05Ni42NjEsMTcyLjA4N0wxMDMuMjkzLDI3Ni42MTEiIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOnJnYigyMjQsMTQ5LDApO3N0cm9rZS13aWR0aDoxMC4zMXB4OyIvPgogICAgPC9nPgo8L3N2Zz4K"
style = [];
style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_IMAGE;
style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
style[mxConstants.STYLE_IMAGE] = actorImageBase64;
//style[mxConstants.STYLE_IMAGE] = 'img/actor.svg';
style[mxConstants.STYLE_FONTCOLOR] = '#FFFFFF';
graph.getStylesheet().putCellStyle('actorImage', style); // name style
graph.addListener(mxEvent.DOUBLE_CLICK, function(sender, evt){
const cell = evt.getProperty('cell');
if (cell !== null) {
if (cell.edge !== null && cell.edge==1) {
const relationEdge = cell;
const srcCell = relationEdge.source
let trgCell = relationEdge.target
const srcComponentNamespace = srcCell.getAttribute('namespace')
const srcComponentName = srcCell.getAttribute('name')
const isSrcProvide = srcCell.getAttribute('provide')
const srcOperationCell = determineOperationVertex(srcCell)
const srcProvideCell = determineProvideVertex(srcCell)
const srcComponentCell = determineComponentVertex(srcCell)
const trgOperationCell = determineOperationVertex(trgCell)
const trgProvideCell = determineProvideVertex(trgCell)
const trgComponentCell = determineComponentVertex(trgCell)
if (srcComponentCell && trgComponentCell) {
const srcComponentNamespace2 = srcComponentCell.getAttribute('namespace')
const srcComponentName2 = srcComponentCell.getAttribute('name')
const trgComponentNamespace2 = trgComponentCell.getAttribute('namespace')
const trgComponentName2 = trgComponentCell.getAttribute('name')
const isDependsOn = trgComponentCell.getAttribute('isDependsOn')
if (isDependsOn) {
console.log("Navigate to target component and provided feature of target component")
const trgProvideName2 = relationEdge.getAttribute('provideName')
const searchParams = new URLSearchParams(window.location.search);
searchParams.set("consumer", srcComponentNamespace2+"."+srcComponentName2);
searchParams.set("focused", trgComponentNamespace2+"."+trgComponentName2);
if (trgProvideName2) {
searchParams.set("providingFeature", trgProvideName2);
}
window.location.search = searchParams.toString();
return
}
}
const srcIsOperation = srcCell.getAttribute('operation')
if (srcIsOperation) {
const srcProvideCell = srcCell.parent
const isSrcProvide = srcProvideCell.getAttribute('provide')
if (isSrcProvide) {
const componentCell = srcProvideCell.parent
const srcComponentNamespace2 = srcCell.getAttribute('namespace')
const srcComponentName2 = srcCell.getAttribute('name')
}
}
const srcIsOperationLineStart = srcCell.getAttribute('operationLineStart')
/* determine provide-target cell*/
let trgIsOperation = trgCell.getAttribute('operation')
if (trgIsOperation) {
trgCell = trgCell.parent
if (trgCell.getAttribute('subResource')) {
trgCell = trgCell.parent
}
}
let isTrgProvide = trgCell.getAttribute('provide')
let trgComponentNamespace = trgCell.getAttribute('namespace')
let trgComponentName = trgCell.getAttribute('name')
let trgProvideName = undefined
if (isTrgProvide) {
const trgComponentCell = trgCell.parent
trgProvideName = trgCell.getAttribute('name')
trgComponentName = trgComponentCell.getAttribute('name')
trgComponentNamespace = trgComponentCell.getAttribute('namespace')
} else {
trgComponentName = trgCell.getAttribute('name')
trgComponentNamespace = trgCell.getAttribute('namespace')
}
if (srcComponentName && trgComponentName) {
const searchParams = new URLSearchParams(window.location.search);
searchParams.set("consumer", srcComponentNamespace+"."+srcComponentName);
searchParams.set("focused", trgComponentNamespace+"."+trgComponentName);
if (trgProvideName) {
searchParams.set("providingFeature", trgProvideName);
}
window.location.search = searchParams.toString();
}
}
}
});
/* Layout algorithm */
let layout = null;
if (diagramCtx.focused) {
layout = new mxCompactTreeLayout(graph);
/* right to left */
layout.attachParent = function(node, height)
{
const x = this.nodeDistance + this.levelDistance;
const y2 = (height - node.width) / 2 - this.nodeDistance;
const y1 = y2 + node.width + 2 * this.nodeDistance - height;
node.child.offsetX = -(x + node.height);
node.child.offsetY = y1;
node.contour.upperHead = this.createLine(node.height, 0,
this.createLine(x, y1, node.contour.upperHead));
node.contour.lowerHead = this.createLine(node.height, 0,
this.createLine(x, y2, node.contour.lowerHead));
};
layout.useBoundingBox = false;
layout.edgeRouting = false;
layout.levelDistance = 180;
layout.nodeDistance = 20;
} else {
layout = new mxFastOrganicLayout(graph);
layout.forceConstant = 220;
layout.maintainParentLocation = false;
layout.edgeRouting = true;
layout.isVertexMovable = function( cell ) {
const fixed = cell.getAttribute('fixed')
if (fixed) {
return false
}
return true
}
}
const groupLayout = new mxFastOrganicLayout(graph);
groupLayout.resizeParent = true
groupLayout.forceConstant = 120;
// layout supporting parallel edges
const parallelEdgeLayout = new mxParallelEdgeLayout(graph);
parallelEdgeLayout.spacing = 20;
/* override layout function to consider offset of groups */
parallelEdgeLayout.layout = function(parallels)
{
var edge = parallels[0];
var view = this.graph.getView();
var model = this.graph.getModel();
const srcCell = view.getVisibleTerminal(edge, true)
const trgCell = view.getVisibleTerminal(edge, false)
const srcParentCell = srcCell.getParent()
const trgParentCell= trgCell.getParent()
const isSrcGroup = srcParentCell.getAttribute('isGroup')
const isTrgGroup = trgParentCell.getAttribute('isGroup')
var src = model.getGeometry(srcCell);
var trg = model.getGeometry(trgCell);
// Routes multiple loops
if (src == trg)
{
var x0 = src.x + src.width + this.spacing;
var y0 = src.y + src.height / 2;
for (var i = 0; i < parallels.length; i++)
{
this.route(parallels[i], x0, y0);
x0 += this.spacing;
}
}
else if (src != null && trg != null)
{
/* consider offset of group parent */
let srcOffsetX = src.x
let srcOffsetY = src.y
if (isSrcGroup && !isTrgGroup) {
const srcParent = model.getGeometry(srcParentCell);
srcOffsetX += srcParent.x
srcOffsetY += srcParent.y
}
let trgOffsetX = trg.x
let trgOffsetY = trg.y
if (isTrgGroup && !isSrcGroup) {
const trgParent = model.getGeometry(trgParentCell);
trgOffsetX += trgParent.x
trgOffsetY += trgParent.y
}
/* Routes parallel edges */
var scx = srcOffsetX + src.width / 2;
var scy = srcOffsetY + src.height / 2;
var tcx = trgOffsetX + trg.width / 2;
var tcy = trgOffsetY + trg.height / 2;
var dx = tcx - scx;
var dy = tcy - scy;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0)
{
var x0 = scx + dx / 2;
var y0 = scy + dy / 2;
var nx = dy * this.spacing / len;
var ny = dx * this.spacing / len;
x0 += nx * (parallels.length - 1) / 2;
y0 -= ny * (parallels.length - 1) / 2;
for (var i = 0; i < parallels.length; i++)
{
this.route(parallels[i], x0, y0);
x0 -= nx;
y0 += ny;
}
}
}
};
const layoutMgr = new mxLayoutManager(graph);
layoutMgr.getLayout = function(cell) {
return parallelEdgeLayout;
};
let parent = graph.getDefaultParent();
graph.border = 30;
graph.isWrapping = function(cell)
{
return true
};
const data = createDiagramData();
diagramCtx.applicationNamespace = data.namespace
if (titleLink) {
titleLink.onclick = function() {
const fileName = data.title.toLowerCase() + '-components.svg'
downloadSVG(graph, fileName);
};
}
const processesLink = document.getElementById('processesLink')
if (processesLink) {
processesLink.onclick = function() {
if (window.location.href.includes("diagram/processes/")) {
window.location.href = data.applicationName.toLowerCase() + "_processes.html";
} else {
window.location.href = "processes/" + data.applicationName.toLowerCase() + "_processes.html";
}
};
}
const componentsLink = document.getElementById('componentsLink')
if (componentsLink) {
componentsLink.onclick = function() {
if (window.location.href.includes("diagram/processes/")) {
window.location.href = "../" + data.applicationName.toLowerCase() + ".html";
} else {
window.location.href = data.applicationName.toLowerCase() + ".html";
}
};
}
const layoutLink = document.getElementById('layoutLink')
if (layoutLink) {
layoutLink.onclick = function() {
downloadLayout(graph, parent, diagramName + ".json")
};
}
// Load cells and layouts the graph
graph.getModel().beginUpdate();
try {
// draw the diagram
draw(graph, parent, layout, groupLayout, data, diagramCtx);
} finally {
// Updates the display
graph.getModel().endUpdate();
}
if (mxClient.IS_QUIRKS) {
document.body.style.overflow = 'hidden';
new mxDivResizer(container);
}
}
}