Event Sequence Diagram in JavaScript

In this article we will look at the steps you need to make to create an interactive online application for creating sequence of events diagrams. We will use MindFusion Diagramming library for JavaScript.

You can run the application from here.

And here is how the final version looks:

Image for post
Image for post
Sequence of events diagram built with MindFusion Diagramming for JavaScript library

I. General Setup

We start by creating an empty directory for the project and adding there the two JavaScript files from the Js Diagram library that we will need: and Then we create an empty web page and in it, right before the closing BODY tag we add references to the two libraries cited above:

<script src=”Scripts/MindFusion.Common.js” type=”text/javascript”></script>
<script src=”Scripts/MindFusion.Diagramming.js” type=”text/javascript”></script>
<script src=”EventSequence.js” type=”text/javascript”></script>

We also add a reference to another JavaScript file, which is called EventSequence.js This is a code-behind file that we will use for storing the source code of our application.

In our HTML page we add two HTML Canvas instances — one for the diagram and the other for the NodeListView control. They both need a Canvas to render itself onto. Note that we need to provide an id for those Canvas instances because we will access them from the code-behind file.

<div style=”width: 200px; height: 100%; overflow-y: auto; overflow-x: hidden; position: absolute;
top: 5px; left: 0px; right: 0px; bottom: 0px;”>
<canvas id=”nodeList” width=”200">
</canvas>
</div>
<div style=”overflow: visible; height:100%; margin: 1px; padding: 0px;”>
<canvas id=”diagram” width=”2100" height=”2500">
This page requires a browser that supports HTML 5 Canvas element.
</canvas>
</div>

We also create a color picker — we will use this standard HTML input control to change the color of the diagram nodes:

<input type=”color” title=”Choose node background” value=”#88b663" id=”colorBkgr”>

II. Creating the Diagram

We create the diagram by using the Canvas element we initialized in the web page:

var diagramEl = document.getElementById(‘diagram’);
// create a Diagram component that wraps the “diagram” canvas
diagram = AbstractionLayer.createControl(Diagram, null, null, null, diagramEl);

We create the instance of the diagram control with the AbstractionLayer class. Before that we have taken a reference to the DOM element of the Canvas. Then we set come properties that will make the control behave the way we want it:

diagram.setAllowInplaceEdit(true);
diagram.setRouteLinks(true);
diagram.setShowGrid(true);
diagram.setRoundedLinks(true);
diagram.setDefaultShape(‘Ellipse’);
diagram.setShadowsStyle(MindFusion.Diagramming.ShadowsStyle.None);
diagram.setBounds(new Rect(0, 0, 2000,2000));

We allow in-place edit, which means uses can edit the diagram items interactively. We show the background grid of the diagram work are and set the default shape of newly created diagram items to ellipse. We disable shadows for diagram items and make the links rounded.

After we’ve finished that we use the Style class to create two style instances — one for DiagramLink and the other for ShapeNode. The Style class exposes a set of properties that specify the appearance of diagram items. Styles are used in Theme instances, where you can gather a collection of styles or the different diagram items. The theme is then assigned to the diagram.

//create a theme for initial styling of diagram nodes and links
var theme = new Theme();

var shapeNodeStyle = new Style();
shapeNodeStyle.setBrush({ type: ‘SolidBrush’, color: insertAlpha(backgroundColor, 0.5) });
shapeNodeStyle.setStroke(backgroundColor);
shapeNodeStyle.setStrokeThickness(10);
shapeNodeStyle.setTextColor(“#202020”);
shapeNodeStyle.setFontName(“Times New Roman”);
shapeNodeStyle.setFontStyle(MindFusion.Drawing.FontStyle.Bold);
shapeNodeStyle.setFontSize(4);
shapeNodeStyle.setBackBrush(“#e0e9e9”);
theme.styles[“std:ShapeNode”] = shapeNodeStyle;
diagram.setTheme(theme);

III. The NodeListView

The NodeListView control also needs an instance of the DOM Element that represents its Canvas:

var nodeListEl = document.getElementById(‘nodeList’);
// create an NodeListView component that wraps the “nodeList” canvas
nodeList = AbstractionLayer.createControl(NodeListView, null, null, null, nodeListEl);

We create it in a way similar to the Diagram control — with the help o the AbstractionLayer class.

After we’ve initialized the control, we add some settings to it:

nodeList.setIconSize(new Size(30,30));
nodeList.setDefaultNodeSize(new Size(10,10));

The NodeListView shows instances of diagram shapes. We need to add those shapes to the control. We will add three shapes — a transparent node that will be used for rendering text; a circle (e.g. an ellipse) and an arrow shape. Here is how we create the transparent text node:

//transparent text node
var node = new ShapeNode();
node.setTransparent(true);
node.setShape(“Rectangle”);
node.setText(“Text”);
node.setTextColor(“#7F7F7F”);
node.setFont(new Font(“Verdana”, 12, true, false));
nodeList.addNode(node, “Text”);

We create the other two ShapeNode -s in a similar way, we only change their type to “Ellipse” and “Arrow3”. We also handle the nodeSelected event of the NodeListView control — we want the selected shape to be set as default shape for the diagram. This means that nodes created by drawing with the mouse will have the selected shape, not just nodes created by dragging it.

nodeList.addEventListener(Events.nodeSelected, onShapeSelected);

And here is the event handling method, which takes the selected shape and assigns it as a default diagram shape:

//when a shape is selected, it is set as the default for the diagram
function onShapeSelected(sender, e)
{
var selectedNode = e.getNode();
if (selectedNode)
diagram.setDefaultShape(selectedNode.getShape());
}

IV. Creating the Initial Diagram

We want the diagram items that you see at the beginning to follow a specific pattern. When you look at it you can probably spot that it looks like a portion o a sine curve. So, we’ve used the equation of the cosine function to create a portion of the graph between 120 and 420 degrees, which we convert to radians and calculate the location of the diagram nodes. The location points are along the cosine curve:

var counter = 0;

//the nodes follow the pattern of a cosine curve
for(i=120; i<=420; i+=32){

x = i*0.9–80;
y = 65 — Math.cos(i*(Math.PI/180)) * 50;

var node = diagram.getFactory().createShapeNode(new Rect(x, y, 17, 17));
node.setStroke(colors[counter]);
node.setStrokeThickness(10.0);
node.setBrush({ type: ‘SolidBrush’, color: insertAlpha(colors[counter], 0.5)});
………………
}

As you see we get the coordinates of the node as computed by the cosine function. We also use the Factory class of the diagram to create a new ShapeNode. This ShapeNode has a thick border — its stroke is 10 points. It also has a semi-transparent internal part. We get the correct color by using a helper method called insertAlpha:

//inserts the given alpha value to the provided color
function insertAlpha(color, alpha)
{
//the color is in hex format — convert it to rgb
var rgbValue = hexToRgb(color);

if(rgbValue)
{
var result = “rgba(“ + rgbValue.r + “,” + rgbValue.g + “,” + rgbValue.b + “,” + alpha + “)”;
return result;
}
}

The insertAlpha method uses another method to convert a hexadecimal color to its RGB representation:

//return the RGB value of a color in hexadecimal format
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}

After getting the RGB value of the provided color, we construct a new color, this time from RGBA and we add as alpha the value that was provided as a parameter to the function. This way we can convert any hex color to the same RGBA color with transparency of our choice.

We then create transparent nodes that hold the text to be associated with the diagram nodes. We use again Factory to link each circle node with its text:

//connect the node and the text represented by another node
var link = diagram.getFactory().createDiagramLink(textNode, node);
link.setHeadShape(null);
link.setHeadBrush({ type: ‘SolidBrush’, color: ‘#7F7F7F’ });

Finally we cycle through all nodes and connect with a thick DiagramLink each subsequent ellipse nodes.

//connect all nodes with thick links
for(var i = 0; i < nodes.length — 2; i+=2)
{
var link = diagram.getFactory().createDiagramLink(nodes[i], nodes[i+2]);
link.setStroke(link.getOrigin().getStroke());
link.setHeadBrush({ type: ‘SolidBrush’, color: link.getOrigin().getStroke()});
link.setStrokeThickness(8);
}

We use the getOrigin method of DiagramLink to get the node from which this link comes. Then we get its color and use it to fill the link.

V. Events

We will handle two events: nodeCreated and linkCreated.

//wire the node created and link created events
diagram.addEventListener(Events.linkCreated, onLinkCreated);
diagram.addEventListener(Events.nodeCreated, onNodeCreated);

In the nodeCreated event we will check what type of node is created. If it is an ellipse, we will take the current background color and draw the node with a thick stroke of that color. The inner part will be semi-transparent with the same color:

//handles the nodeCreated event
function onNodeCreated(sender, args)
{
var node = args.getNode();
node.setStroke(backgroundColor);

//if the created node is an ellipse, its brush must be semi-transparent and the stroke — thick.
if(node.getShape().getId() === ‘Ellipse’)
{
node.setBrush({ type: ‘SolidBrush’, color: insertAlpha(backgroundColor, 0.5)});
node.setStrokeThickness(10.0);
}
else
{ //if it is a text node — the color of text should be light gray
node.setBrush({ type: ‘SolidBrush’, color: backgroundColor });
node.setTextColor(‘#7F7F7F’);
}
}

If the node is a text node, we will make its text be drawn with gray brush using the setTextColor method. We also set the brush of nodes that are not ellipse to a solid color. That doesn’t matter when the node is transparent, but it is exactly what is needed if the user is drawing an arrow node.

In the event handler of the linkCreated event we use the getOrigin() method of DiagramLink to get the node, from which the link starts. Then, if it is an ellipse, we fill the link with the color of the ellipse:

//handle the linkCreated event
function onLinkCreated(sender, args)
{
var link = args.getLink();
//if the link comes from an ellipse, its color must be the color of the node
if(link.getOrigin().getShape().getId() === ‘Ellipse’)
{
link.setStroke(link.getOrigin().getStroke());
link.setHeadBrush({ type: ‘SolidBrush’, color: link.getOrigin().getStroke()});
link.setStrokeThickness(8);//if it comes from a text node, it must not have arrow heads
}else if(link.getOrigin().getShape().getId() === ‘Rectangle’)
{
link.setHeadShape(null);
link.setBaseShape(null);
link.setHeadBrush({ type: ‘SolidBrush’, color: ‘#7F7F7F’ });
}

link.setTextAlignment(MindFusion.Diagramming.Alignment.Near);
}

If the link comes from a rectangle that means this is a text node. In this case we draw a thin, light gray line that connects the text node with its destination.

And that is the end of this tutorial. With the steps above we have built a JavaScript application that allows the users to create event-sequence diagrams. We have used MindFusion JavaScript Diagramming library to achieve this.

You can download the source code of the application, together with all libraries from this link.

This native JavaScript library provides developers with the ability to create and customize any type of diagram, decision tree, flowchart, class hierarchy, graph, genealogy tree, BPMN diagrams and much more. The control offers rich event set, numerous customization options, animations, graph operations, styling and themes. You have more than 100 predefined nodes, table nodes and more than 15 automatic layout algorithms. Learn more about Diagramming for JavaScript at https://mindfusion.eu/javascript-diagram.html.

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store