All Downloads are FREE. Search and download functionalities are using the official Maven repository.

diagram.lib.components.js Maven / Gradle / Ivy

There is a newer version: 1.3.46
Show newest version
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); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy