The use of jsPlumb Process Flow Diagram Designer

Recommended for you: Get network issues from WhatsUp Gold. Not end users.

JsPlumb is a more powerful graphics component, it provides a kind of method, is mainly used for connecting elements on a webpage. In modern browsers, it use SVG or Canvas, and for IE8 (including IE8) antique browser, use the VML Technology.

The project home page:

GitHub: https://github.com/sporritt/jsPlumb

As a plug-in, mainly in support of jQuery/MooTools/YUI3 three JS, the latest version of 1.4.1. The jQuery plugin needs to use the jQuery, jQuery UI, suggested the use of the latest version of the library to avoid some of the bug.

In this paper, using the jQuery 1.9.0, jQuery UI 1.9.2, jsPlumb 1.4.1 to draw a flow chart.

Provision of resources

Download the jsPlumb, use the following documents:

And build/demo/js/demo-helper-jquery.js, mainly used for switching the drawing mode, adjusted for the following code:

jsPlumb.bind("ready", function() {
  // chrome fix.
  document.onselectstart = function() { return false; };
  // render mode
  var resetRenderMode = function(desiredMode) {
    var newMode = jsPlumb.setRenderMode(desiredMode);
    $(".rmode").removeClass("selected");
    $(".rmode[mode='" + newMode + "']").addClass("selected");
    $(".rmode[mode='canvas']").attr("disabled", !jsPlumb.isCanvasAvailable());
    $(".rmode[mode='svg']").attr("disabled", !jsPlumb.isSVGAvailable());
    $(".rmode[mode='vml']").attr("disabled", !jsPlumb.isVMLAvailable());
    nodeFlow.init();
  };
  $(".rmode").bind("click", function() {
    var desiredMode = $(this).attr("mode");
    if (jsPlumbDemo.reset) jsPlumbDemo.reset();
    jsPlumb.reset();
    resetRenderMode(desiredMode);
  });
  resetRenderMode(jsPlumb.SVG);
});

To prepare CSS style (from the flowchartDemo.css adjustment.):

.node { border: 1px solid #346789; box-shadow: 2px 2px 19px #aaa; -o-box-shadow: 2px 2px 19px #aaa; -webkit-box-shadow: 2px 2px 19px #aaa; -moz-box-shadow: 2px 2px 19px #aaa; -moz-border-radius: 0.5em; border-radius: 0.5em; opacity: 0.8; filter: alpha(opacity=80); width: 7em; height: 5em; line-height: 5em; text-align: center; z-index: 20; position: absolute; background-color: #eeeeef; color: black; font-family: helvetica; padding: 0.5em; font-size: 1em; }
  .node:hover { box-shadow: 2px 2px 19px #444; -o-box-shadow: 2px 2px 19px #444; -webkit-box-shadow: 2px 2px 19px #444; -moz-box-shadow: 2px 2px 19px #444; opacity: 0.8; filter: alpha(opacity=80); }

._jsPlumb_connector { z-index: 4; }
._jsPlumb_endpoint { z-index: 21; cursor: pointer; }
._jsPlumb_dragging { z-index: 4000; }

.dragHover { border: 1px dotted red; }

.aLabel { background-color: white; padding: 0.4em; font: 12px sans-serif; color: #444; z-index: 21; border: 1px dotted gray; opacity: 0.8; filter: alpha(opacity=80); }

.ep { position: absolute; right: 5px; top: 5px; width: 1em; height: 1em; background-color: #994466; cursor: pointer; }

Finally the concept of resources are as follows:

   <script src='js/jquery-1.9.0.min.js'></script>
   <script src='js/jquery-ui-1.9.2.min.js'>
  <link href="css/demo.css" rel="stylesheet" />
  <script src="js/jquery.jsPlumb-1.4.1-all-min.js"></script>
  <script src="js/jquery.ui.touch-punch.min.js"></script>
  <script src="js/demo.init.js"></script>
  <script src="js/demo-helper-jquery.js"></script>

The main implementation

According to the example of Flowchart and State Machine, to achieve the following:

; (function() {
  window.nodeFlow = {
    init: function() {
      // The set point, the default style line
      jsPlumb.importDefaults({
        DragOptions: { cursor: 'pointer', zIndex: 2000 },
        Endpoint: ["Dot", { radius: 1 }],
        HoverPaintStyle: { strokeStyle: "#42a62c", lineWidth: 2 },
        ConnectionOverlays: [
          ["Arrow", { location: -7, id: "arrow", length: 14, foldback: 0.8 }],
          ["Label", { location: 0.1, id: "label" }]
        ]
      });
      // Connection events
      jsPlumb.bind("jsPlumbConnection", function(conn, originalEvent) {
        if (conn.connection.sourceId == conn.connection.targetId) {
          jsPlumb.detach(conn);
          alert("Unable to connect to their!");
        }
        $.each(jsPlumb.getEndpoints(conn.source), function(i, el) {
          if (conn.connection != el.connections[0] &&
            (el.connections[0].targetId == conn.targetId || (el.connections[0].sourceId == conn.targetId && el.connections[0].targetId == conn.sourceId))) {
            jsPlumb.detach(conn);
            alert("Don't repeat connection!");
            return false;
          }
        });

        nodeFlow.onConnectionChange && nodeFlow.onConnectionChange(conn);
        conn.connection.bind("editCompleted", function(o) {
          if (typeof console != "undefined")
            console.log("connection edited. path is now ", o.path);
        });
      });
      // Cancel the connection events
      jsPlumb.bind("jsPlumbConnectionDetached", function(conn) {
        nodeFlow.onConnectionChange && nodeFlow.onConnectionChange(conn);
      });
      // Double click Cancel.
      jsPlumb.bind("dblclick", function(conn, originalEvent) {
        jsPlumb.detach(conn);
      });
      // Connecting element
      // In the case of the.Node is the source and target
      var nodeList = $(".node");
      nodeList.each(function(i, e) {
        // Set the source element connection
        jsPlumb.makeSource($(e), {
          filter: ".ep", // The.Ep element is used to drag.
          anchor: "Continuous",
          connector: ["Flowchart", { curviness: 20 }], // Connection for flow chart
          connectorStyle: { strokeStyle: "#014ae1", lineWidth: 2 },
          maxConnections: -1 // The maximum number of connections is not restricted
        });
      });
      // Set the connection object
      jsPlumb.makeTarget(nodeList, {
        dropOptions: { hoverClass: "dragHover" },
        anchor: "Continuous"
      });
      // Initialize all connection element for the drag
      jsPlumb.draggable(nodeList);
    }
  };
})();

 

Save and load state

Create a HTML structure as test:

<asp:HiddenField runat="server" ID="connections" /><!-- save connection>
<asp:HiddenField runat="server" ID="locations" /><!-- save element position -->
<div class="nodeWrapper" style="height:100%;">
  <div class="node" id='node1' data-id="1">
    <div class="ep"></div>
    <strong>Node 1</strong>
  </div>
  <div class="node" id='node1' data-id="1">
    <div class="ep"></div>
    <strong>Node 1</strong>
  </div>
  <div class="node" id='node2' data-id="2">
    <div class="ep"></div>
    <strong>Node 2</strong>
  </div>
  <div class="node" id='node3' data-id="3">
    <div class="ep"></div>
    <strong>Node 3</strong>
  </div>
</div>

In the connection status changes, the form is submitted to save the connection data:

      // Change the connection node position all, connected to JSON format into hiding domain
      nodeFlow.onConnectionChange = function() {
        var connections = [], locations = [], conns = jsPlumb.getAllConnections();
        $.each(conns, function(scopeName, scopeConnections) {
          $.each(scopeConnections, function(i, el) {
            locations.push($.extend(el.source.offset(), { nodeId: el.source.data("id") }));
            locations.push($.extend(el.target.offset(), { nodeId: el.target.data("id") }));
            connections.push({ source: el.source.data("id"), target: el.target.data("id") });
          });
        });
        $("input[id$=connections]").val(JSON.stringify(connections));
        $("input[id$=locations]").val(JSON.stringify(locations));
      };
      // Update data when the form is submitted.
      $(":submit").click(nodeFlow.onConnectionChange);

Through the above code, namely when the form is submitted to the flow chart of the state is saved to the database.

Load data

Adjust the HTML code as follows:

<div class="nodeWrapper" style="height:100%;">
  <asp:Repeater runat="server" ID="nodeList">
    <ItemTemplate>
      <div class="node" id='node<%#Eval("nodeId") %>' data-id="<%#Eval("nodeId") %>" style="<%#GetLocation((int)Eval("nodeId"))%>">
        <div class="ep"></div>
        <strong><%#Eval("nodeName") %></strong>
      </div>
    </ItemTemplate>
  </asp:Repeater>
</div>

Gets the node position, from the database, data connection:

        /// <summary>
        /// The node information
        /// </summary>
        public class NodeItem
        {
            public int NodeId { get; set; }
            public string NodeName { get; set; }
        }
        /// <summary>
        /// Node location information
        /// </summary>
        public class NodeLocation
        {
            public int NodeId { get; set; }
            public double Left { get; set; }
            public double Top { get; set; }
        }
        /// <summary>
        /// Node connection information
        /// </summary>
        public class NodeConnection
        {
            public int Source { get; set; }
            public int Target { get; set; }
        }

        List<NodeLocation> locationData;

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                var nodeData = new List<NodeItem>
                {
                    new NodeItem{NodeId=1, NodeName="Node 1"},
                    new NodeItem{NodeId=2, NodeName="Node 2"},
                    new NodeItem{NodeId=3, NodeName="Node 3"}
                };
                nodeList.DataSource = nodeData;
                nodeList.DataBind();
                
                // Gets the location and connection from the database
                locationData = JsonConvert.DeserializeObject<List<NodeLocation>>(locationString);
                var connectionData = JsonConvert.DeserializeObject<List<NodeConnection>>(connectionString);

                // Connect all the nodes
                var builder = new StringBuilder();
                builder.Append("jsPlumb.bind(\"ready\", function() {");
                connectionData.ForEach(c =>
                {
                    builder.AppendFormat("jsPlumb.connect({{source: 'node{0}', target: 'node{1}'}});", c.Source.ToString(), c.Target.ToString());
                });
                builder.Append("});");
            }
        }

        /// <summary>
        /// Gets the position
        /// </summary>
        protected string GetLocation(int nodeId)
        {
            var ll = locationData.FirstOrDefault(l => l.NodeId == nodeId);
            if (ll != null)
                return "left:" + ll.Left.ToString() + "px;top:" + ll.Top.ToString() + "px;";
            return string.Empty;
        }

Among them, each loaded, all need to get all the data connection, and connect all the nodes through the script.

 

Ending

The above function is only a small amount of API to realize jsPlumb, are relatively simple. The official documents and the API documents more expansion.

Before using jsPlumb have also seen some other JS components:

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Posted by Bennett at November 27, 2013 - 10:10 AM