Simple draw.io embedding walk-through

draw.io 01 May 2020

Share:

You can embed draw.io as an application within another app, where you store the diagram data in the host app. It takes around 15 minutes to get a basic example running.

Taking an example Stack Overflow page as a template, we’ve created our own, slightly altered version of the question. Double click on the diagram to open the online draw.io editor in a new window, make some diagram edits then click Save to save the changed diagram back to the page.

You can repeat the process to continue editing the diagram at a later time. Let’s take a look at both the flow of this process and the code in the page that allows this to happen. If you view the page source you’ll see on line 1695:

<img class="drawio" style="cursor:default;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhE....

The image itself is placed in the page as a base64 encoded dataURI. We’re doing this because there’s no actual back-end in this example (refresh and you will lose your changes), we’ve nowhere to write an image to persist it.

The diagram data itself is embedded within the compressed text section of the PNG. When you double click on the image we open a new window and load draw.io in embedded mode in that window:

<script>
  // Edits an image with drawio class on double click
  document.addEventListener('dblclick', function(evt)
  {
    var url = 'https://embed.diagrams.net/?embed=1&ui=atlas&spin=1&modified=unsavedChanges&proto=json';
    var source = evt.srcElement || evt.target;

    if (source.nodeName == 'IMG' && source.className == 'drawio')
    {
      if (source.drawIoWindow == null || source.drawIoWindow.closed)
      {
        // Implements protocol for loading and exporting with embedded XML
        var receive = function(evt)
        {
          if (evt.data.length > 0 && evt.source == source.drawIoWindow)
          {
            var msg = JSON.parse(evt.data);

            // Received if the editor is ready
            if (msg.event == 'init')
            {
              // Sends the data URI with embedded XML to editor
              source.drawIoWindow.postMessage(JSON.stringify
                {action: 'load', xmlpng: source.getAttribute('src')}), '*');
            }
            // Received if the user clicks save
            else if (msg.event == 'save')
            {
              // Sends a request to export the diagram as XML with embedded PNG
              source.drawIoWindow.postMessage(JSON.stringify(
                {action: 'export', format: 'xmlpng', spinKey: 'saving'}), '*');
            }
            // Received if the export request was processed
            else if (msg.event == 'export')
            {
              // Updates the data URI of the image
              source.setAttribute('src', msg.data);
            }

            // Received if the user clicks exit or after export
            if (msg.event == 'exit' || msg.event == 'export')
            {
              // Closes the editor
              window.removeEventListener('message', receive);
              source.drawIoWindow.close();
              source.drawIoWindow = null;
            }
          }
        };

        // Opens the editor
        window.addEventListener('message', receive);
        source.drawIoWindow = window.open(url);
      }
      else
      {
        // Shows existing editor window
        source.drawIoWindow.focus();
      }
    }
  });
</script>
  • embed=1 URL parameter tells draw.io to operate in embedded mode.
  • protocol=json means we are using the JSON protocol for message passing. Always use this mode for now.
  • postMessage is then used to communicate the diagram data.

Note that you could open the diagram in an iFrame instead of a new window, and this might make the flow clearer for the user.

In this case, create an iFrame at the start of the function:

var iframe = document.createElement('iframe');
iframe.setAttribute('frameborder', '0');

var close = function()
{
    window.removeEventListener('message', receive);
    document.body.removeChild(iframe);
};

Append the iFrame to the window at the end of the function, after starting to listen for messages:

window.addEventListener('message', receive);
iframe.setAttribute('src', editor);
document.body.appendChild(iframe);

You might also want to make the iFrame completely cover the initial page via CSS:

iframe {
        border:0;
        position:fixed;
        top:0;
        left:0;
        right:0;
        bottom:0;
        width:100%;
        height:100%
    }

When the draw.io editor is loaded in the new window/iFrame it’s just the static application, there’s no data. We use the message passing protocol to load in the data to draw.io, after receiving the “init” message from draw.io to tell us that it is ready.

Sequence diagram example

Edit the diagram in the editor and click Save when finished. The additional messages sent after saving is to provide granularity for requesting another format, in this case by asking for the PNG+XML format via an Export. In our case we also exit the editor when the export is complete, but you could leave it open and wait for the user to explicitly Exit the editor before closing it.

The XML embedded PNG as a base64 dataURI is saved back on top of the src attribute of the image and this results in the page being updated with the new image.

Lastly, it’s important to distinguish between the static draw.io application and the data flow in embedded mode. Our online editor is loaded as a static application in the draw.io window, but the diagram data is passed entirely client-side between windows, it’s never sent back to, or sourced from, the draw.io application server. This means you control and store your data, and you only use the diagramming functionality provided by draw.io to edit and change that data.

Follow us on GitHub, Twitter, Facebook.

Share: