airavata-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sma...@apache.org
Subject [airavata-sandbox] 01/19: Initial workflow composer implementation
Date Wed, 06 Dec 2017 03:13:45 GMT
This is an automated email from the ASF dual-hosted git repository.

smarru pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-sandbox.git

commit 7f9341b1e0ea3434f5d27e046289b27d2653ed8a
Author: dimuthu.upeksha2@gmail.com <Dimu@1234>
AuthorDate: Fri Nov 3 23:35:01 2017 +0530

    Initial workflow composer implementation
---
 airavata-kubernetes/workflow-composer/index.html   |   374 +
 airavata-kubernetes/workflow-composer/mxClient.js  | 88604 +++++++++++++++++++
 .../workflow-composer/mxClient.min.js              |  1797 +
 .../workflow-composer/src/css/common.css           |   160 +
 .../workflow-composer/src/css/explorer.css         |    18 +
 .../workflow-composer/src/icons/copy.png           |   Bin 0 -> 3575 bytes
 .../workflow-composer/src/icons/http.png           |   Bin 0 -> 3218 bytes
 .../workflow-composer/src/icons/parallel.png       |   Bin 0 -> 3150 bytes
 .../workflow-composer/src/icons/s3.png             |   Bin 0 -> 3988 bytes
 .../workflow-composer/src/icons/ssh.png            |   Bin 0 -> 3815 bytes
 .../workflow-composer/src/icons/start.png          |   Bin 0 -> 3303 bytes
 .../workflow-composer/src/icons/stop.png           |   Bin 0 -> 3525 bytes
 .../workflow-composer/src/images/button.gif        |   Bin 0 -> 137 bytes
 .../workflow-composer/src/images/close.gif         |   Bin 0 -> 70 bytes
 .../workflow-composer/src/images/collapsed.gif     |   Bin 0 -> 877 bytes
 .../workflow-composer/src/images/error.gif         |   Bin 0 -> 907 bytes
 .../workflow-composer/src/images/expanded.gif      |   Bin 0 -> 878 bytes
 .../workflow-composer/src/images/maximize.gif      |   Bin 0 -> 843 bytes
 .../workflow-composer/src/images/minimize.gif      |   Bin 0 -> 64 bytes
 .../workflow-composer/src/images/normalize.gif     |   Bin 0 -> 845 bytes
 .../workflow-composer/src/images/point.gif         |   Bin 0 -> 55 bytes
 .../workflow-composer/src/images/resize.gif        |   Bin 0 -> 74 bytes
 .../workflow-composer/src/images/separator.gif     |   Bin 0 -> 146 bytes
 .../workflow-composer/src/images/submenu.gif       |   Bin 0 -> 56 bytes
 .../workflow-composer/src/images/transparent.gif   |   Bin 0 -> 90 bytes
 .../workflow-composer/src/images/warning.gif       |   Bin 0 -> 276 bytes
 .../workflow-composer/src/images/warning.png       |   Bin 0 -> 425 bytes
 .../workflow-composer/src/images/window-title.gif  |   Bin 0 -> 275 bytes
 .../workflow-composer/src/images/window.gif        |   Bin 0 -> 75 bytes
 .../workflow-composer/src/js/components.js         |    53 +
 .../src/js/editor/mxDefaultKeyHandler.js           |   126 +
 .../src/js/editor/mxDefaultPopupMenu.js            |   306 +
 .../src/js/editor/mxDefaultToolbar.js              |   564 +
 .../workflow-composer/src/js/editor/mxEditor.js    |  3114 +
 .../src/js/handler/mxCellHighlight.js              |   314 +
 .../src/js/handler/mxCellMarker.js                 |   430 +
 .../src/js/handler/mxCellTracker.js                |   145 +
 .../src/js/handler/mxConnectionHandler.js          |  2204 +
 .../src/js/handler/mxConstraintHandler.js          |   520 +
 .../src/js/handler/mxEdgeHandler.js                |  2409 +
 .../src/js/handler/mxEdgeSegmentHandler.js         |   401 +
 .../src/js/handler/mxElbowEdgeHandler.js           |   229 +
 .../src/js/handler/mxGraphHandler.js               |  1074 +
 .../workflow-composer/src/js/handler/mxHandle.js   |   353 +
 .../src/js/handler/mxKeyHandler.js                 |   428 +
 .../src/js/handler/mxPanningHandler.js             |   462 +
 .../src/js/handler/mxPopupMenuHandler.js           |   218 +
 .../src/js/handler/mxRubberband.js                 |   401 +
 .../src/js/handler/mxSelectionCellsHandler.js      |   287 +
 .../src/js/handler/mxTooltipHandler.js             |   337 +
 .../src/js/handler/mxVertexHandler.js              |  1950 +
 .../workflow-composer/src/js/index.txt             |   316 +
 .../workflow-composer/src/js/io/mxCellCodec.js     |   189 +
 .../src/js/io/mxChildChangeCodec.js                |   149 +
 .../workflow-composer/src/js/io/mxCodec.js         |   596 +
 .../workflow-composer/src/js/io/mxCodecRegistry.js |   137 +
 .../src/js/io/mxDefaultKeyHandlerCodec.js          |    88 +
 .../src/js/io/mxDefaultPopupMenuCodec.js           |    54 +
 .../src/js/io/mxDefaultToolbarCodec.js             |   312 +
 .../workflow-composer/src/js/io/mxEditorCodec.js   |   245 +
 .../src/js/io/mxGenericChangeCodec.js              |    64 +
 .../workflow-composer/src/js/io/mxGraphCodec.js    |    28 +
 .../src/js/io/mxGraphViewCodec.js                  |   197 +
 .../workflow-composer/src/js/io/mxModelCodec.js    |    80 +
 .../workflow-composer/src/js/io/mxObjectCodec.js   |  1077 +
 .../src/js/io/mxRootChangeCodec.js                 |    83 +
 .../src/js/io/mxStylesheetCodec.js                 |   217 +
 .../src/js/io/mxTerminalChangeCodec.js             |    42 +
 .../model/mxGraphAbstractHierarchyCell.js          |   206 +
 .../hierarchical/model/mxGraphHierarchyEdge.js     |   187 +
 .../hierarchical/model/mxGraphHierarchyModel.js    |   681 +
 .../hierarchical/model/mxGraphHierarchyNode.js     |   220 +
 .../layout/hierarchical/model/mxSwimlaneModel.js   |   801 +
 .../js/layout/hierarchical/mxHierarchicalLayout.js |   846 +
 .../src/js/layout/hierarchical/mxSwimlaneLayout.js |   937 +
 .../hierarchical/stage/mxCoordinateAssignment.js   |  1830 +
 .../stage/mxHierarchicalLayoutStage.js             |    25 +
 .../stage/mxMedianHybridCrossingReduction.js       |   675 +
 .../hierarchical/stage/mxMinimumCycleRemover.js    |   108 +
 .../hierarchical/stage/mxSwimlaneOrdering.js       |    96 +
 .../src/js/layout/mxCircleLayout.js                |   203 +
 .../src/js/layout/mxCompactTreeLayout.js           |  1203 +
 .../src/js/layout/mxCompositeLayout.js             |   101 +
 .../src/js/layout/mxEdgeLabelLayout.js             |   165 +
 .../src/js/layout/mxFastOrganicLayout.js           |   591 +
 .../src/js/layout/mxGraphLayout.js                 |   461 +
 .../src/js/layout/mxParallelEdgeLayout.js          |   225 +
 .../src/js/layout/mxPartitionLayout.js             |   240 +
 .../src/js/layout/mxRadialTreeLayout.js            |   317 +
 .../src/js/layout/mxStackLayout.js                 |   515 +
 .../workflow-composer/src/js/model/mxCell.js       |   825 +
 .../workflow-composer/src/js/model/mxCellPath.js   |   163 +
 .../workflow-composer/src/js/model/mxGeometry.js   |   415 +
 .../workflow-composer/src/js/model/mxGraphModel.js |  2667 +
 .../workflow-composer/src/js/mxClient.js           |   769 +
 .../workflow-composer/src/js/shape/mxActor.js      |    86 +
 .../workflow-composer/src/js/shape/mxArrow.js      |   115 +
 .../src/js/shape/mxArrowConnector.js               |   485 +
 .../workflow-composer/src/js/shape/mxCloud.js      |    55 +
 .../workflow-composer/src/js/shape/mxConnector.js  |   149 +
 .../workflow-composer/src/js/shape/mxCylinder.js   |   105 +
 .../src/js/shape/mxDoubleEllipse.js                |   114 +
 .../workflow-composer/src/js/shape/mxEllipse.js    |    48 +
 .../workflow-composer/src/js/shape/mxHexagon.js    |    34 +
 .../workflow-composer/src/js/shape/mxImageShape.js |   233 +
 .../workflow-composer/src/js/shape/mxLabel.js      |   275 +
 .../workflow-composer/src/js/shape/mxLine.js       |    51 +
 .../workflow-composer/src/js/shape/mxMarker.js     |   208 +
 .../workflow-composer/src/js/shape/mxPolyline.js   |   127 +
 .../src/js/shape/mxRectangleShape.js               |   117 +
 .../workflow-composer/src/js/shape/mxRhombus.js    |    54 +
 .../workflow-composer/src/js/shape/mxShape.js      |  1604 +
 .../workflow-composer/src/js/shape/mxStencil.js    |   761 +
 .../src/js/shape/mxStencilRegistry.js              |    53 +
 .../workflow-composer/src/js/shape/mxSwimlane.js   |   410 +
 .../workflow-composer/src/js/shape/mxText.js       |  1263 +
 .../workflow-composer/src/js/shape/mxTriangle.js   |    33 +
 .../src/js/util/mxAbstractCanvas2D.js              |   642 +
 .../workflow-composer/src/js/util/mxAnimation.js   |    92 +
 .../src/js/util/mxAutoSaveManager.js               |   213 +
 .../workflow-composer/src/js/util/mxClipboard.js   |   221 +
 .../workflow-composer/src/js/util/mxConstants.js   |  2275 +
 .../workflow-composer/src/js/util/mxDictionary.js  |   130 +
 .../workflow-composer/src/js/util/mxDivResizer.js  |   151 +
 .../workflow-composer/src/js/util/mxDragSource.js  |   679 +
 .../workflow-composer/src/js/util/mxEffects.js     |   211 +
 .../workflow-composer/src/js/util/mxEvent.js       |  1406 +
 .../workflow-composer/src/js/util/mxEventObject.js |   111 +
 .../workflow-composer/src/js/util/mxEventSource.js |   189 +
 .../workflow-composer/src/js/util/mxForm.js        |   202 +
 .../workflow-composer/src/js/util/mxGuide.js       |   401 +
 .../workflow-composer/src/js/util/mxImage.js       |    40 +
 .../workflow-composer/src/js/util/mxImageBundle.js |   103 +
 .../workflow-composer/src/js/util/mxImageExport.js |   175 +
 .../workflow-composer/src/js/util/mxLog.js         |   413 +
 .../workflow-composer/src/js/util/mxMorphing.js    |   248 +
 .../workflow-composer/src/js/util/mxMouseEvent.js  |   244 +
 .../src/js/util/mxObjectIdentity.js                |    72 +
 .../src/js/util/mxPanningManager.js                |   262 +
 .../workflow-composer/src/js/util/mxPoint.js       |    54 +
 .../workflow-composer/src/js/util/mxPopupMenu.js   |   613 +
 .../workflow-composer/src/js/util/mxRectangle.js   |   179 +
 .../workflow-composer/src/js/util/mxResources.js   |   450 +
 .../workflow-composer/src/js/util/mxSvgCanvas2D.js |  2179 +
 .../workflow-composer/src/js/util/mxToolbar.js     |   527 +
 .../workflow-composer/src/js/util/mxUndoManager.js |   229 +
 .../src/js/util/mxUndoableEdit.js                  |   213 +
 .../src/js/util/mxUrlConverter.js                  |   151 +
 .../workflow-composer/src/js/util/mxUtils.js       |  4353 +
 .../workflow-composer/src/js/util/mxVmlCanvas2D.js |  1102 +
 .../workflow-composer/src/js/util/mxWindow.js      |  1130 +
 .../workflow-composer/src/js/util/mxXmlCanvas2D.js |  1217 +
 .../workflow-composer/src/js/util/mxXmlRequest.js  |   463 +
 .../workflow-composer/src/js/view/mxCellEditor.js  |  1069 +
 .../workflow-composer/src/js/view/mxCellOverlay.js |   233 +
 .../src/js/view/mxCellRenderer.js                  |  1553 +
 .../workflow-composer/src/js/view/mxCellState.js   |   431 +
 .../src/js/view/mxCellStatePreview.js              |   203 +
 .../src/js/view/mxConnectionConstraint.js          |    50 +
 .../workflow-composer/src/js/view/mxEdgeStyle.js   |  1569 +
 .../workflow-composer/src/js/view/mxGraph.js       | 12768 +++
 .../src/js/view/mxGraphSelectionModel.js           |   436 +
 .../workflow-composer/src/js/view/mxGraphView.js   |  3001 +
 .../src/js/view/mxLayoutManager.js                 |   409 +
 .../src/js/view/mxMultiplicity.js                  |   257 +
 .../workflow-composer/src/js/view/mxOutline.js     |   761 +
 .../workflow-composer/src/js/view/mxPerimeter.js   |   921 +
 .../src/js/view/mxPrintPreview.js                  |  1175 +
 .../src/js/view/mxStyleRegistry.js                 |    71 +
 .../workflow-composer/src/js/view/mxStylesheet.js  |   266 +
 .../src/js/view/mxSwimlaneManager.js               |   450 +
 .../src/js/view/mxTemporaryCellStates.js           |   108 +
 .../workflow-composer/src/resources/editor.txt     |     5 +
 .../workflow-composer/src/resources/editor_de.txt  |     5 +
 .../workflow-composer/src/resources/editor_zh.txt  |     5 +
 .../workflow-composer/src/resources/graph.txt      |    11 +
 .../workflow-composer/src/resources/graph_de.txt   |    11 +
 .../workflow-composer/src/resources/graph_zh.txt   |    11 +
 178 files changed, 180123 insertions(+)

diff --git a/airavata-kubernetes/workflow-composer/index.html b/airavata-kubernetes/workflow-composer/index.html
new file mode 100644
index 0000000..7db81ca
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/index.html
@@ -0,0 +1,374 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Airavata Workflow Composer</title>
+    <script type="text/javascript">
+        mxBasePath = 'src';
+    </script>
+    <script type="text/javascript" src="src/js/mxClient.js"></script>
+    <script type="text/javascript" src="src/js/components.js"></script>
+    <script type="text/javascript">
+        function main(container, tbContainer)
+        {
+            if (!mxClient.isBrowserSupported())
+            {
+                // Displays an error message if the browser is not supported.
+                mxUtils.error('Browser is not supported!', 200, false);
+            }
+            else
+            {
+                var toolbar = new mxToolbar(tbContainer);
+                toolbar.enabled = false
+
+                // Workaround for Internet Explorer ignoring certain styles
+                if (mxClient.IS_QUIRKS)
+                {
+                    document.body.style.overflow = 'hidden';
+                    new mxDivResizer(tbContainer);
+                    new mxDivResizer(container);
+                }
+
+                var doc = mxUtils.createXmlDocument();
+
+                var relation = doc.createElement('Edge');
+
+                var graph = new mxGraph(container);
+
+                graph.dropEnabled = true;
+
+                mxDragSource.prototype.getDropTarget = function(graph, x, y)
+                {
+                    var cell = graph.getCellAt(x, y);
+
+                    if (!graph.isValidDropTarget(cell))
+                    {
+                        cell = null;
+                    }
+
+                    return cell;
+                };
+
+                // Enables new connections in the graph
+                graph.setConnectable(true);
+                graph.setMultigraph(false);
+
+                var addVertex = function(icon, w, h, componentName)
+                {
+                    addToolbarItem(graph, toolbar, fetchComponent(componentName, doc), icon);
+                };
+
+                addVertex('src/icons/start.png', 40, 40, 'START');
+                addVertex('src/icons/stop.png', 40, 40, 'STOP');
+                addVertex('src/icons/parallel.png', 40, 40, 'PARALLEL');
+                addVertex('src/icons/ssh.png', 40, 60, 'SSH');
+                addVertex('src/icons/copy.png', 40, 60, 'CP');
+                addVertex('src/icons/s3.png', 40, 60, 'S3');
+                toolbar.addLine();
+
+                graph.setCellsResizable(false);
+                graph.setResizeContainer(true);
+                graph.minimumContainerSize = new mxRectangle(0, 0, 500, 380);
+                graph.setBorder(60);
+
+                new mxKeyHandler(graph);
+
+                // Overrides method to disallow edge label editing
+                graph.isCellEditable = function(cell)
+                {
+                    return !this.getModel().isEdge(cell);
+                };
+
+                // Overrides method to provide a cell label in the display
+                graph.convertValueToString = function(cell)
+                {
+                    if (mxUtils.isNode(cell.value))
+                    {
+                        if (cell.value.nodeName.toLowerCase() == 'processingelement')
+                        {
+                            var name = cell.getAttribute('name', '');
+
+                            return name;
+                        }
+                        else if (cell.value.nodeName.toLowerCase() == 'edge')
+                        {
+                            return '';
+                        }
+
+                    }
+
+                    return '';
+                };
+
+                // Overrides method to store a cell label in the model
+                var cellLabelChanged = graph.cellLabelChanged;
+                graph.cellLabelChanged = function(cell, newValue, autoSize)
+                {
+                    if (mxUtils.isNode(cell.value) &&
+                        cell.value.nodeName.toLowerCase() == 'processingelement')
+                    {
+                        // Clones the value for correct undo/redo
+                        var elt = cell.value.cloneNode(true);
+
+                        elt.setAttribute('name', newValue);
+                        newValue = elt;
+                        autoSize = true;
+                    }
+
+                    cellLabelChanged.apply(this, arguments);
+                };
+
+                // Overrides method to create the editing value
+                var getEditingValue = graph.getEditingValue;
+                graph.getEditingValue = function(cell)
+                {
+                    if (mxUtils.isNode(cell.value) &&
+                        cell.value.nodeName.toLowerCase() == 'processingelement')
+                    {
+                        var name = cell.getAttribute('name', '');
+
+                        return name;
+                    }
+                };
+
+                new mxRubberband(graph);
+
+                document.body.appendChild(mxUtils.button('View XML', function()
+                {
+                    var encoder = new mxCodec();
+                    var node = encoder.encode(graph.getModel());
+                    mxUtils.popup(mxUtils.getPrettyXml(node), true);
+                }));
+
+                // Changes the style for match the markup
+                // Creates the default style for vertices
+                var style = graph.getStylesheet().getDefaultVertexStyle();
+                style[mxConstants.STYLE_STROKECOLOR] = 'gray';
+                style[mxConstants.STYLE_ROUNDED] = true;
+                style[mxConstants.STYLE_SHADOW] = true;
+                style[mxConstants.STYLE_FILLCOLOR] = '#DFDFDF';
+                style[mxConstants.STYLE_GRADIENTCOLOR] = 'white';
+                style[mxConstants.STYLE_FONTCOLOR] = 'black';
+                style[mxConstants.STYLE_FONTSIZE] = '12';
+                style[mxConstants.STYLE_SPACING] = 4;
+
+                // Creates the default style for edges
+                style = graph.getStylesheet().getDefaultEdgeStyle();
+                style[mxConstants.STYLE_STROKECOLOR] = '#0C0C0C';
+                style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = 'white';
+                style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector;
+                style[mxConstants.STYLE_ROUNDED] = true;
+                style[mxConstants.STYLE_FONTCOLOR] = 'black';
+                style[mxConstants.STYLE_FONTSIZE] = '10';
+
+                var parent = graph.getDefaultParent();
+                graph.getModel().beginUpdate();
+                try
+                {
+                    //var v1 = graph.insertVertex(parent, null, pe1, 40, 40, 80, 30);
+                    //var v2 = graph.insertVertex(parent, null, pe2, 200, 150, 80, 30);
+                    //var e1 = graph.insertEdge(parent, null, relation, v1, v2);
+                }
+                finally
+                {
+                    // Updates the display
+                    graph.getModel().endUpdate();
+                }
+
+                // Implements a properties panel that uses
+                // mxCellAttributeChange to change properties
+                graph.getSelectionModel().addListener(mxEvent.CHANGE, function(sender, evt)
+                {
+                    selectionChanged(graph);
+                });
+
+                selectionChanged(graph);
+
+            }
+
+            /**
+             * Updates the properties panel
+             */
+            function selectionChanged(graph)
+            {
+                var div = document.getElementById('properties');
+
+                // Forces focusout in IE
+                graph.container.focus();
+
+                // Clears the DIV the non-DOM way
+                div.innerHTML = '';
+
+                // Gets the selection cell
+                var cell = graph.getSelectionCell();
+
+                if (cell == null)
+                {
+                    mxUtils.writeln(div, 'Nothing selected.');
+                }
+                else if (cell.value)
+                {
+                    // Writes the title
+                    var center = document.createElement('center');
+                    mxUtils.writeln(center, cell.value.nodeName + ' (' + cell.id + ')');
+                    div.appendChild(center);
+                    mxUtils.br(div);
+
+                    // Creates the form from the attributes of the user object
+                    var form = new mxForm();
+
+                    var attrs = cell.value.attributes;
+
+                    for (var i = 0; i < attrs.length; i++)
+                    {
+                        if (!attrs[i].nodeName.startsWith("in-") && !attrs[i].nodeName.startsWith("out-")) {
+                            createTextField(graph, form, cell, attrs[i]);
+                        }
+                    }
+
+                    div.appendChild(form.getTable());
+                    mxUtils.br(div);
+                }
+            }
+
+            function createTextField(graph, form, cell, attribute)
+            {
+                var input = form.addText(attribute.nodeName + ':', attribute.nodeValue);
+
+                var applyHandler = function()
+                {
+                    var newValue = input.value || '';
+                    var oldValue = cell.getAttribute(attribute.nodeName, '');
+
+                    if (newValue != oldValue)
+                    {
+                        graph.getModel().beginUpdate();
+
+                        try
+                        {
+                            var edit = new mxCellAttributeChange(
+                                cell, attribute.nodeName,
+                                newValue);
+                            graph.getModel().execute(edit);
+                            graph.updateCellSize(cell);
+                        }
+                        finally
+                        {
+                            graph.getModel().endUpdate();
+                        }
+                    }
+                };
+
+                mxEvent.addListener(input, 'keypress', function (evt)
+                {
+                    // Needs to take shift into account for textareas
+                    if (evt.keyCode == /*enter*/13 &&
+                        !mxEvent.isShiftDown(evt))
+                    {
+                        input.blur();
+                    }
+                });
+
+                if (mxClient.IS_IE)
+                {
+                    mxEvent.addListener(input, 'focusout', applyHandler);
+                }
+                else
+                {
+                    // Note: Known problem is the blurring of fields in
+                    // Firefox by changing the selection, in which case
+                    // no event is fired in FF and the change is lost.
+                    // As a workaround you should use a local variable
+                    // that stores the focused field and invoke blur
+                    // explicitely where we do the graph.focus above.
+                    mxEvent.addListener(input, 'blur', applyHandler);
+                }
+            }
+
+            function addToolbarItem(graph, toolbar, prototype, image)
+            {
+                // Function that is executed when the image is dropped on
+                // the graph. The cell argument points to the cell under
+                // the mousepointer if there is one.
+                var funct = function(graph, evt, cell, x, y)
+                {
+
+                    var parent = graph.getDefaultParent();
+                    var model = graph.getModel();
+
+                    var v1 = null;
+
+                    model.beginUpdate();
+
+                    try
+                    {
+                        v1 = graph.insertVertex(parent, null, prototype, x, y, 80, 60);
+                        v1.setConnectable(false);
+
+                        var inputs = [];
+                        var outputs = [];
+                        for (var i = 0; i < prototype.attributes.length; i++)
+                        {
+                            attr = prototype.attributes[i];
+                            if (attr.nodeName.startsWith("in-")) {
+                                inputs.push(attr.nodeValue);
+                            }
+                            if (attr.nodeName.startsWith("out-")) {
+                                outputs.push(attr.nodeValue);
+                            }
+                        }
+
+                        var inputDivision = 1/(inputs.length + 1);
+                        var outputDivision = 1/(outputs.length + 1);
+
+                        inputs.forEach(function(input, index) {
+                            var v11 = graph.insertVertex(v1, null, input, 0, (index*inputDivision + inputDivision), 10, 10);
+                            v11.geometry.offset = new mxPoint(-5, -5);
+                            v11.geometry.relative = true;
+                        });
+
+                        outputs.forEach(function(output, index) {
+                            var v11 = graph.insertVertex(v1, null, output, 1, (index*outputDivision + outputDivision), 10, 10);
+                            v11.geometry.offset = new mxPoint(-5, -5);
+                            v11.geometry.relative = true;
+                        });
+                    }
+                    finally
+                    {
+                        // Updates the display
+                        graph.getModel().endUpdate();
+                    }
+
+                    graph.updateCellSize(v1);
+                    graph.setSelectionCell(v1);
+                };
+
+                // Creates the image which is used as the drag icon (preview)
+                var img = toolbar.addMode(null, image, funct);
+                mxUtils.makeDraggable(img, graph, funct);
+            }
+        };
+    </script>
+</head>
+<body onload="main(document.getElementById('graphContainer'), document.getElementById('toolContainer'))">
+<table style="position:relative;">
+    <tr>
+        <td>
+            <div id="toolContainer" style="border: solid 1px black; width: 80px; cursor: default">
+
+            </div>
+        </td>
+        <td>
+            <div id="graphContainer"
+                 style="border: solid 1px black;overflow:hidden;width:321px; cursor:default;">
+            </div>
+        </td>
+        <td valign="top">
+            <div id="properties"
+                 style="border: solid 1px black; padding: 10px;">
+            </div>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/airavata-kubernetes/workflow-composer/mxClient.js b/airavata-kubernetes/workflow-composer/mxClient.js
new file mode 100644
index 0000000..8ec0d23
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/mxClient.js
@@ -0,0 +1,88604 @@
+/**
+ * Copyright (c) 2006-2017, JGraph Ltd
+ * Copyright (c) 2006-2017, Gaudenz Alder
+ */
+var mxClient =
+{
+	/**
+	 * Class: mxClient
+	 *
+	 * Bootstrapping mechanism for the mxGraph thin client. The production version
+	 * of this file contains all code required to run the mxGraph thin client, as
+	 * well as global constants to identify the browser and operating system in
+	 * use. You may have to load chrome://global/content/contentAreaUtils.js in
+	 * your page to disable certain security restrictions in Mozilla.
+	 * 
+	 * Variable: VERSION
+	 *
+	 * Contains the current version of the mxGraph library. The strings that
+	 * communicate versions of mxGraph use the following format.
+	 * 
+	 * versionMajor.versionMinor.buildNumber.revisionNumber
+	 * 
+	 * Current version is 3.7.5.
+	 */
+	VERSION: '3.7.5',
+
+	/**
+	 * Variable: IS_IE
+	 *
+	 * True if the current browser is Internet Explorer 10 or below. Use <mxClient.IS_IE11>
+	 * to detect IE 11.
+	 */
+	IS_IE: navigator.userAgent.indexOf('MSIE') >= 0,
+
+	/**
+	 * Variable: IS_IE6
+	 *
+	 * True if the current browser is Internet Explorer 6.x.
+	 */
+	IS_IE6: navigator.userAgent.indexOf('MSIE 6') >= 0,
+
+	/**
+	 * Variable: IS_IE11
+	 *
+	 * True if the current browser is Internet Explorer 11.x.
+	 */
+	IS_IE11: !!navigator.userAgent.match(/Trident\/7\./),
+
+	/**
+	 * Variable: IS_EDGE
+	 *
+	 * True if the current browser is Microsoft Edge.
+	 */
+	IS_EDGE: !!navigator.userAgent.match(/Edge\//),
+
+	/**
+	 * Variable: IS_QUIRKS
+	 *
+	 * True if the current browser is Internet Explorer and it is in quirks mode.
+	 */
+	IS_QUIRKS: navigator.userAgent.indexOf('MSIE') >= 0 && (document.documentMode == null || document.documentMode == 5),
+
+	/**
+	 * Variable: IS_EM
+	 * 
+	 * True if the browser is IE11 in enterprise mode (IE8 standards mode).
+	 */
+	IS_EM: 'spellcheck' in document.createElement('textarea') && document.documentMode == 8,
+
+	/**
+	 * Variable: VML_PREFIX
+	 * 
+	 * Prefix for VML namespace in node names. Default is 'v'.
+	 */
+	VML_PREFIX: 'v',
+
+	/**
+	 * Variable: OFFICE_PREFIX
+	 * 
+	 * Prefix for VML office namespace in node names. Default is 'o'.
+	 */
+	OFFICE_PREFIX: 'o',
+
+	/**
+	 * Variable: IS_NS
+	 *
+	 * True if the current browser is Netscape (including Firefox).
+	 */
+  	IS_NS: navigator.userAgent.indexOf('Mozilla/') >= 0 &&
+  		navigator.userAgent.indexOf('MSIE') < 0 &&
+  		navigator.userAgent.indexOf('Edge/') < 0,
+
+	/**
+	 * Variable: IS_OP
+	 *
+	 * True if the current browser is Opera.
+	 */
+  	IS_OP: navigator.userAgent.indexOf('Opera/') >= 0 ||
+  		navigator.userAgent.indexOf('OPR/') >= 0,
+
+	/**
+	 * Variable: IS_OT
+	 *
+	 * True if -o-transform is available as a CSS style, ie for Opera browsers
+	 * based on a Presto engine with version 2.5 or later.
+	 */
+  	IS_OT: navigator.userAgent.indexOf('Presto/') >= 0 &&
+  		navigator.userAgent.indexOf('Presto/2.4.') < 0 &&
+  		navigator.userAgent.indexOf('Presto/2.3.') < 0 &&
+  		navigator.userAgent.indexOf('Presto/2.2.') < 0 &&
+  		navigator.userAgent.indexOf('Presto/2.1.') < 0 &&
+  		navigator.userAgent.indexOf('Presto/2.0.') < 0 &&
+  		navigator.userAgent.indexOf('Presto/1.') < 0,
+  	
+	/**
+	 * Variable: IS_SF
+	 *
+	 * True if the current browser is Safari.
+	 */
+  	IS_SF: navigator.userAgent.indexOf('AppleWebKit/') >= 0 &&
+  		navigator.userAgent.indexOf('Chrome/') < 0 &&
+  		navigator.userAgent.indexOf('Edge/') < 0,
+  	
+	/**
+	 * Variable: IS_IOS
+	 * 
+	 * Returns true if the user agent is an iPad, iPhone or iPod.
+	 */
+  	IS_IOS: (navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false),
+  		
+	/**
+	 * Variable: IS_GC
+	 *
+	 * True if the current browser is Google Chrome.
+	 */
+  	IS_GC: navigator.userAgent.indexOf('Chrome/') >= 0 &&
+		navigator.userAgent.indexOf('Edge/') < 0,
+	
+	/**
+	 * Variable: IS_CHROMEAPP
+	 *
+	 * True if the this is running inside a Chrome App.
+	 */
+  	IS_CHROMEAPP: window.chrome != null && chrome.app != null && chrome.app.runtime != null,
+		
+	/**
+	 * Variable: IS_FF
+	 *
+	 * True if the current browser is Firefox.
+	 */
+  	IS_FF: navigator.userAgent.indexOf('Firefox/') >= 0,
+  	
+	/**
+	 * Variable: IS_MT
+	 *
+	 * True if -moz-transform is available as a CSS style. This is the case
+	 * for all Firefox-based browsers newer than or equal 3, such as Camino,
+	 * Iceweasel, Seamonkey and Iceape.
+	 */
+  	IS_MT: (navigator.userAgent.indexOf('Firefox/') >= 0 &&
+		navigator.userAgent.indexOf('Firefox/1.') < 0 &&
+  		navigator.userAgent.indexOf('Firefox/2.') < 0) ||
+  		(navigator.userAgent.indexOf('Iceweasel/') >= 0 &&
+  		navigator.userAgent.indexOf('Iceweasel/1.') < 0 &&
+  		navigator.userAgent.indexOf('Iceweasel/2.') < 0) ||
+  		(navigator.userAgent.indexOf('SeaMonkey/') >= 0 &&
+  		navigator.userAgent.indexOf('SeaMonkey/1.') < 0) ||
+  		(navigator.userAgent.indexOf('Iceape/') >= 0 &&
+  		navigator.userAgent.indexOf('Iceape/1.') < 0),
+
+	/**
+	 * Variable: IS_SVG
+	 *
+	 * True if the browser supports SVG.
+	 */
+  	IS_SVG: navigator.userAgent.indexOf('Firefox/') >= 0 || // FF and Camino
+	  	navigator.userAgent.indexOf('Iceweasel/') >= 0 || // Firefox on Debian
+	  	navigator.userAgent.indexOf('Seamonkey/') >= 0 || // Firefox-based
+	  	navigator.userAgent.indexOf('Iceape/') >= 0 || // Seamonkey on Debian
+	  	navigator.userAgent.indexOf('Galeon/') >= 0 || // Gnome Browser (old)
+	  	navigator.userAgent.indexOf('Epiphany/') >= 0 || // Gnome Browser (new)
+	  	navigator.userAgent.indexOf('AppleWebKit/') >= 0 || // Safari/Google Chrome
+	  	navigator.userAgent.indexOf('Gecko/') >= 0 || // Netscape/Gecko
+	  	navigator.userAgent.indexOf('Opera/') >= 0 || // Opera
+	  	(document.documentMode != null && document.documentMode >= 9), // IE9+
+
+	/**
+	 * Variable: NO_FO
+	 *
+	 * True if foreignObject support is not available. This is the case for
+	 * Opera, older SVG-based browsers and all versions of IE.
+	 */
+  	NO_FO: !document.createElementNS || document.createElementNS('http://www.w3.org/2000/svg',
+  		'foreignObject') != '[object SVGForeignObjectElement]' || navigator.userAgent.indexOf('Opera/') >= 0,
+
+	/**
+	 * Variable: IS_VML
+	 *
+	 * True if the browser supports VML.
+	 */
+  	IS_VML: navigator.appName.toUpperCase() == 'MICROSOFT INTERNET EXPLORER',
+
+	/**
+	 * Variable: IS_WIN
+	 *
+	 * True if the client is a Windows.
+	 */
+  	IS_WIN: navigator.appVersion.indexOf('Win') > 0,
+
+	/**
+	 * Variable: IS_MAC
+	 *
+	 * True if the client is a Mac.
+	 */
+  	IS_MAC: navigator.appVersion.indexOf('Mac') > 0,
+
+	/**
+	 * Variable: IS_TOUCH
+	 * 
+	 * True if this device supports touchstart/-move/-end events (Apple iOS,
+	 * Android, Chromebook and Chrome Browser on touch-enabled devices).
+	 */
+  	IS_TOUCH: 'ontouchstart' in document.documentElement,
+
+	/**
+	 * Variable: IS_POINTER
+	 * 
+	 * True if this device supports Microsoft pointer events (always false on Macs).
+	 */
+  	IS_POINTER: window.PointerEvent != null && !(navigator.appVersion.indexOf('Mac') > 0),
+
+	/**
+	 * Variable: IS_LOCAL
+	 *
+	 * True if the documents location does not start with http:// or https://.
+	 */
+  	IS_LOCAL: document.location.href.indexOf('http://') < 0 &&
+  			  document.location.href.indexOf('https://') < 0,
+
+	/**
+	 * Function: isBrowserSupported
+	 *
+	 * Returns true if the current browser is supported, that is, if
+	 * <mxClient.IS_VML> or <mxClient.IS_SVG> is true.
+	 * 
+	 * Example:
+	 * 
+	 * (code)
+	 * if (!mxClient.isBrowserSupported())
+	 * {
+	 *   mxUtils.error('Browser is not supported!', 200, false);
+	 * }
+	 * (end)
+	 */
+	isBrowserSupported: function()
+	{
+		return mxClient.IS_VML || mxClient.IS_SVG;
+	},
+
+	/**
+	 * Function: link
+	 *
+	 * Adds a link node to the head of the document. Use this
+	 * to add a stylesheet to the page as follows:
+	 *
+	 * (code)
+	 * mxClient.link('stylesheet', filename);
+	 * (end)
+	 *
+	 * where filename is the (relative) URL of the stylesheet. The charset
+	 * is hardcoded to ISO-8859-1 and the type is text/css.
+	 * 
+	 * Parameters:
+	 * 
+	 * rel - String that represents the rel attribute of the link node.
+	 * href - String that represents the href attribute of the link node.
+	 * doc - Optional parent document of the link node.
+	 */
+	link: function(rel, href, doc)
+	{
+		doc = doc || document;
+
+		// Workaround for Operation Aborted in IE6 if base tag is used in head
+		if (mxClient.IS_IE6)
+		{
+			doc.write('<link rel="' + rel + '" href="' + href + '" charset="UTF-8" type="text/css"/>');
+		}
+		else
+		{	
+			var link = doc.createElement('link');
+			
+			link.setAttribute('rel', rel);
+			link.setAttribute('href', href);
+			link.setAttribute('charset', 'UTF-8');
+			link.setAttribute('type', 'text/css');
+			
+			var head = doc.getElementsByTagName('head')[0];
+	   		head.appendChild(link);
+		}
+	},
+	
+	/**
+	 * Function: include
+	 *
+	 * Dynamically adds a script node to the document header.
+	 * 
+	 * In production environments, the includes are resolved in the mxClient.js
+	 * file to reduce the number of requests required for client startup. This
+	 * function should only be used in development environments, but not in
+	 * production systems.
+	 */
+	include: function(src)
+	{
+		document.write('<script src="'+src+'"></script>');
+	},
+	
+	/**
+	 * Function: dispose
+	 * 
+	 * Frees up memory in IE by resolving cyclic dependencies between the DOM
+	 * and the JavaScript objects.
+	 */
+	dispose: function()
+	{
+		// Cleans all objects where listeners have been added
+		for (var i = 0; i < mxEvent.objects.length; i++)
+		{
+			if (mxEvent.objects[i].mxListenerList != null)
+			{
+				mxEvent.removeAllListeners(mxEvent.objects[i]);
+			}
+		}
+	}
+
+};
+
+/**
+ * Variable: mxLoadResources
+ * 
+ * Optional global config variable to toggle loading of the two resource files
+ * in <mxGraph> and <mxEditor>. Default is true. NOTE: This is a global variable,
+ * not a variable of mxClient.
+ *
+ * (code)
+ * <script type="text/javascript">
+ * 		var mxLoadResources = false;
+ * </script>
+ * <script type="text/javascript" src="/path/to/core/directory/js/mxClient.js"></script>
+ * (end)
+ */
+if (typeof(mxLoadResources) == 'undefined')
+{
+	mxLoadResources = true;
+}
+
+/**
+ * Variable: mxForceIncludes
+ * 
+ * Optional global config variable to force loading the JavaScript files in
+ * development mode. Default is undefined. NOTE: This is a global variable,
+ * not a variable of mxClient.
+ *
+ * (code)
+ * <script type="text/javascript">
+ * 		var mxLoadResources = true;
+ * </script>
+ * <script type="text/javascript" src="/path/to/core/directory/js/mxClient.js"></script>
+ * (end)
+ */
+if (typeof(mxForceIncludes) == 'undefined')
+{
+	mxForceIncludes = false;
+}
+
+/**
+ * Variable: mxResourceExtension
+ * 
+ * Optional global config variable to specify the extension of resource files.
+ * Default is true. NOTE: This is a global variable, not a variable of mxClient.
+ *
+ * (code)
+ * <script type="text/javascript">
+ * 		var mxResourceExtension = '.txt';
+ * </script>
+ * <script type="text/javascript" src="/path/to/core/directory/js/mxClient.js"></script>
+ * (end)
+ */
+if (typeof(mxResourceExtension) == 'undefined')
+{
+	mxResourceExtension = '.txt';
+}
+
+/**
+ * Variable: mxLoadStylesheets
+ * 
+ * Optional global config variable to toggle loading of the CSS files when
+ * the library is initialized. Default is true. NOTE: This is a global variable,
+ * not a variable of mxClient.
+ *
+ * (code)
+ * <script type="text/javascript">
+ * 		var mxLoadStylesheets = false;
+ * </script>
+ * <script type="text/javascript" src="/path/to/core/directory/js/mxClient.js"></script>
+ * (end)
+ */
+if (typeof(mxLoadStylesheets) == 'undefined')
+{
+	mxLoadStylesheets = true;
+}
+
+/**
+ * Variable: basePath
+ *
+ * Basepath for all URLs in the core without trailing slash. Default is '.'.
+ * Set mxBasePath prior to loading the mxClient library as follows to override
+ * this setting:
+ *
+ * (code)
+ * <script type="text/javascript">
+ * 		mxBasePath = '/path/to/core/directory';
+ * </script>
+ * <script type="text/javascript" src="/path/to/core/directory/js/mxClient.js"></script>
+ * (end)
+ * 
+ * When using a relative path, the path is relative to the URL of the page that
+ * contains the assignment. Trailing slashes are automatically removed.
+ */
+if (typeof(mxBasePath) != 'undefined' && mxBasePath.length > 0)
+{
+	// Adds a trailing slash if required
+	if (mxBasePath.substring(mxBasePath.length - 1) == '/')
+	{
+		mxBasePath = mxBasePath.substring(0, mxBasePath.length - 1);
+	}
+
+	mxClient.basePath = mxBasePath;
+}
+else
+{
+	mxClient.basePath = '.';
+}
+
+/**
+ * Variable: imageBasePath
+ *
+ * Basepath for all images URLs in the core without trailing slash. Default is
+ * <mxClient.basePath> + '/images'. Set mxImageBasePath prior to loading the
+ * mxClient library as follows to override this setting:
+ *
+ * (code)
+ * <script type="text/javascript">
+ * 		mxImageBasePath = '/path/to/image/directory';
+ * </script>
+ * <script type="text/javascript" src="/path/to/core/directory/js/mxClient.js"></script>
+ * (end)
+ * 
+ * When using a relative path, the path is relative to the URL of the page that
+ * contains the assignment. Trailing slashes are automatically removed.
+ */
+if (typeof(mxImageBasePath) != 'undefined' && mxImageBasePath.length > 0)
+{
+	// Adds a trailing slash if required
+	if (mxImageBasePath.substring(mxImageBasePath.length - 1) == '/')
+	{
+		mxImageBasePath = mxImageBasePath.substring(0, mxImageBasePath.length - 1);
+	}
+
+	mxClient.imageBasePath = mxImageBasePath;
+}
+else
+{
+	mxClient.imageBasePath = mxClient.basePath + '/images';	
+}
+
+/**
+ * Variable: language
+ *
+ * Defines the language of the client, eg. en for english, de for german etc.
+ * The special value 'none' will disable all built-in internationalization and
+ * resource loading. See <mxResources.getSpecialBundle> for handling identifiers
+ * with and without a dash.
+ * 
+ * Set mxLanguage prior to loading the mxClient library as follows to override
+ * this setting:
+ *
+ * (code)
+ * <script type="text/javascript">
+ * 		mxLanguage = 'en';
+ * </script>
+ * <script type="text/javascript" src="js/mxClient.js"></script>
+ * (end)
+ * 
+ * If internationalization is disabled, then the following variables should be
+ * overridden to reflect the current language of the system. These variables are
+ * cleared when i18n is disabled.
+ * <mxEditor.askZoomResource>, <mxEditor.lastSavedResource>,
+ * <mxEditor.currentFileResource>, <mxEditor.propertiesResource>,
+ * <mxEditor.tasksResource>, <mxEditor.helpResource>, <mxEditor.outlineResource>,
+ * <mxElbowEdgeHandler.doubleClickOrientationResource>, <mxUtils.errorResource>,
+ * <mxUtils.closeResource>, <mxGraphSelectionModel.doneResource>,
+ * <mxGraphSelectionModel.updatingSelectionResource>, <mxGraphView.doneResource>,
+ * <mxGraphView.updatingDocumentResource>, <mxCellRenderer.collapseExpandResource>,
+ * <mxGraph.containsValidationErrorsResource> and
+ * <mxGraph.alreadyConnectedResource>.
+ */
+if (typeof(mxLanguage) != 'undefined' && mxLanguage != null)
+{
+	mxClient.language = mxLanguage;
+}
+else
+{
+	mxClient.language = (mxClient.IS_IE) ? navigator.userLanguage : navigator.language;
+}
+
+/**
+ * Variable: defaultLanguage
+ * 
+ * Defines the default language which is used in the common resource files. Any
+ * resources for this language will only load the common resource file, but not
+ * the language-specific resource file. Default is 'en'.
+ * 
+ * Set mxDefaultLanguage prior to loading the mxClient library as follows to override
+ * this setting:
+ *
+ * (code)
+ * <script type="text/javascript">
+ * 		mxDefaultLanguage = 'de';
+ * </script>
+ * <script type="text/javascript" src="js/mxClient.js"></script>
+ * (end)
+ */
+if (typeof(mxDefaultLanguage) != 'undefined' && mxDefaultLanguage != null)
+{
+	mxClient.defaultLanguage = mxDefaultLanguage;
+}
+else
+{
+	mxClient.defaultLanguage = 'en';
+}
+
+// Adds all required stylesheets and namespaces
+if (mxLoadStylesheets)
+{
+	mxClient.link('stylesheet', mxClient.basePath + '/css/common.css');
+}
+
+/**
+ * Variable: languages
+ *
+ * Defines the optional array of all supported language extensions. The default
+ * language does not have to be part of this list. See
+ * <mxResources.isLanguageSupported>.
+ *
+ * (code)
+ * <script type="text/javascript">
+ * 		mxLanguages = ['de', 'it', 'fr'];
+ * </script>
+ * <script type="text/javascript" src="js/mxClient.js"></script>
+ * (end)
+ * 
+ * This is used to avoid unnecessary requests to language files, ie. if a 404
+ * will be returned.
+ */
+if (typeof(mxLanguages) != 'undefined' && mxLanguages != null)
+{
+	mxClient.languages = mxLanguages;
+}
+
+// Adds required namespaces, stylesheets and memory handling for older IE browsers
+if (mxClient.IS_VML)
+{
+	if (mxClient.IS_SVG)
+	{
+		mxClient.IS_VML = false;
+	}
+	else
+	{
+		// Enables support for IE8 standards mode. Note that this requires all attributes for VML
+		// elements to be set using direct notation, ie. node.attr = value. The use of setAttribute
+		// is not possible.
+		if (document.documentMode == 8)
+		{
+			document.namespaces.add(mxClient.VML_PREFIX, 'urn:schemas-microsoft-com:vml', '#default#VML');
+			document.namespaces.add(mxClient.OFFICE_PREFIX, 'urn:schemas-microsoft-com:office:office', '#default#VML');
+		}
+		else
+		{
+			document.namespaces.add(mxClient.VML_PREFIX, 'urn:schemas-microsoft-com:vml');
+			document.namespaces.add(mxClient.OFFICE_PREFIX, 'urn:schemas-microsoft-com:office:office');
+		}
+
+		// Workaround for limited number of stylesheets in IE (does not work in standards mode)
+		if (mxClient.IS_QUIRKS && document.styleSheets.length >= 30)
+		{
+			(function()
+			{
+				var node = document.createElement('style');
+				node.type = 'text/css';
+				node.styleSheet.cssText = mxClient.VML_PREFIX + '\\:*{behavior:url(#default#VML)}' +
+		        	mxClient.OFFICE_PREFIX + '\\:*{behavior:url(#default#VML)}';
+		        document.getElementsByTagName('head')[0].appendChild(node);
+			})();
+		}
+		else
+		{
+			document.createStyleSheet().cssText = mxClient.VML_PREFIX + '\\:*{behavior:url(#default#VML)}' +
+		    	mxClient.OFFICE_PREFIX + '\\:*{behavior:url(#default#VML)}';
+		}
+	    
+	    if (mxLoadStylesheets)
+	    {
+	    	mxClient.link('stylesheet', mxClient.basePath + '/css/explorer.css');
+	    }
+	
+		// Cleans up resources when the application terminates
+		window.attachEvent('onunload', mxClient.dispose);
+	}
+}
+
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxLog =
+{
+	/**
+	 * Class: mxLog
+	 * 
+	 * A singleton class that implements a simple console.
+	 * 
+	 * Variable: consoleName
+	 * 
+	 * Specifies the name of the console window. Default is 'Console'.
+	 */
+	consoleName: 'Console',
+	
+	/**
+	 * Variable: TRACE
+	 * 
+	 * Specified if the output for <enter> and <leave> should be visible in the
+	 * console. Default is false.
+	 */
+	TRACE: false,
+
+	/**
+	 * Variable: DEBUG
+	 * 
+	 * Specifies if the output for <debug> should be visible in the console.
+	 * Default is true.
+	 */
+	DEBUG: true,
+
+	/**
+	 * Variable: WARN
+	 * 
+	 * Specifies if the output for <warn> should be visible in the console.
+	 * Default is true.
+	 */
+	WARN: true,
+
+	/**
+	 * Variable: buffer
+	 * 
+	 * Buffer for pre-initialized content.
+	 */
+	buffer: '',
+	
+	/**
+	 * Function: init
+	 *
+	 * Initializes the DOM node for the console. This requires document.body to
+	 * point to a non-null value. This is called from within <setVisible> if the
+	 * log has not yet been initialized.
+	 */
+	init: function()
+	{
+		if (mxLog.window == null && document.body != null)
+		{
+			var title = mxLog.consoleName + ' - mxGraph ' + mxClient.VERSION;
+
+			// Creates a table that maintains the layout
+			var table = document.createElement('table');
+			table.setAttribute('width', '100%');
+			table.setAttribute('height', '100%');
+
+			var tbody = document.createElement('tbody');
+			var tr = document.createElement('tr');
+			var td = document.createElement('td');
+			td.style.verticalAlign = 'top';
+				
+			// Adds the actual console as a textarea
+			mxLog.textarea = document.createElement('textarea');
+			mxLog.textarea.setAttribute('wrap', 'off');
+			mxLog.textarea.setAttribute('readOnly', 'true');
+			mxLog.textarea.style.height = '100%';
+			mxLog.textarea.style.resize = 'none';
+			mxLog.textarea.value = mxLog.buffer;
+
+			// Workaround for wrong width in standards mode
+			if (mxClient.IS_NS && document.compatMode != 'BackCompat')
+			{
+				mxLog.textarea.style.width = '99%';
+			}
+			else
+			{
+				mxLog.textarea.style.width = '100%';
+			}
+			
+			td.appendChild(mxLog.textarea);
+			tr.appendChild(td);
+			tbody.appendChild(tr);
+
+			// Creates the container div
+			tr = document.createElement('tr');
+			mxLog.td = document.createElement('td');
+			mxLog.td.style.verticalAlign = 'top';
+			mxLog.td.setAttribute('height', '30px');
+			
+			tr.appendChild(mxLog.td);
+			tbody.appendChild(tr);
+			table.appendChild(tbody);
+
+			// Adds various debugging buttons
+			mxLog.addButton('Info', function (evt)
+			{
+				mxLog.info();
+			});
+		
+			mxLog.addButton('DOM', function (evt)
+			{
+				var content = mxUtils.getInnerHtml(document.body);
+				mxLog.debug(content);
+			});
+	
+			mxLog.addButton('Trace', function (evt)
+			{
+				mxLog.TRACE = !mxLog.TRACE;
+				
+				if (mxLog.TRACE)
+				{
+					mxLog.debug('Tracing enabled');
+				}
+				else
+				{
+					mxLog.debug('Tracing disabled');
+				}
+			});	
+
+			mxLog.addButton('Copy', function (evt)
+			{
+				try
+				{
+					mxUtils.copy(mxLog.textarea.value);
+				}
+				catch (err)
+				{
+					mxUtils.alert(err);
+				}
+			});			
+
+			mxLog.addButton('Show', function (evt)
+			{
+				try
+				{
+					mxUtils.popup(mxLog.textarea.value);
+				}
+				catch (err)
+				{
+					mxUtils.alert(err);
+				}
+			});	
+			
+			mxLog.addButton('Clear', function (evt)
+			{
+				mxLog.textarea.value = '';
+			});
+
+			// Cross-browser code to get window size
+			var h = 0;
+			var w = 0;
+			
+			if (typeof(window.innerWidth) === 'number')
+			{
+				h = window.innerHeight;
+				w = window.innerWidth;
+			}
+			else
+			{
+				h = (document.documentElement.clientHeight || document.body.clientHeight);
+				w = document.body.clientWidth;
+			}
+
+			mxLog.window = new mxWindow(title, table, Math.max(0, w - 320), Math.max(0, h - 210), 300, 160);
+			mxLog.window.setMaximizable(true);
+			mxLog.window.setScrollable(false);
+			mxLog.window.setResizable(true);
+			mxLog.window.setClosable(true);
+			mxLog.window.destroyOnClose = false;
+			
+			// Workaround for ignored textarea height in various setups
+			if (((mxClient.IS_NS || mxClient.IS_IE) && !mxClient.IS_GC &&
+				!mxClient.IS_SF && document.compatMode != 'BackCompat') ||
+				document.documentMode == 11)
+			{
+				var elt = mxLog.window.getElement();
+				
+				var resizeHandler = function(sender, evt)
+				{
+					mxLog.textarea.style.height = Math.max(0, elt.offsetHeight - 70) + 'px';
+				}; 
+				
+				mxLog.window.addListener(mxEvent.RESIZE_END, resizeHandler);
+				mxLog.window.addListener(mxEvent.MAXIMIZE, resizeHandler);
+				mxLog.window.addListener(mxEvent.NORMALIZE, resizeHandler);
+
+				mxLog.textarea.style.height = '92px';
+			}
+		}
+	},
+	
+	/**
+	 * Function: info
+	 * 
+	 * Writes the current navigator information to the console.
+	 */
+	info: function()
+	{
+		mxLog.writeln(mxUtils.toString(navigator));
+	},
+			
+	/**
+	 * Function: addButton
+	 * 
+	 * Adds a button to the console using the given label and function.
+	 */
+	addButton: function(lab, funct)
+	{
+		var button = document.createElement('button');
+		mxUtils.write(button, lab);
+		mxEvent.addListener(button, 'click', funct);
+		mxLog.td.appendChild(button);
+	},
+				
+	/**
+	 * Function: isVisible
+	 * 
+	 * Returns true if the console is visible.
+	 */
+	isVisible: function()
+	{
+		if (mxLog.window != null)
+		{
+			return mxLog.window.isVisible();
+		}
+		
+		return false;
+	},
+	
+
+	/**
+	 * Function: show
+	 * 
+	 * Shows the console.
+	 */
+	show: function()
+	{
+		mxLog.setVisible(true);
+	},
+
+	/**
+	 * Function: setVisible
+	 * 
+	 * Shows or hides the console.
+	 */
+	setVisible: function(visible)
+	{
+		if (mxLog.window == null)
+		{
+			mxLog.init();
+		}
+
+		if (mxLog.window != null)
+		{
+			mxLog.window.setVisible(visible);
+		}
+	},
+
+	/**
+	 * Function: enter
+	 * 
+	 * Writes the specified string to the console
+	 * if <TRACE> is true and returns the current 
+	 * time in milliseconds.
+	 *
+	 * Example:
+	 * 
+	 * (code)
+	 * mxLog.show();
+	 * var t0 = mxLog.enter('Hello');
+	 * // Do something
+	 * mxLog.leave('World!', t0);
+	 * (end)
+	 */
+	enter: function(string)
+	{
+		if (mxLog.TRACE)
+		{
+			mxLog.writeln('Entering '+string);
+			
+			return new Date().getTime();
+		}
+	},
+
+	/**
+	 * Function: leave
+	 * 
+	 * Writes the specified string to the console
+	 * if <TRACE> is true and computes the difference
+	 * between the current time and t0 in milliseconds.
+	 * See <enter> for an example.
+	 */
+	leave: function(string, t0)
+	{
+		if (mxLog.TRACE)
+		{
+			var dt = (t0 != 0) ? ' ('+(new Date().getTime() - t0)+' ms)' : '';
+			mxLog.writeln('Leaving '+string+dt);
+		}
+	},
+	
+	/**
+	 * Function: debug
+	 * 
+	 * Adds all arguments to the console if <DEBUG> is enabled.
+	 *
+	 * Example:
+	 * 
+	 * (code)
+	 * mxLog.show();
+	 * mxLog.debug('Hello, World!');
+	 * (end)
+	 */
+	debug: function()
+	{
+		if (mxLog.DEBUG)
+		{
+			mxLog.writeln.apply(this, arguments);
+		}
+	},
+	
+	/**
+	 * Function: warn
+	 * 
+	 * Adds all arguments to the console if <WARN> is enabled.
+	 *
+	 * Example:
+	 * 
+	 * (code)
+	 * mxLog.show();
+	 * mxLog.warn('Hello, World!');
+	 * (end)
+	 */
+	warn: function()
+	{
+		if (mxLog.WARN)
+		{
+			mxLog.writeln.apply(this, arguments);
+		}
+	},
+
+	/**
+	 * Function: write
+	 * 
+	 * Adds the specified strings to the console.
+	 */
+	write: function()
+	{
+		var string = '';
+		
+		for (var i = 0; i < arguments.length; i++)
+		{
+			string += arguments[i];
+			
+			if (i < arguments.length - 1)
+			{
+				string += ' ';
+			}
+		}
+		
+		if (mxLog.textarea != null)
+		{
+			mxLog.textarea.value = mxLog.textarea.value + string;
+
+			// Workaround for no update in Presto 2.5.22 (Opera 10.5)
+			if (navigator.userAgent.indexOf('Presto/2.5') >= 0)
+			{
+				mxLog.textarea.style.visibility = 'hidden';
+				mxLog.textarea.style.visibility = 'visible';
+			}
+			
+			mxLog.textarea.scrollTop = mxLog.textarea.scrollHeight;
+		}
+		else
+		{
+			mxLog.buffer += string;
+		}
+	},
+	
+	/**
+	 * Function: writeln
+	 * 
+	 * Adds the specified strings to the console, appending a linefeed at the
+	 * end of each string.
+	 */
+	writeln: function()
+	{
+		var string = '';
+		
+		for (var i = 0; i < arguments.length; i++)
+		{
+			string += arguments[i];
+			
+			if (i < arguments.length - 1)
+			{
+				string += ' ';
+			}
+		}
+
+		mxLog.write(string + '\n');
+	}
+	
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxObjectIdentity =
+{
+	/**
+	 * Class: mxObjectIdentity
+	 * 
+	 * Identity for JavaScript objects and functions. This is implemented using
+	 * a simple incrementing counter which is stored in each object under
+	 * <FIELD_NAME>.
+	 * 
+	 * The identity for an object does not change during its lifecycle.
+	 * 
+	 * Variable: FIELD_NAME
+	 * 
+	 * Name of the field to be used to store the object ID. Default is
+	 * <code>mxObjectId</code>.
+	 */
+	FIELD_NAME: 'mxObjectId',
+
+	/**
+	 * Variable: counter
+	 * 
+	 * Current counter.
+	 */
+	counter: 0,
+
+	/**
+	 * Function: get
+	 * 
+	 * Returns the ID for the given object or function or null if no object
+	 * is specified.
+	 */
+	get: function(obj)
+	{
+		if (obj != null)
+		{
+			if (obj[mxObjectIdentity.FIELD_NAME] == null)
+			{
+				if (typeof obj === 'object')
+				{
+					var ctor = mxUtils.getFunctionName(obj.constructor);
+					obj[mxObjectIdentity.FIELD_NAME] = ctor + '#' + mxObjectIdentity.counter++;
+				}
+				else if (typeof obj === 'function')
+				{
+					obj[mxObjectIdentity.FIELD_NAME] = 'Function#' + mxObjectIdentity.counter++;
+				}
+			}
+			
+			return obj[mxObjectIdentity.FIELD_NAME];
+		}
+		
+		return null;
+	},
+
+	/**
+	 * Function: clear
+	 * 
+	 * Deletes the ID from the given object or function.
+	 */
+	clear: function(obj)
+	{
+		if (typeof(obj) === 'object' || typeof obj === 'function')
+		{
+			delete obj[mxObjectIdentity.FIELD_NAME];
+		}
+	}
+
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDictionary
+ *
+ * A wrapper class for an associative array with object keys. Note: This
+ * implementation uses <mxObjectIdentitiy> to turn object keys into strings.
+ * 
+ * Constructor: mxEventSource
+ *
+ * Constructs a new dictionary which allows object to be used as keys.
+ */
+function mxDictionary()
+{
+	this.clear();
+};
+
+/**
+ * Function: map
+ *
+ * Stores the (key, value) pairs in this dictionary.
+ */
+mxDictionary.prototype.map = null;
+
+/**
+ * Function: clear
+ *
+ * Clears the dictionary.
+ */
+mxDictionary.prototype.clear = function()
+{
+	this.map = {};
+};
+
+/**
+ * Function: get
+ *
+ * Returns the value for the given key.
+ */
+mxDictionary.prototype.get = function(key)
+{
+	var id = mxObjectIdentity.get(key);
+	
+	return this.map[id];
+};
+
+/**
+ * Function: put
+ *
+ * Stores the value under the given key and returns the previous
+ * value for that key.
+ */
+mxDictionary.prototype.put = function(key, value)
+{
+	var id = mxObjectIdentity.get(key);
+	var previous = this.map[id];
+	this.map[id] = value;
+	
+	return previous;
+};
+
+/**
+ * Function: remove
+ *
+ * Removes the value for the given key and returns the value that
+ * has been removed.
+ */
+mxDictionary.prototype.remove = function(key)
+{
+	var id = mxObjectIdentity.get(key);
+	var previous = this.map[id];
+	delete this.map[id];
+	
+	return previous;
+};
+
+/**
+ * Function: getKeys
+ *
+ * Returns all keys as an array.
+ */
+mxDictionary.prototype.getKeys = function()
+{
+	var result = [];
+	
+	for (var key in this.map)
+	{
+		result.push(key);
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getValues
+ *
+ * Returns all values as an array.
+ */
+mxDictionary.prototype.getValues = function()
+{
+	var result = [];
+	
+	for (var key in this.map)
+	{
+		result.push(this.map[key]);
+	}
+	
+	return result;
+};
+
+/**
+ * Function: visit
+ *
+ * Visits all entries in the dictionary using the given function with the
+ * following signature: function(key, value) where key is a string and
+ * value is an object.
+ * 
+ * Parameters:
+ * 
+ * visitor - A function that takes the key and value as arguments.
+ */
+mxDictionary.prototype.visit = function(visitor)
+{
+	for (var key in this.map)
+	{
+		visitor(key, this.map[key]);
+	}
+};
+/**
+ * Copyright (c) 2006-2016, JGraph Ltd
+ * Copyright (c) 2006-2016, Gaudenz Alder
+ */
+var mxResources =
+{
+	/**
+	 * Class: mxResources
+	 * 
+	 * Implements internationalization. You can provide any number of 
+	 * resource files on the server using the following format for the 
+	 * filename: name[-en].properties. The en stands for any lowercase 
+	 * 2-character language shortcut (eg. de for german, fr for french).
+	 *
+	 * If the optional language extension is omitted, then the file is used as a 
+	 * default resource which is loaded in all cases. If a properties file for a 
+	 * specific language exists, then it is used to override the settings in the 
+	 * default resource. All entries in the file are of the form key=value. The
+	 * values may then be accessed in code via <get>. Lines without 
+	 * equal signs in the properties files are ignored.
+	 *
+	 * Resource files may either be added programmatically using
+	 * <add> or via a resource tag in the UI section of the 
+	 * editor configuration file, eg:
+	 * 
+	 * (code)
+	 * <mxEditor>
+	 *   <ui>
+	 *     <resource basename="examples/resources/mxWorkflow"/>
+	 * (end)
+	 * 
+	 * The above element will load examples/resources/mxWorkflow.properties as well
+	 * as the language specific file for the current language, if it exists.
+	 * 
+	 * Values may contain placeholders of the form {1}...{n} where each placeholder
+	 * is replaced with the value of the corresponding array element in the params
+	 * argument passed to <mxResources.get>. The placeholder {1} maps to the first
+	 * element in the array (at index 0).
+	 * 
+	 * See <mxClient.language> for more information on specifying the default
+	 * language or disabling all loading of resources.
+	 * 
+	 * Lines that start with a # sign will be ignored.
+	 * 
+	 * Special characters
+	 * 
+	 * To use unicode characters, use the standard notation (eg. \u8fd1) or %u as a
+	 * prefix (eg. %u20AC will display a Euro sign). For normal hex encoded strings,
+	 * use % as a prefix, eg. %F6 will display a "o umlaut" (&ouml;).
+	 * 
+	 * See <resourcesEncoded> to disable this. If you disable this, make sure that
+	 * your files are UTF-8 encoded.
+	 * 
+	 * Asynchronous loading
+	 * 
+	 * By default, the core adds two resource files synchronously at load time.
+	 * To load these files asynchronously, set <mxLoadResources> to false
+	 * before loading mxClient.js and use <mxResources.loadResources> instead.
+	 * 
+	 * Variable: resources
+	 * 
+	 * Associative array that maps from keys to values.
+	 */
+	resources: [],
+
+	/**
+	 * Variable: extension
+	 * 
+	 * Specifies the extension used for language files. Default is <mxResourceExtension>.
+	 */
+	extension: mxResourceExtension,
+
+	/**
+	 * Variable: resourcesEncoded
+	 * 
+	 * Specifies whether or not values in resource files are encoded with \u or
+	 * percentage. Default is false.
+	 */
+	resourcesEncoded: false,
+
+	/**
+	 * Variable: loadDefaultBundle
+	 * 
+	 * Specifies if the default file for a given basename should be loaded.
+	 * Default is true.
+	 */
+	loadDefaultBundle: true,
+
+	/**
+	 * Variable: loadDefaultBundle
+	 * 
+	 * Specifies if the specific language file file for a given basename should
+	 * be loaded. Default is true.
+	 */
+	loadSpecialBundle: true,
+
+	/**
+	 * Function: isLanguageSupported
+	 * 
+	 * Hook for subclassers to disable support for a given language. This
+	 * implementation returns true if lan is in <mxClient.languages>.
+	 * 
+	 * Parameters:
+	 *
+	 * lan - The current language.
+	 */
+	isLanguageSupported: function(lan)
+	{
+		if (mxClient.languages != null)
+		{
+			return mxUtils.indexOf(mxClient.languages, lan) >= 0;
+		}
+		
+		return true;
+	},
+
+	/**
+	 * Function: getDefaultBundle
+	 * 
+	 * Hook for subclassers to return the URL for the special bundle. This
+	 * implementation returns basename + <extension> or null if
+	 * <loadDefaultBundle> is false.
+	 * 
+	 * Parameters:
+	 * 
+	 * basename - The basename for which the file should be loaded.
+	 * lan - The current language.
+	 */
+	getDefaultBundle: function(basename, lan)
+	{
+		if (mxResources.loadDefaultBundle || !mxResources.isLanguageSupported(lan))
+		{
+			return basename + mxResources.extension;
+		}
+		else
+		{
+			return null;
+		}
+	},
+
+	/**
+	 * Function: getSpecialBundle
+	 * 
+	 * Hook for subclassers to return the URL for the special bundle. This
+	 * implementation returns basename + '_' + lan + <extension> or null if
+	 * <loadSpecialBundle> is false or lan equals <mxClient.defaultLanguage>.
+	 * 
+	 * If <mxResources.languages> is not null and <mxClient.language> contains
+	 * a dash, then this method checks if <isLanguageSupported> returns true
+	 * for the full language (including the dash). If that returns false the
+	 * first part of the language (up to the dash) will be tried as an extension.
+	 * 
+	 * If <mxResources.language> is null then the first part of the language is
+	 * used to maintain backwards compatibility.
+	 * 
+	 * Parameters:
+	 * 
+	 * basename - The basename for which the file should be loaded.
+	 * lan - The language for which the file should be loaded.
+	 */
+	getSpecialBundle: function(basename, lan)
+	{
+		if (mxClient.languages == null || !this.isLanguageSupported(lan))
+		{
+			var dash = lan.indexOf('-');
+			
+			if (dash > 0)
+			{
+				lan = lan.substring(0, dash);
+			}
+		}
+
+		if (mxResources.loadSpecialBundle && mxResources.isLanguageSupported(lan) && lan != mxClient.defaultLanguage)
+		{
+			return basename + '_' + lan + mxResources.extension;
+		}
+		else
+		{
+			return null;
+		}
+	},
+
+	/**
+	 * Function: add
+	 * 
+	 * Adds the default and current language properties file for the specified
+	 * basename. Existing keys are overridden as new files are added. If no
+	 * callback is used then the request is synchronous.
+	 *
+	 * Example:
+	 * 
+	 * At application startup, additional resources may be 
+	 * added using the following code:
+	 * 
+	 * (code)
+	 * mxResources.add('resources/editor');
+	 * (end)
+	 * 
+	 * Parameters:
+	 * 
+	 * basename - The basename for which the file should be loaded.
+	 * lan - The language for which the file should be loaded.
+	 * callback - Optional callback for asynchronous loading.
+	 */
+	add: function(basename, lan, callback)
+	{
+		lan = (lan != null) ? lan : ((mxClient.language != null) ?
+			mxClient.language.toLowerCase() : mxConstants.NONE);
+		
+		if (lan != mxConstants.NONE)
+		{
+			var defaultBundle = mxResources.getDefaultBundle(basename, lan);
+			var specialBundle = mxResources.getSpecialBundle(basename, lan);
+			
+			var loadSpecialBundle = function()
+			{
+				if (specialBundle != null)
+				{
+					if (callback)
+					{
+						mxUtils.get(specialBundle, function(req)
+						{
+							mxResources.parse(req.getText());
+							callback();
+						}, function()
+						{
+							callback();
+						});
+					}
+					else
+					{
+						try
+						{
+					   		var req = mxUtils.load(specialBundle);
+					   		
+					   		if (req.isReady())
+					   		{
+					 	   		mxResources.parse(req.getText());
+					   		}
+				   		}
+				   		catch (e)
+				   		{
+				   			// ignore
+					   	}
+					}
+				}
+				else if (callback != null)
+				{
+					callback();
+				}
+			}
+			
+			if (defaultBundle != null)
+			{
+				if (callback)
+				{
+					mxUtils.get(defaultBundle, function(req)
+					{
+						mxResources.parse(req.getText());
+						loadSpecialBundle();
+					}, function()
+					{
+						loadSpecialBundle();
+					});
+				}
+				else
+				{
+					try
+					{
+				   		var req = mxUtils.load(defaultBundle);
+				   		
+				   		if (req.isReady())
+				   		{
+				 	   		mxResources.parse(req.getText());
+				   		}
+				   		
+				   		loadSpecialBundle();
+				  	}
+				  	catch (e)
+				  	{
+				  		// ignore
+				  	}
+				}
+			}
+			else
+			{
+				// Overlays the language specific file (_lan-extension)
+				loadSpecialBundle();
+			}
+		}
+	},
+
+	/**
+	 * Function: parse
+	 * 
+	 * Parses the key, value pairs in the specified
+	 * text and stores them as local resources.
+	 */
+	parse: function(text)
+	{
+		if (text != null)
+		{
+			var lines = text.split('\n');
+			
+			for (var i = 0; i < lines.length; i++)
+			{
+				if (lines[i].charAt(0) != '#')
+				{
+					var index = lines[i].indexOf('=');
+					
+					if (index > 0)
+					{
+						var key = lines[i].substring(0, index);
+						var idx = lines[i].length;
+						
+						if (lines[i].charCodeAt(idx - 1) == 13)
+						{
+							idx--;
+						}
+						
+						var value = lines[i].substring(index + 1, idx);
+						
+						if (this.resourcesEncoded)
+						{
+							value = value.replace(/\\(?=u[a-fA-F\d]{4})/g,"%");
+							mxResources.resources[key] = unescape(value);
+						}
+						else
+						{
+							mxResources.resources[key] = value;
+						}
+					}
+				}
+			}
+		}
+	},
+
+	/**
+	 * Function: get
+	 * 
+	 * Returns the value for the specified resource key.
+	 *
+	 * Example:
+	 * To read the value for 'welomeMessage', use the following:
+	 * (code)
+	 * var result = mxResources.get('welcomeMessage') || '';
+	 * (end)
+	 *
+	 * This would require an entry of the following form in
+	 * one of the English language resource files:
+	 * (code)
+	 * welcomeMessage=Welcome to mxGraph!
+	 * (end)
+	 * 
+	 * The part behind the || is the string value to be used if the given
+	 * resource is not available.
+	 * 
+	 * Parameters:
+	 * 
+	 * key - String that represents the key of the resource to be returned.
+	 * params - Array of the values for the placeholders of the form {1}...{n}
+	 * to be replaced with in the resulting string.
+	 * defaultValue - Optional string that specifies the default return value.
+	 */
+	get: function(key, params, defaultValue)
+	{
+		var value = mxResources.resources[key];
+		
+		// Applies the default value if no resource was found
+		if (value == null)
+		{
+			value = defaultValue;
+		}
+		
+		// Replaces the placeholders with the values in the array
+		if (value != null && params != null)
+		{
+			value = mxResources.replacePlaceholders(value, params);
+		}
+		
+		return value;
+	},
+
+	/**
+	 * Function: replacePlaceholders
+	 * 
+	 * Replaces the given placeholders with the given parameters.
+	 * 
+	 * Parameters:
+	 * 
+	 * value - String that contains the placeholders.
+	 * params - Array of the values for the placeholders of the form {1}...{n}
+	 * to be replaced with in the resulting string.
+	 */
+	replacePlaceholders: function(value, params)
+	{
+		var result = [];
+		var index = null;
+		
+		for (var i = 0; i < value.length; i++)
+		{
+			var c = value.charAt(i);
+
+			if (c == '{')
+			{
+				index = '';
+			}
+			else if (index != null && 	c == '}')
+			{
+				index = parseInt(index)-1;
+				
+				if (index >= 0 && index < params.length)
+				{
+					result.push(params[index]);
+				}
+				
+				index = null;
+			}
+			else if (index != null)
+			{
+				index += c;
+			}
+			else
+			{
+				result.push(c);
+			}
+		}
+		
+		return result.join('');
+	},
+
+	/**
+	 * Function: loadResources
+	 * 
+	 * Loads all required resources asynchronously. Use this to load the graph and
+	 * editor resources if <mxLoadResources> is false.
+	 * 
+	 * Parameters:
+	 * 
+	 * callback - Callback function for asynchronous loading.
+	 */
+	loadResources: function(callback)
+	{
+		mxResources.add(mxClient.basePath+'/resources/editor', null, function()
+		{
+			mxResources.add(mxClient.basePath+'/resources/graph', null, callback);
+		});
+	}
+
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxPoint
+ *
+ * Implements a 2-dimensional vector with double precision coordinates.
+ * 
+ * Constructor: mxPoint
+ *
+ * Constructs a new point for the optional x and y coordinates. If no
+ * coordinates are given, then the default values for <x> and <y> are used.
+ */
+function mxPoint(x, y)
+{
+	this.x = (x != null) ? x : 0;
+	this.y = (y != null) ? y : 0;
+};
+
+/**
+ * Variable: x
+ *
+ * Holds the x-coordinate of the point. Default is 0.
+ */
+mxPoint.prototype.x = null;
+
+/**
+ * Variable: y
+ *
+ * Holds the y-coordinate of the point. Default is 0.
+ */
+mxPoint.prototype.y = null;
+
+/**
+ * Function: equals
+ * 
+ * Returns true if the given object equals this point.
+ */
+mxPoint.prototype.equals = function(obj)
+{
+	return obj != null && obj.x == this.x && obj.y == this.y;
+};
+
+/**
+ * Function: clone
+ *
+ * Returns a clone of this <mxPoint>.
+ */
+mxPoint.prototype.clone = function()
+{
+	// Handles subclasses as well
+	return mxUtils.clone(this);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxRectangle
+ *
+ * Extends <mxPoint> to implement a 2-dimensional rectangle with double
+ * precision coordinates.
+ * 
+ * Constructor: mxRectangle
+ *
+ * Constructs a new rectangle for the optional parameters. If no parameters
+ * are given then the respective default values are used.
+ */
+function mxRectangle(x, y, width, height)
+{
+	mxPoint.call(this, x, y);
+
+	this.width = (width != null) ? width : 0;
+	this.height = (height != null) ? height : 0;
+};
+
+/**
+ * Extends mxPoint.
+ */
+mxRectangle.prototype = new mxPoint();
+mxRectangle.prototype.constructor = mxRectangle;
+
+/**
+ * Variable: width
+ *
+ * Holds the width of the rectangle. Default is 0.
+ */
+mxRectangle.prototype.width = null;
+
+/**
+ * Variable: height
+ *
+ * Holds the height of the rectangle. Default is 0.
+ */
+mxRectangle.prototype.height = null;
+
+/**
+ * Function: setRect
+ * 
+ * Sets this rectangle to the specified values
+ */
+mxRectangle.prototype.setRect = function(x, y, w, h)
+{
+    this.x = x;
+    this.y = y;
+    this.width = w;
+    this.height = h;
+};
+
+/**
+ * Function: getCenterX
+ * 
+ * Returns the x-coordinate of the center point.
+ */
+mxRectangle.prototype.getCenterX = function ()
+{
+	return this.x + this.width/2;
+};
+
+/**
+ * Function: getCenterY
+ * 
+ * Returns the y-coordinate of the center point.
+ */
+mxRectangle.prototype.getCenterY = function ()
+{
+	return this.y + this.height/2;
+};
+
+/**
+ * Function: add
+ *
+ * Adds the given rectangle to this rectangle.
+ */
+mxRectangle.prototype.add = function(rect)
+{
+	if (rect != null)
+	{
+		var minX = Math.min(this.x, rect.x);
+		var minY = Math.min(this.y, rect.y);
+		var maxX = Math.max(this.x + this.width, rect.x + rect.width);
+		var maxY = Math.max(this.y + this.height, rect.y + rect.height);
+		
+		this.x = minX;
+		this.y = minY;
+		this.width = maxX - minX;
+		this.height = maxY - minY;
+	}
+};
+
+/**
+ * Function: intersect
+ * 
+ * Changes this rectangle to where it overlaps with the given rectangle.
+ */
+mxRectangle.prototype.intersect = function(rect)
+{
+	if (rect != null)
+	{
+		var r1 = this.x + this.width;
+		var r2 = rect.x + rect.width;
+		
+		var b1 = this.y + this.height;
+		var b2 = rect.y + rect.height;
+		
+		this.x = Math.max(this.x, rect.x);
+		this.y = Math.max(this.y, rect.y);
+		this.width = Math.min(r1, r2) - this.x;
+		this.height = Math.min(b1, b2) - this.y;
+	}
+};
+
+/**
+ * Function: grow
+ *
+ * Grows the rectangle by the given amount, that is, this method subtracts
+ * the given amount from the x- and y-coordinates and adds twice the amount
+ * to the width and height.
+ */
+mxRectangle.prototype.grow = function(amount)
+{
+	this.x -= amount;
+	this.y -= amount;
+	this.width += 2 * amount;
+	this.height += 2 * amount;
+};
+
+/**
+ * Function: getPoint
+ * 
+ * Returns the top, left corner as a new <mxPoint>.
+ */
+mxRectangle.prototype.getPoint = function()
+{
+	return new mxPoint(this.x, this.y);
+};
+
+/**
+ * Function: rotate90
+ * 
+ * Rotates this rectangle by 90 degree around its center point.
+ */
+mxRectangle.prototype.rotate90 = function()
+{
+	var t = (this.width - this.height) / 2;
+	this.x += t;
+	this.y -= t;
+	var tmp = this.width;
+	this.width = this.height;
+	this.height = tmp;
+};
+
+/**
+ * Function: equals
+ * 
+ * Returns true if the given object equals this rectangle.
+ */
+mxRectangle.prototype.equals = function(obj)
+{
+	return obj != null && obj.x == this.x && obj.y == this.y &&
+		obj.width == this.width && obj.height == this.height;
+};
+
+/**
+ * Function: fromRectangle
+ * 
+ * Returns a new <mxRectangle> which is a copy of the given rectangle.
+ */
+mxRectangle.fromRectangle = function(rect)
+{
+	return new mxRectangle(rect.x, rect.y, rect.width, rect.height);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxEffects =
+{
+
+	/**
+	 * Class: mxEffects
+	 * 
+	 * Provides animation effects.
+	 */
+
+	/**
+	 * Function: animateChanges
+	 * 
+	 * Asynchronous animated move operation. See also: <mxMorphing>.
+	 * 
+	 * Example:
+	 * 
+	 * (code)
+	 * graph.model.addListener(mxEvent.CHANGE, function(sender, evt)
+	 * {
+	 *   var changes = evt.getProperty('edit').changes;
+	 * 
+	 *   if (changes.length < 10)
+	 *   {
+	 *     mxEffects.animateChanges(graph, changes);
+	 *   }
+	 * });
+	 * (end)
+	 * 
+	 * Parameters:
+	 * 
+	 * graph - <mxGraph> that received the changes.
+	 * changes - Array of changes to be animated.
+	 * done - Optional function argument that is invoked after the
+	 * last step of the animation.
+	 */
+	animateChanges: function(graph, changes, done)
+	{
+		var maxStep = 10;
+		var step = 0;
+
+		var animate = function() 
+		{
+			var isRequired = false;
+			
+			for (var i = 0; i < changes.length; i++)
+			{
+				var change = changes[i];
+				
+				if (change instanceof mxGeometryChange ||
+					change instanceof mxTerminalChange ||
+					change instanceof mxValueChange ||
+					change instanceof mxChildChange ||
+					change instanceof mxStyleChange)
+				{
+					var state = graph.getView().getState(change.cell || change.child, false);
+					
+					if (state != null)
+					{
+						isRequired = true;
+					
+						if (change.constructor != mxGeometryChange || graph.model.isEdge(change.cell))
+						{
+							mxUtils.setOpacity(state.shape.node, 100 * step / maxStep);
+						}
+						else
+						{
+							var scale = graph.getView().scale;					
+
+							var dx = (change.geometry.x - change.previous.x) * scale;
+							var dy = (change.geometry.y - change.previous.y) * scale;
+							
+							var sx = (change.geometry.width - change.previous.width) * scale;
+							var sy = (change.geometry.height - change.previous.height) * scale;
+							
+							if (step == 0)
+							{
+								state.x -= dx;
+								state.y -= dy;
+								state.width -= sx;
+								state.height -= sy;
+							}
+							else
+							{
+								state.x += dx / maxStep;
+								state.y += dy / maxStep;
+								state.width += sx / maxStep;
+								state.height += sy / maxStep;
+							}
+							
+							graph.cellRenderer.redraw(state);
+							
+							// Fades all connected edges and children
+							mxEffects.cascadeOpacity(graph, change.cell, 100 * step / maxStep);
+						}
+					}
+				}
+			}
+
+			if (step < maxStep && isRequired)
+			{
+				step++;
+				window.setTimeout(animate, delay);
+			}
+			else if (done != null)
+			{
+				done();
+			}
+		};
+		
+		var delay = 30;
+		animate();
+	},
+    
+	/**
+	 * Function: cascadeOpacity
+	 * 
+	 * Sets the opacity on the given cell and its descendants.
+	 * 
+	 * Parameters:
+	 * 
+	 * graph - <mxGraph> that contains the cells.
+	 * cell - <mxCell> to set the opacity for.
+	 * opacity - New value for the opacity in %.
+	 */
+    cascadeOpacity: function(graph, cell, opacity)
+	{
+		// Fades all children
+		var childCount = graph.model.getChildCount(cell);
+		
+		for (var i=0; i<childCount; i++)
+		{
+			var child = graph.model.getChildAt(cell, i);
+			var childState = graph.getView().getState(child);
+			
+			if (childState != null)
+			{
+				mxUtils.setOpacity(childState.shape.node, opacity);
+				mxEffects.cascadeOpacity(graph, child, opacity);
+			}
+		}
+		
+		// Fades all connected edges
+		var edges = graph.model.getEdges(cell);
+		
+		if (edges != null)
+		{
+			for (var i=0; i<edges.length; i++)
+			{
+				var edgeState = graph.getView().getState(edges[i]);
+				
+				if (edgeState != null)
+				{
+					mxUtils.setOpacity(edgeState.shape.node, opacity);
+				}
+			}
+		}
+	},
+
+	/**
+	 * Function: fadeOut
+	 * 
+	 * Asynchronous fade-out operation.
+	 */
+	fadeOut: function(node, from, remove, step, delay, isEnabled)
+	{
+		step = step || 40;
+		delay = delay || 30;
+		
+		var opacity = from || 100;
+		
+		mxUtils.setOpacity(node, opacity);
+		
+		if (isEnabled || isEnabled == null)
+		{
+			var f = function()
+			{
+			    opacity = Math.max(opacity-step, 0);
+				mxUtils.setOpacity(node, opacity);
+				
+				if (opacity > 0)
+				{
+					window.setTimeout(f, delay);
+				}
+				else
+				{
+					node.style.visibility = 'hidden';
+					
+					if (remove && node.parentNode)
+					{
+						node.parentNode.removeChild(node);
+					}
+				}
+			};
+			window.setTimeout(f, delay);
+		}
+		else
+		{
+			node.style.visibility = 'hidden';
+			
+			if (remove && node.parentNode)
+			{
+				node.parentNode.removeChild(node);
+			}
+		}
+	}
+
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxUtils =
+{
+	/**
+	 * Class: mxUtils
+	 * 
+	 * A singleton class that provides cross-browser helper methods.
+	 * This is a global functionality. To access the functions in this
+	 * class, use the global classname appended by the functionname.
+	 * You may have to load chrome://global/content/contentAreaUtils.js
+	 * to disable certain security restrictions in Mozilla for the <open>,
+	 * <save>, <saveAs> and <copy> function.
+	 * 
+	 * For example, the following code displays an error message:
+	 * 
+	 * (code)
+	 * mxUtils.error('Browser is not supported!', 200, false);
+	 * (end)
+	 * 
+	 * Variable: errorResource
+	 * 
+	 * Specifies the resource key for the title of the error window. If the
+	 * resource for this key does not exist then the value is used as
+	 * the title. Default is 'error'.
+	 */
+	errorResource: (mxClient.language != 'none') ? 'error' : '',
+	
+	/**
+	 * Variable: closeResource
+	 * 
+	 * Specifies the resource key for the label of the close button. If the
+	 * resource for this key does not exist then the value is used as
+	 * the label. Default is 'close'.
+	 */
+	closeResource: (mxClient.language != 'none') ? 'close' : '',
+
+	/**
+	 * Variable: errorImage
+	 * 
+	 * Defines the image used for error dialogs.
+	 */
+	errorImage: mxClient.imageBasePath + '/error.gif',
+	
+	/**
+	 * Function: removeCursors
+	 * 
+	 * Removes the cursors from the style of the given DOM node and its
+	 * descendants.
+	 * 
+	 * Parameters:
+	 * 
+	 * element - DOM node to remove the cursor style from.
+	 */
+	removeCursors: function(element)
+	{
+		if (element.style != null)
+		{
+			element.style.cursor = '';
+		}
+		
+		var children = element.childNodes;
+		
+		if (children != null)
+		{
+	        var childCount = children.length;
+	        
+	        for (var i = 0; i < childCount; i += 1)
+	        {
+	            mxUtils.removeCursors(children[i]);
+	        }
+	    }
+	},
+
+	/**
+	 * Function: getCurrentStyle
+	 * 
+	 * Returns the current style of the specified element.
+	 * 
+	 * Parameters:
+	 * 
+	 * element - DOM node whose current style should be returned.
+	 */
+	getCurrentStyle: function()
+	{
+		if (mxClient.IS_IE)
+		{
+			return function(element)
+			{
+				return (element != null) ? element.currentStyle : null;
+			};
+		}
+		else
+		{
+			return function(element)
+			{
+				return (element != null) ?
+					window.getComputedStyle(element, '') :
+					null;
+			};
+		}
+	}(),
+	
+	/**
+	 * Function: parseCssNumber
+	 * 
+	 * Parses the given CSS numeric value adding handling for the values thin,
+	 * medium and thick (2, 4 and 6).
+	 */
+	parseCssNumber: function(value)
+	{
+		if (value == 'thin')
+		{
+			value = '2';
+		}
+		else if (value == 'medium')
+		{
+			value = '4';
+		}
+		else if (value == 'thick')
+		{
+			value = '6';
+		}
+		
+		value = parseFloat(value);
+		
+		if (isNaN(value))
+		{
+			value = 0;
+		}
+		
+		return value;
+	},
+
+	/**
+	 * Function: setPrefixedStyle
+	 * 
+	 * Adds the given style with the standard name and an optional vendor prefix for the current
+	 * browser.
+	 * 
+	 * (code)
+	 * mxUtils.setPrefixedStyle(node.style, 'transformOrigin', '0% 0%');
+	 * (end)
+	 */
+	setPrefixedStyle: function()
+	{
+		var prefix = null;
+		
+		if (mxClient.IS_OT)
+		{
+			prefix = 'O';
+		}
+		else if (mxClient.IS_SF || mxClient.IS_GC)
+		{
+			prefix = 'Webkit';
+		}
+		else if (mxClient.IS_MT)
+		{
+			prefix = 'Moz';
+		}
+		else if (mxClient.IS_IE && document.documentMode >= 9 && document.documentMode < 10)
+		{
+			prefix = 'ms';
+		}
+
+		return function(style, name, value)
+		{
+			style[name] = value;
+			
+			if (prefix != null && name.length > 0)
+			{
+				name = prefix + name.substring(0, 1).toUpperCase() + name.substring(1);
+				style[name] = value;
+			}
+		};
+	}(),
+	
+	/**
+	 * Function: hasScrollbars
+	 * 
+	 * Returns true if the overflow CSS property of the given node is either
+	 * scroll or auto.
+	 * 
+	 * Parameters:
+	 * 
+	 * node - DOM node whose style should be checked for scrollbars.
+	 */
+	hasScrollbars: function(node)
+	{
+		var style = mxUtils.getCurrentStyle(node);
+
+		return style != null && (style.overflow == 'scroll' || style.overflow == 'auto');
+	},
+	
+	/**
+	 * Function: bind
+	 * 
+	 * Returns a wrapper function that locks the execution scope of the given
+	 * function to the specified scope. Inside funct, the "this" keyword
+	 * becomes a reference to that scope.
+	 */
+	bind: function(scope, funct)
+	{
+		return function()
+		{
+			return funct.apply(scope, arguments);
+		};
+	},
+	
+	/**
+	 * Function: eval
+	 * 
+	 * Evaluates the given expression using eval and returns the JavaScript
+	 * object that represents the expression result. Supports evaluation of
+	 * expressions that define functions and returns the function object for
+	 * these expressions.
+	 * 
+	 * Parameters:
+	 * 
+	 * expr - A string that represents a JavaScript expression.
+	 */
+	eval: function(expr)
+	{
+		var result = null;
+
+		if (expr.indexOf('function') >= 0)
+		{
+			try
+			{
+				eval('var _mxJavaScriptExpression='+expr);
+				result = _mxJavaScriptExpression;
+				// TODO: Use delete here?
+				_mxJavaScriptExpression = null;
+			}
+			catch (e)
+			{
+				mxLog.warn(e.message + ' while evaluating ' + expr);
+			}
+		}
+		else
+		{
+			try
+			{
+				result = eval(expr);
+			}
+			catch (e)
+			{
+				mxLog.warn(e.message + ' while evaluating ' + expr);
+			}
+		}
+		
+		return result;
+	},
+	
+	/**
+	 * Function: findNode
+	 * 
+	 * Returns the first node where attr equals value.
+	 * This implementation does not use XPath.
+	 */
+	findNode: function(node, attr, value)
+	{
+		if (node.nodeType == mxConstants.NODETYPE_ELEMENT)
+		{
+			var tmp = node.getAttribute(attr);
+	
+			if (tmp != null && tmp == value)
+			{
+				return node;
+			}
+		}
+		
+		node = node.firstChild;
+		
+		while (node != null)
+		{
+			var result = mxUtils.findNode(node, attr, value);
+			
+			if (result != null)
+			{
+				return result;
+			}
+			
+			node = node.nextSibling;
+		}
+		
+		return null;
+	},
+
+	/**
+	 * Function: getFunctionName
+	 * 
+	 * Returns the name for the given function.
+	 * 
+	 * Parameters:
+	 * 
+	 * f - JavaScript object that represents a function.
+	 */
+	getFunctionName: function(f)
+	{
+		var str = null;
+
+		if (f != null)
+		{
+			if (f.name != null)
+			{
+				str = f.name;
+			}
+			else
+			{
+				str = mxUtils.trim(f.toString());
+				
+				if (/^function\s/.test(str))
+				{
+					str = mxUtils.ltrim(str.substring(9));
+					var idx2 = str.indexOf('(');
+					
+					if (idx2 > 0)
+					{
+						str = str.substring(0, idx2);
+					}
+				}
+			}
+		}
+		
+		return str;
+	},
+
+	/**
+	 * Function: indexOf
+	 * 
+	 * Returns the index of obj in array or -1 if the array does not contain
+	 * the given object.
+	 * 
+	 * Parameters:
+	 * 
+	 * array - Array to check for the given obj.
+	 * obj - Object to find in the given array.
+	 */
+	indexOf: function(array, obj)
+	{
+		if (array != null && obj != null)
+		{
+			for (var i = 0; i < array.length; i++)
+			{
+				if (array[i] == obj)
+				{
+					return i;
+				}
+			}
+		}
+		
+		return -1;
+	},
+
+	/**
+	 * Function: forEach
+	 * 
+	 * Calls the given function for each element of the given array and returns
+	 * the array.
+	 * 
+	 * Parameters:
+	 * 
+	 * array - Array that contains the elements.
+	 * fn - Function to be called for each object.
+	 */
+	forEach: function(array, fn)
+	{
+		if (array != null && fn != null)
+		{
+			for (var i = 0; i < array.length; i++)
+			{
+				fn(array[i]);
+			}
+		}
+		
+		return array;
+	},
+
+	/**
+	 * Function: remove
+	 * 
+	 * Removes all occurrences of the given object in the given array or
+	 * object. If there are multiple occurrences of the object, be they
+	 * associative or as an array entry, all occurrences are removed from
+	 * the array or deleted from the object. By removing the object from
+	 * the array, all elements following the removed element are shifted
+	 * by one step towards the beginning of the array.
+	 * 
+	 * The length of arrays is not modified inside this function.
+	 * 
+	 * Parameters:
+	 * 
+	 * obj - Object to find in the given array.
+	 * array - Array to check for the given obj.
+	 */
+	remove: function(obj, array)
+	{
+		var result = null;
+		
+		if (typeof(array) == 'object')
+		{
+			var index = mxUtils.indexOf(array, obj);
+			
+			while (index >= 0)
+			{
+				array.splice(index, 1);
+				result = obj;
+				index = mxUtils.indexOf(array, obj);
+			}
+		}
+
+		for (var key in array)
+		{
+			if (array[key] == obj)
+			{
+				delete array[key];
+				result = obj;
+			}
+		}
+		
+		return result;
+	},
+	
+	/**
+	 * Function: isNode
+	 * 
+	 * Returns true if the given value is an XML node with the node name
+	 * and if the optional attribute has the specified value.
+	 * 
+	 * This implementation assumes that the given value is a DOM node if the
+	 * nodeType property is numeric, that is, if isNaN returns false for
+	 * value.nodeType.
+	 * 
+	 * Parameters:
+	 * 
+	 * value - Object that should be examined as a node.
+	 * nodeName - String that specifies the node name.
+	 * attributeName - Optional attribute name to check.
+	 * attributeValue - Optional attribute value to check.
+	 */
+	 isNode: function(value, nodeName, attributeName, attributeValue)
+	 {
+	 	if (value != null && !isNaN(value.nodeType) && (nodeName == null ||
+	 		value.nodeName.toLowerCase() == nodeName.toLowerCase()))
+ 		{
+ 			return attributeName == null ||
+ 				value.getAttribute(attributeName) == attributeValue;
+ 		}
+	 	
+	 	return false;
+	 },
+	
+	/**
+	 * Function: isAncestorNode
+	 * 
+	 * Returns true if the given ancestor is an ancestor of the
+	 * given DOM node in the DOM. This also returns true if the
+	 * child is the ancestor.
+	 * 
+	 * Parameters:
+	 * 
+	 * ancestor - DOM node that represents the ancestor.
+	 * child - DOM node that represents the child.
+	 */
+	 isAncestorNode: function(ancestor, child)
+	 {
+	 	var parent = child;
+	 	
+	 	while (parent != null)
+	 	{
+	 		if (parent == ancestor)
+	 		{
+	 			return true;
+	 		}
+
+	 		parent = parent.parentNode;
+	 	}
+	 	
+	 	return false;
+	 },
+
+	/**
+	 * Function: getChildNodes
+	 * 
+	 * Returns an array of child nodes that are of the given node type.
+	 * 
+	 * Parameters:
+	 * 
+	 * node - Parent DOM node to return the children from.
+	 * nodeType - Optional node type to return. Default is
+	 * <mxConstants.NODETYPE_ELEMENT>.
+	 */
+	getChildNodes: function(node, nodeType)
+	{
+		nodeType = nodeType || mxConstants.NODETYPE_ELEMENT;
+		
+		var children = [];
+		var tmp = node.firstChild;
+		
+		while (tmp != null)
+		{
+			if (tmp.nodeType == nodeType)
+			{
+				children.push(tmp);
+			}
+			
+			tmp = tmp.nextSibling;
+		}
+		
+		return children;
+	},
+
+	/**
+	 * Function: importNode
+	 * 
+	 * Cross browser implementation for document.importNode. Uses document.importNode
+	 * in all browsers but IE, where the node is cloned by creating a new node and
+	 * copying all attributes and children into it using importNode, recursively.
+	 * 
+	 * Parameters:
+	 * 
+	 * doc - Document to import the node into.
+	 * node - Node to be imported.
+	 * allChildren - If all children should be imported.
+	 */
+	importNode: function(doc, node, allChildren)
+	{
+		if (mxClient.IS_IE && (document.documentMode == null || document.documentMode < 10))
+		{
+			switch (node.nodeType)
+			{
+				case 1: /* element */
+				{
+					var newNode = doc.createElement(node.nodeName);
+					
+					if (node.attributes && node.attributes.length > 0)
+					{
+						for (var i = 0; i < node.attributes.length; i++)
+						{
+							newNode.setAttribute(node.attributes[i].nodeName,
+								node.getAttribute(node.attributes[i].nodeName));
+						}
+						
+						if (allChildren && node.childNodes && node.childNodes.length > 0)
+						{
+							for (var i = 0; i < node.childNodes.length; i++)
+							{
+								newNode.appendChild(mxUtils.importNode(doc, node.childNodes[i], allChildren));
+							}
+						}
+					}
+					
+					return newNode;
+					break;
+				}
+				case 3: /* text */
+			    case 4: /* cdata-section */
+			    case 8: /* comment */
+			    {
+			      return doc.createTextNode(node.value);
+			      break;
+			    }
+			};
+		}
+		else
+		{
+			return doc.importNode(node, allChildren);
+		}
+	},
+
+	/**
+	 * Function: createXmlDocument
+	 * 
+	 * Returns a new, empty XML document.
+	 */
+	createXmlDocument: function()
+	{
+		var doc = null;
+		
+		if (document.implementation && document.implementation.createDocument)
+		{
+			doc = document.implementation.createDocument('', '', null);
+		}
+		else if (window.ActiveXObject)
+		{
+			doc = new ActiveXObject('Microsoft.XMLDOM');
+	 	}
+	 	
+	 	return doc;
+	},
+
+	/**
+	 * Function: parseXml
+	 * 
+	 * Parses the specified XML string into a new XML document and returns the
+	 * new document.
+	 * 
+	 * Example:
+	 * 
+	 * (code)
+	 * var doc = mxUtils.parseXml(
+	 *   '<mxGraphModel><root><MyDiagram id="0"><mxCell/></MyDiagram>'+
+	 *   '<MyLayer id="1"><mxCell parent="0" /></MyLayer><MyObject id="2">'+
+	 *   '<mxCell style="strokeColor=blue;fillColor=red" parent="1" vertex="1">'+
+	 *   '<mxGeometry x="10" y="10" width="80" height="30" as="geometry"/>'+
+	 *   '</mxCell></MyObject></root></mxGraphModel>');
+	 * (end)
+	 * 
+	 * Parameters:
+	 * 
+	 * xml - String that contains the XML data.
+	 */
+	parseXml: function()
+	{
+		if (window.DOMParser)
+		{
+			return function(xml)
+			{
+				var parser = new DOMParser();
+				
+				return parser.parseFromString(xml, 'text/xml');
+			};
+		}
+		else // IE<=9
+		{
+			return function(xml)
+			{
+				var result = mxUtils.createXmlDocument();
+				result.async = false;
+				// Workaround for parsing errors with SVG DTD
+				result.validateOnParse = false;
+				result.resolveExternals = false;
+				result.loadXML(xml);
+				
+				return result;
+			};
+		}
+	}(),
+
+	/**
+	 * Function: clearSelection
+	 * 
+	 * Clears the current selection in the page.
+	 */
+	clearSelection: function()
+	{
+		if (document.selection)
+		{
+			return function()
+			{
+				document.selection.empty();
+			};
+		}
+		else if (window.getSelection)
+		{
+			return function()
+			{
+				window.getSelection().removeAllRanges();
+			};
+		}
+		else
+		{
+			return function() { };
+		}
+	}(),
+
+	/**
+	 * Function: getPrettyXML
+	 * 
+	 * Returns a pretty printed string that represents the XML tree for the
+	 * given node. This method should only be used to print XML for reading,
+	 * use <getXml> instead to obtain a string for processing.
+	 * 
+	 * Parameters:
+	 * 
+	 * node - DOM node to return the XML for.
+	 * tab - Optional string that specifies the indentation for one level.
+	 * Default is two spaces.
+	 * indent - Optional string that represents the current indentation.
+	 * Default is an empty string.
+	 */
+	getPrettyXml: function(node, tab, indent)
+	{
+		var result = [];
+		
+		if (node != null)
+		{
+			tab = tab || '  ';
+			indent = indent || '';
+			
+			if (node.nodeType == mxConstants.NODETYPE_TEXT)
+			{
+				result.push(node.value);
+			}
+			else
+			{
+				result.push(indent + '<' + node.nodeName);
+				
+				// Creates the string with the node attributes
+				// and converts all HTML entities in the values
+				var attrs = node.attributes;
+				
+				if (attrs != null)
+				{
+					for (var i = 0; i < attrs.length; i++)
+					{
+						var val = mxUtils.htmlEntities(attrs[i].value);
+						result.push(' ' + attrs[i].nodeName + '="' + val + '"');
+					}
+				}
+
+				// Recursively creates the XML string for each
+				// child nodes and appends it here with an
+				// indentation
+				var tmp = node.firstChild;
+				
+				if (tmp != null)
+				{
+					result.push('>\n');
+					
+					while (tmp != null)
+					{
+						result.push(mxUtils.getPrettyXml(tmp, tab, indent + tab));
+						tmp = tmp.nextSibling;
+					}
+					
+					result.push(indent + '</'+node.nodeName + '>\n');
+				}
+				else
+				{
+					result.push('/>\n');
+				}
+			}
+		}
+		
+		return result.join('');
+	},
+	
+	/**
+	 * Function: removeWhitespace
+	 * 
+	 * Removes the sibling text nodes for the given node that only consists
+	 * of tabs, newlines and spaces.
+	 * 
+	 * Parameters:
+	 * 
+	 * node - DOM node whose siblings should be removed.
+	 * before - Optional boolean that specifies the direction of the traversal.
+	 */
+	removeWhitespace: function(node, before)
+	{
+		var tmp = (before) ? node.previousSibling : node.nextSibling;
+		
+		while (tmp != null && tmp.nodeType == mxConstants.NODETYPE_TEXT)
+		{
+			var next = (before) ? tmp.previousSibling : tmp.nextSibling;
+			var text = mxUtils.getTextContent(tmp);
+			
+			if (mxUtils.trim(text).length == 0)
+			{
+				tmp.parentNode.removeChild(tmp);
+			}
+			
+			tmp = next;
+		}
+	},
+	
+	/**
+	 * Function: htmlEntities
+	 * 
+	 * Replaces characters (less than, greater than, newlines and quotes) with
+	 * their HTML entities in the given string and returns the result.
+	 * 
+	 * Parameters:
+	 * 
+	 * s - String that contains the characters to be converted.
+	 * newline - If newlines should be replaced. Default is true.
+	 */
+	htmlEntities: function(s, newline)
+	{
+		s = String(s || '');
+		
+		s = s.replace(/&/g,'&amp;'); // 38 26
+		s = s.replace(/"/g,'&quot;'); // 34 22
+		s = s.replace(/\'/g,'&#39;'); // 39 27
+		s = s.replace(/</g,'&lt;'); // 60 3C
+		s = s.replace(/>/g,'&gt;'); // 62 3E
+
+		if (newline == null || newline)
+		{
+			s = s.replace(/\n/g, '&#xa;');
+		}
+		
+		return s;
+	},
+	
+	/**
+	 * Function: isVml
+	 * 
+	 * Returns true if the given node is in the VML namespace.
+	 * 
+	 * Parameters:
+	 * 
+	 * node - DOM node whose tag urn should be checked.
+	 */
+	isVml: function(node)
+	{
+		return node != null && node.tagUrn == 'urn:schemas-microsoft-com:vml';
+	},
+
+	/**
+	 * Function: getXml
+	 * 
+	 * Returns the XML content of the specified node. For Internet Explorer,
+	 * all \r\n\t[\t]* are removed from the XML string and the remaining \r\n
+	 * are replaced by \n. All \n are then replaced with linefeed, or &#xa; if
+	 * no linefeed is defined.
+	 * 
+	 * Parameters:
+	 * 
+	 * node - DOM node to return the XML for.
+	 * linefeed - Optional string that linefeeds are converted into. Default is
+	 * &#xa;
+	 */
+	getXml: function(node, linefeed)
+	{
+		var xml = '';
+
+		if (window.XMLSerializer != null)
+		{
+			var xmlSerializer = new XMLSerializer();
+			xml = xmlSerializer.serializeToString(node);     
+		}
+		else if (node.xml != null)
+		{
+			xml = node.xml.replace(/\r\n\t[\t]*/g, '').
+				replace(/>\r\n/g, '>').
+				replace(/\r\n/g, '\n');
+		}
+
+		// Replaces linefeeds with HTML Entities.
+		linefeed = linefeed || '&#xa;';
+		xml = xml.replace(/\n/g, linefeed);
+		  
+		return xml;
+	},
+	
+	/**
+	 * Function: extractTextWithWhitespace
+	 * 
+	 * Returns the text content of the specified node.
+	 * 
+	 * Parameters:
+	 * 
+	 * elems - DOM nodes to return the text for.
+	 */
+	extractTextWithWhitespace: function(elems)
+	{
+	    // Known block elements for handling linefeeds (list is not complete)
+		var blocks = ['BLOCKQUOTE', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'OL', 'P', 'PRE', 'TABLE', 'UL'];
+		var ret = [];
+		
+		function doExtract(elts)
+		{
+			// Single break should be ignored
+			if (elts.length == 1 && (elts[0].nodeName == 'BR' ||
+				elts[0].innerHTML == '\n'))
+			{
+				return;
+			}
+			
+		    for (var i = 0; i < elts.length; i++)
+		    {
+		        var elem = elts[i];
+
+				// DIV with a br or linefeed forces a linefeed
+				if (elem.nodeName == 'BR' || elem.innerHTML == '\n' ||
+					((elts.length == 1 || i == 0) && (elem.nodeName == 'DIV' &&
+					elem.innerHTML.toLowerCase() == '<br>')))
+		    	{
+	    			ret.push('\n');
+		    	}
+				else
+				{
+			        if (elem.nodeType === 3 || elem.nodeType === 4)
+			        {
+			        	if (elem.nodeValue.length > 0)
+			        	{
+			        		ret.push(elem.nodeValue);
+			        	}
+			        }
+			        else if (elem.nodeType !== 8 && elem.childNodes.length > 0)
+					{
+						doExtract(elem.childNodes);
+					}
+			        
+	        		if (i < elts.length - 1 && mxUtils.indexOf(blocks, elts[i + 1].nodeName) >= 0)
+	        		{
+	        			ret.push('\n');		
+	        		}
+				}
+		    }
+		};
+		
+		doExtract(elems);
+	    
+	    return ret.join('');
+	},
+
+	/**
+	 * Function: replaceTrailingNewlines
+	 * 
+	 * Replaces each trailing newline with the given pattern.
+	 */
+	replaceTrailingNewlines: function(str, pattern)
+	{
+		// LATER: Check is this can be done with a regular expression
+		var postfix = '';
+		
+		while (str.length > 0 && str.charAt(str.length - 1) == '\n')
+		{
+			str = str.substring(0, str.length - 1);
+			postfix += pattern;
+		}
+		
+		return str + postfix;
+	},
+
+	/**
+	 * Function: getTextContent
+	 * 
+	 * Returns the text content of the specified node.
+	 * 
+	 * Parameters:
+	 * 
+	 * node - DOM node to return the text content for.
+	 */
+	getTextContent: function(node)
+	{
+		if (node.innerText !== undefined)
+		{
+			return node.innerText;
+		}
+		else
+		{
+			return (node != null) ? node[(node.textContent === undefined) ? 'text' : 'textContent'] : '';
+		}
+	},
+	
+	/**
+	 * Function: setTextContent
+	 * 
+	 * Sets the text content of the specified node.
+	 * 
+	 * Parameters:
+	 * 
+	 * node - DOM node to set the text content for.
+	 * text - String that represents the text content.
+	 */
+	setTextContent: function(node, text)
+	{
+		if (node.innerText !== undefined)
+		{
+			node.innerText = text;
+		}
+		else
+		{
+			node[(node.textContent === undefined) ? 'text' : 'textContent'] = text;
+		}
+	},
+	
+	/**
+	 * Function: getInnerHtml
+	 * 
+	 * Returns the inner HTML for the given node as a string or an empty string
+	 * if no node was specified. The inner HTML is the text representing all
+	 * children of the node, but not the node itself.
+	 * 
+	 * Parameters:
+	 * 
+	 * node - DOM node to return the inner HTML for.
+	 */
+	getInnerHtml: function()
+	{
+		if (mxClient.IS_IE)
+		{
+			return function(node)
+			{
+				if (node != null)
+				{
+					return node.innerHTML;
+				}
+				
+				return '';
+			};
+		}
+		else
+		{
+			return function(node)
+			{
+				if (node != null)
+				{
+					var serializer = new XMLSerializer();
+					return serializer.serializeToString(node);
+				}
+				
+				return '';
+			};
+		}
+	}(),
+
+	/**
+	 * Function: getOuterHtml
+	 * 
+	 * Returns the outer HTML for the given node as a string or an empty
+	 * string if no node was specified. The outer HTML is the text representing
+	 * all children of the node including the node itself.
+	 * 
+	 * Parameters:
+	 * 
+	 * node - DOM node to return the outer HTML for.
+	 */
+	getOuterHtml: function()
+	{
+		if (mxClient.IS_IE)
+		{
+			return function(node)
+			{
+				if (node != null)
+				{
+					if (node.outerHTML != null)
+					{
+						return node.outerHTML;
+					}
+					else
+					{
+						var tmp = [];
+						tmp.push('<'+node.nodeName);
+						
+						var attrs = node.attributes;
+						
+						if (attrs != null)
+						{
+							for (var i = 0; i < attrs.length; i++)
+							{
+								var value = attrs[i].value;
+								
+								if (value != null && value.length > 0)
+								{
+									tmp.push(' ');
+									tmp.push(attrs[i].nodeName);
+									tmp.push('="');
+									tmp.push(value);
+									tmp.push('"');
+								}
+							}
+						}
+						
+						if (node.innerHTML.length == 0)
+						{
+							tmp.push('/>');
+						}
+						else
+						{
+							tmp.push('>');
+							tmp.push(node.innerHTML);
+							tmp.push('</'+node.nodeName+'>');
+						}
+						
+						return tmp.join('');
+					}
+				}
+				
+				return '';
+			};
+		}
+		else
+		{
+			return function(node)
+			{
+				if (node != null)
+				{
+					var serializer = new XMLSerializer();
+					return serializer.serializeToString(node);
+				}
+				
+				return '';
+			};
+		}
+	}(),
+	
+	/**
+	 * Function: write
+	 * 
+	 * Creates a text node for the given string and appends it to the given
+	 * parent. Returns the text node.
+	 * 
+	 * Parameters:
+	 * 
+	 * parent - DOM node to append the text node to.
+	 * text - String representing the text to be added.
+	 */
+	write: function(parent, text)
+	{
+		var doc = parent.ownerDocument;
+		var node = doc.createTextNode(text);
+		
+		if (parent != null)
+		{
+			parent.appendChild(node);
+		}
+		
+		return node;
+	},
+	
+	/**
+	 * Function: writeln
+	 * 
+	 * Creates a text node for the given string and appends it to the given
+	 * parent with an additional linefeed. Returns the text node.
+	 * 
+	 * Parameters:
+	 * 
+	 * parent - DOM node to append the text node to.
+	 * text - String representing the text to be added.
+	 */
+	writeln: function(parent, text)
+	{
+		var doc = parent.ownerDocument;
+		var node = doc.createTextNode(text);
+		
+		if (parent != null)
+		{
+			parent.appendChild(node);
+			parent.appendChild(document.createElement('br'));
+		}
+		
+		return node;
+	},
+	
+	/**
+	 * Function: br
+	 * 
+	 * Appends a linebreak to the given parent and returns the linebreak.
+	 * 
+	 * Parameters:
+	 * 
+	 * parent - DOM node to append the linebreak to.
+	 */
+	br: function(parent, count)
+	{
+		count = count || 1;
+		var br = null;
+		
+		for (var i = 0; i < count; i++)
+		{
+			if (parent != null)
+			{
+				br = parent.ownerDocument.createElement('br');
+				parent.appendChild(br);
+			}
+		}
+		
+		return br;
+	},
+		
+	/**
+	 * Function: button
+	 * 
+	 * Returns a new button with the given level and function as an onclick
+	 * event handler.
+	 * 
+	 * (code)
+	 * document.body.appendChild(mxUtils.button('Test', function(evt)
+	 * {
+	 *   alert('Hello, World!');
+	 * }));
+	 * (end)
+	 * 
+	 * Parameters:
+	 * 
+	 * label - String that represents the label of the button.
+	 * funct - Function to be called if the button is pressed.
+	 * doc - Optional document to be used for creating the button. Default is the
+	 * current document.
+	 */
+	button: function(label, funct, doc)
+	{
+		doc = (doc != null) ? doc : document;
+		
+		var button = doc.createElement('button');
+		mxUtils.write(button, label);
+
+		mxEvent.addListener(button, 'click', function(evt)
+		{
+			funct(evt);
+		});
+		
+		return button;
+	},
+	
+	/**
+	 * Function: para
+	 * 
+	 * Appends a new paragraph with the given text to the specified parent and
+	 * returns the paragraph.
+	 * 
+	 * Parameters:
+	 * 
+	 * parent - DOM node to append the text node to.
+	 * text - String representing the text for the new paragraph.
+	 */
+	para: function(parent, text)
+	{
+		var p = document.createElement('p');
+		mxUtils.write(p, text);
+
+		if (parent != null)
+		{
+			parent.appendChild(p);
+		}
+		
+		return p;
+	},
+
+	/**
+	 * Function: addTransparentBackgroundFilter
+	 * 
+	 * Adds a transparent background to the filter of the given node. This
+	 * background can be used in IE8 standards mode (native IE8 only) to pass
+	 * events through the node.
+	 */
+	addTransparentBackgroundFilter: function(node)
+	{
+		node.style.filter += 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' +
+			mxClient.imageBasePath + '/transparent.gif\', sizingMethod=\'scale\')';
+	},
+
+	/**
+	 * Function: linkAction
+	 * 
+	 * Adds a hyperlink to the specified parent that invokes action on the
+	 * specified editor.
+	 * 
+	 * Parameters:
+	 * 
+	 * parent - DOM node to contain the new link.
+	 * text - String that is used as the link label.
+	 * editor - <mxEditor> that will execute the action.
+	 * action - String that defines the name of the action to be executed.
+	 * pad - Optional left-padding for the link. Default is 0.
+	 */
+	linkAction: function(parent, text, editor, action, pad)
+	{
+		return mxUtils.link(parent, text, function()
+		{
+			editor.execute(action);
+		}, pad);
+	},
+
+	/**
+	 * Function: linkInvoke
+	 * 
+	 * Adds a hyperlink to the specified parent that invokes the specified
+	 * function on the editor passing along the specified argument. The
+	 * function name is the name of a function of the editor instance,
+	 * not an action name.
+	 * 
+	 * Parameters:
+	 * 
+	 * parent - DOM node to contain the new link.
+	 * text - String that is used as the link label.
+	 * editor - <mxEditor> instance to execute the function on.
+	 * functName - String that represents the name of the function.
+	 * arg - Object that represents the argument to the function.
+	 * pad - Optional left-padding for the link. Default is 0.
+	 */
+	linkInvoke: function(parent, text, editor, functName, arg, pad)
+	{
+		return mxUtils.link(parent, text, function()
+		{
+			editor[functName](arg);
+		}, pad);
+	},
+	
+	/**
+	 * Function: link
+	 * 
+	 * Adds a hyperlink to the specified parent and invokes the given function
+	 * when the link is clicked.
+	 * 
+	 * Parameters:
+	 * 
+	 * parent - DOM node to contain the new link.
+	 * text - String that is used as the link label.
+	 * funct - Function to execute when the link is clicked.
+	 * pad - Optional left-padding for the link. Default is 0.
+	 */
+	link: function(parent, text, funct, pad)
+	{
+		var a = document.createElement('span');
+		
+		a.style.color = 'blue';
+		a.style.textDecoration = 'underline';
+		a.style.cursor = 'pointer';
+		
+		if (pad != null)
+		{
+			a.style.paddingLeft = pad+'px';
+		}
+		
+		mxEvent.addListener(a, 'click', funct);
+		mxUtils.write(a, text);
+		
+		if (parent != null)
+		{
+			parent.appendChild(a);
+		}
+		
+		return a;
+	},
+
+	/**
+	 * Function: fit
+	 * 
+	 * Makes sure the given node is inside the visible area of the window. This
+	 * is done by setting the left and top in the style. 
+	 */
+	fit: function(node)
+	{
+		var left = parseInt(node.offsetLeft);
+		var width = parseInt(node.offsetWidth);
+			
+		var offset = mxUtils.getDocumentScrollOrigin(node.ownerDocument);
+		var sl = offset.x;
+		var st = offset.y;
+
+		var b = document.body;
+		var d = document.documentElement;
+		var right = (sl) + (b.clientWidth || d.clientWidth);
+		
+		if (left + width > right)
+		{
+			node.style.left = Math.max(sl, right - width) + 'px';
+		}
+		
+		var top = parseInt(node.offsetTop);
+		var height = parseInt(node.offsetHeight);
+		
+		var bottom = st + Math.max(b.clientHeight || 0, d.clientHeight);
+		
+		if (top + height > bottom)
+		{
+			node.style.top = Math.max(st, bottom - height) + 'px';
+		}
+	},
+
+	/**
+	 * Function: load
+	 * 
+	 * Loads the specified URL *synchronously* and returns the <mxXmlRequest>.
+	 * Throws an exception if the file cannot be loaded. See <mxUtils.get> for
+	 * an asynchronous implementation.
+	 *
+	 * Example:
+	 * 
+	 * (code)
+	 * try
+	 * {
+	 *   var req = mxUtils.load(filename);
+	 *   var root = req.getDocumentElement();
+	 *   // Process XML DOM...
+	 * }
+	 * catch (ex)
+	 * {
+	 *   mxUtils.alert('Cannot load '+filename+': '+ex);
+	 * }
+	 * (end)
+	 * 
+	 * Parameters:
+	 * 
+	 * url - URL to get the data from.
+	 */
+	load: function(url)
+	{
+		var req = new mxXmlRequest(url, null, 'GET', false);
+		req.send();
+		
+		return req;
+	},
+
+	/**
+	 * Function: get
+	 * 
+	 * Loads the specified URL *asynchronously* and invokes the given functions
+	 * depending on the request status. Returns the <mxXmlRequest> in use. Both
+	 * functions take the <mxXmlRequest> as the only parameter. See
+	 * <mxUtils.load> for a synchronous implementation.
+	 *
+	 * Example:
+	 * 
+	 * (code)
+	 * mxUtils.get(url, function(req)
+	 * {
+	 *    var node = req.getDocumentElement();
+	 *    // Process XML DOM...
+	 * });
+	 * (end)
+	 * 
+	 * So for example, to load a diagram into an existing graph model, the
+	 * following code is used.
+	 * 
+	 * (code)
+	 * mxUtils.get(url, function(req)
+	 * {
+	 *   var node = req.getDocumentElement();
+	 *   var dec = new mxCodec(node.ownerDocument);
+	 *   dec.decode(node, graph.getModel());
+	 * });
+	 * (end)
+	 * 
+	 * Parameters:
+	 * 
+	 * url - URL to get the data from.
+	 * onload - Optional function to execute for a successful response.
+	 * onerror - Optional function to execute on error.
+	 * binary - Optional boolean parameter that specifies if the request is
+	 * binary.
+	 * timeout - Optional timeout in ms before calling ontimeout.
+	 * ontimeout - Optional function to execute on timeout.
+	 */
+	get: function(url, onload, onerror, binary, timeout, ontimeout)
+	{
+		var req = new mxXmlRequest(url, null, 'GET');
+		
+		if (binary != null)
+		{
+			req.setBinary(binary);
+		}
+		
+		req.send(onload, onerror, timeout, ontimeout);
+		
+		return req;
+	},
+
+	/**
+	 * Function: getAll
+	 * 
+	 * Loads the URLs in the given array *asynchronously* and invokes the given function
+	 * if all requests returned with a valid 2xx status. The error handler is invoked
+	 * once on the first error or invalid response.
+	 *
+	 * Parameters:
+	 * 
+	 * urls - Array of URLs to be loaded.
+	 * onload - Callback with array of <mxXmlRequests>.
+	 * onerror - Optional function to execute on error.
+	 */
+	getAll: function(urls, onload, onerror)
+	{
+		var remain = urls.length;
+		var result = [];
+		var errors = 0;
+		var err = function()
+		{
+			if (errors == 0 && onerror != null)
+			{
+				onerror();
+			}
+
+			errors++;
+		};
+		
+		for (var i = 0; i < urls.length; i++)
+		{
+			(function(url, index)
+			{
+				mxUtils.get(url, function(req)
+				{
+					var status = req.getStatus();
+					
+					if (status < 200 || status > 299)
+					{
+						err();
+					}
+					else
+					{
+						result[index] = req;
+						remain--;
+						
+						if (remain == 0)
+						{
+							onload(result);
+						}
+					}
+				}, err);
+			})(urls[i], i);
+		}
+		
+		if (remain == 0)
+		{
+			onload(result);			
+		}
+	},
+	
+	/**
+	 * Function: post
+	 * 
+	 * Posts the specified params to the given URL *asynchronously* and invokes
+	 * the given functions depending on the request status. Returns the
+	 * <mxXmlRequest> in use. Both functions take the <mxXmlRequest> as the
+	 * only parameter. Make sure to use encodeURIComponent for the parameter
+	 * values.
+	 *
+	 * Example:
+	 * 
+	 * (code)
+	 * mxUtils.post(url, 'key=value', function(req)
+	 * {
+	 * 	mxUtils.alert('Ready: '+req.isReady()+' Status: '+req.getStatus());
+	 *  // Process req.getDocumentElement() using DOM API if OK...
+	 * });
+	 * (end)
+	 * 
+	 * Parameters:
+	 * 
+	 * url - URL to get the data from.
+	 * params - Parameters for the post request.
+	 * onload - Optional function to execute for a successful response.
+	 * onerror - Optional function to execute on error.
+	 */
+	post: function(url, params, onload, onerror)
+	{
+		return new mxXmlRequest(url, params).send(onload, onerror);
+	},
+	
+	/**
+	 * Function: submit
+	 * 
+	 * Submits the given parameters to the specified URL using
+	 * <mxXmlRequest.simulate> and returns the <mxXmlRequest>.
+	 * Make sure to use encodeURIComponent for the parameter
+	 * values.
+	 * 
+	 * Parameters:
+	 * 
+	 * url - URL to get the data from.
+	 * params - Parameters for the form.
+	 * doc - Document to create the form in.
+	 * target - Target to send the form result to.
+	 */
+	submit: function(url, params, doc, target)
+	{
+		return new mxXmlRequest(url, params).simulate(doc, target);
+	},
+	
+	/**
+	 * Function: loadInto
+	 * 
+	 * Loads the specified URL *asynchronously* into the specified document,
+	 * invoking onload after the document has been loaded. This implementation
+	 * does not use <mxXmlRequest>, but the document.load method.
+	 * 
+	 * Parameters:
+	 * 
+	 * url - URL to get the data from.
+	 * doc - The document to load the URL into.
+	 * onload - Function to execute when the URL has been loaded.
+	 */
+	loadInto: function(url, doc, onload)
+	{
+		if (mxClient.IS_IE)
+		{
+			doc.onreadystatechange = function ()
+			{
+				if (doc.readyState == 4)
+				{
+					onload();
+				}
+			};
+		}
+		else
+		{
+			doc.addEventListener('load', onload, false);
+		}
+		
+		doc.load(url);
+	},
+	
+	/**
+	 * Function: getValue
+	 * 
+	 * Returns the value for the given key in the given associative array or
+	 * the given default value if the value is null.
+	 * 
+	 * Parameters:
+	 * 
+	 * array - Associative array that contains the value for the key.
+	 * key - Key whose value should be returned.
+	 * defaultValue - Value to be returned if the value for the given
+	 * key is null.
+	 */
+	getValue: function(array, key, defaultValue)
+	{
+		var value = (array != null) ? array[key] : null;
+
+		if (value == null)
+		{
+			value = defaultValue;			
+		}
+		
+		return value;
+	},
+	
+	/**
+	 * Function: getNumber
+	 * 
+	 * Returns the numeric value for the given key in the given associative
+	 * array or the given default value (or 0) if the value is null. The value
+	 * is converted to a numeric value using the Number function.
+	 * 
+	 * Parameters:
+	 * 
+	 * array - Associative array that contains the value for the key.
+	 * key - Key whose value should be returned.
+	 * defaultValue - Value to be returned if the value for the given
+	 * key is null. Default is 0.
+	 */
+	getNumber: function(array, key, defaultValue)
+	{
+		var value = (array != null) ? array[key] : null;
+
+		if (value == null)
+		{
+			value = defaultValue || 0;			
+		}
+		
+		return Number(value);
+	},
+	
+	/**
+	 * Function: getColor
+	 * 
+	 * Returns the color value for the given key in the given associative
+	 * array or the given default value if the value is null. If the value
+	 * is <mxConstants.NONE> then null is returned.
+	 * 
+	 * Parameters:
+	 * 
+	 * array - Associative array that contains the value for the key.
+	 * key - Key whose value should be returned.
+	 * defaultValue - Value to be returned if the value for the given
+	 * key is null. Default is null.
+	 */
+	getColor: function(array, key, defaultValue)
+	{
+		var value = (array != null) ? array[key] : null;
+
+		if (value == null)
+		{
+			value = defaultValue;
+		}
+		else if (value == mxConstants.NONE)
+		{
+			value = null;
+		}
+		
+		return value;
+	},
+
+	/**
+	 * Function: clone
+	 * 
+	 * Recursively clones the specified object ignoring all fieldnames in the
+	 * given array of transient fields. <mxObjectIdentity.FIELD_NAME> is always
+	 * ignored by this function.
+	 * 
+	 * Parameters:
+	 * 
+	 * obj - Object to be cloned.
+	 * transients - Optional array of strings representing the fieldname to be
+	 * ignored.
+	 * shallow - Optional boolean argument to specify if a shallow clone should
+	 * be created, that is, one where all object references are not cloned or,
+	 * in other words, one where only atomic (strings, numbers) values are
+	 * cloned. Default is false.
+	 */
+	clone: function(obj, transients, shallow)
+	{
+		shallow = (shallow != null) ? shallow : false;
+		var clone = null;
+		
+		if (obj != null && typeof(obj.constructor) == 'function')
+		{
+			clone = new obj.constructor();
+			
+		    for (var i in obj)
+		    {
+		    	if (i != mxObjectIdentity.FIELD_NAME && (transients == null ||
+		    		mxUtils.indexOf(transients, i) < 0))
+		    	{
+			    	if (!shallow && typeof(obj[i]) == 'object')
+			    	{
+			            clone[i] = mxUtils.clone(obj[i]);
+			        }
+			        else
+			        {
+			            clone[i] = obj[i];
+			        }
+				}
+		    }
+		}
+		
+	    return clone;
+	},
+
+	/**
+	 * Function: equalPoints
+	 * 
+	 * Compares all mxPoints in the given lists.
+	 * 
+	 * Parameters:
+	 * 
+	 * a - Array of <mxPoints> to be compared.
+	 * b - Array of <mxPoints> to be compared.
+	 */
+	equalPoints: function(a, b)
+	{
+		if ((a == null && b != null) || (a != null && b == null) ||
+			(a != null && b != null && a.length != b.length))
+		{
+			return false;
+		}
+		else if (a != null && b != null)
+		{
+			for (var i = 0; i < a.length; i++)
+			{
+				if (a[i] == b[i] || (a[i] != null && !a[i].equals(b[i])))
+				{
+					return false;
+				}
+			}
+		}
+		
+		return true;
+	},
+
+	/**
+	 * Function: equalEntries
+	 * 
+	 * Returns true if all properties of the given objects are equal. Values
+	 * with NaN are equal to NaN and unequal to any other value.
+	 * 
+	 * Parameters:
+	 * 
+	 * a - First object to be compared.
+	 * b - Second object to be compared.
+	 */
+	equalEntries: function(a, b)
+	{
+		if ((a == null && b != null) || (a != null && b == null) ||
+			(a != null && b != null && a.length != b.length))
+		{
+			return false;
+		}
+		else if (a != null && b != null)
+		{
+			// Counts keys in b to check if all values have been compared
+			var count = 0;
+			
+			for (var key in b)
+			{
+				count++;
+			}
+			
+			for (var key in a)
+			{
+				count--
+				
+				if ((!mxUtils.isNaN(a[key]) || !mxUtils.isNaN(b[key])) && a[key] != b[key])
+				{
+					return false;
+				}
+			}
+		}
+		
+		return count == 0;
+	},
+	
+	/**
+	 * Function: removeDuplicates
+	 * 
+	 * Removes all duplicates from the given array.
+	 */
+	removeDuplicates: function(arr)
+	{
+		var dict = new mxDictionary();
+		var result = [];
+		
+		for (var i = 0; i < arr.length; i++)
+		{
+			if (!dict.get(arr[i]))
+			{
+				result.push(arr[i]);
+				dict.put(arr[i], true);
+			}
+		}
+
+		return result;
+	},
+	
+	/**
+	 * Function: isNaN
+	 *
+	 * Returns true if the given value is of type number and isNaN returns true.
+	 */
+	isNaN: function(value)
+	{
+		return typeof(value) == 'number' && isNaN(value);
+	},
+	
+	/**
+	 * Function: extend
+	 *
+	 * Assigns a copy of the superclass prototype to the subclass prototype.
+	 * Note that this does not call the constructor of the superclass at this
+	 * point, the superclass constructor should be called explicitely in the
+	 * subclass constructor. Below is an example.
+	 * 
+	 * (code)
+	 * MyGraph = function(container, model, renderHint, stylesheet)
+	 * {
+	 *   mxGraph.call(this, container, model, renderHint, stylesheet);
+	 * }
+	 * 
+	 * mxUtils.extend(MyGraph, mxGraph);
+	 * (end)
+	 * 
+	 * Parameters:
+	 * 
+	 * ctor - Constructor of the subclass.
+	 * superCtor - Constructor of the superclass.
+	 */
+	extend: function(ctor, superCtor)
+	{
+		var f = function() {};
+		f.prototype = superCtor.prototype;
+		
+		ctor.prototype = new f();
+		ctor.prototype.constructor = ctor;
+	},
+
+	/**
+	 * Function: toString
+	 * 
+	 * Returns a textual representation of the specified object.
+	 * 
+	 * Parameters:
+	 * 
+	 * obj - Object to return the string representation for.
+	 */
+	toString: function(obj)
+	{
+	    var output = '';
+	    
+	    for (var i in obj)
+	    {
+	    	try
+	    	{
+			    if (obj[i] == null)
+			    {
+		            output += i + ' = [null]\n';
+			    }
+			    else if (typeof(obj[i]) == 'function')
+			    {
+		            output += i + ' => [Function]\n';
+		        }
+		        else if (typeof(obj[i]) == 'object')
+		        {
+		        	var ctor = mxUtils.getFunctionName(obj[i].constructor); 
+		            output += i + ' => [' + ctor + ']\n';
+		        }
+		        else
+		        {
+		            output += i + ' = ' + obj[i] + '\n';
+		        }
+	    	}
+	    	catch (e)
+	    	{
+	    		output += i + '=' + e.message;
+	    	}
+	    }
+	    
+	    return output;
+	},
+
+	/**
+	 * Function: toRadians
+	 * 
+	 * Converts the given degree to radians.
+	 */
+	toRadians: function(deg)
+	{
+		return Math.PI * deg / 180;
+	},
+
+	/**
+	 * Function: toDegree
+	 * 
+	 * Converts the given radians to degree.
+	 */
+	toDegree: function(rad)
+	{
+		return rad * 180 / Math.PI;
+	},
+	
+	/**
+	 * Function: arcToCurves
+	 * 
+	 * Converts the given arc to a series of curves.
+	 */
+	arcToCurves: function(x0, y0, r1, r2, angle, largeArcFlag, sweepFlag, x, y)
+	{
+		x -= x0;
+		y -= y0;
+		
+        if (r1 === 0 || r2 === 0) 
+        {
+        	return result;
+        }
+        
+        var fS = sweepFlag;
+        var psai = angle;
+        r1 = Math.abs(r1);
+        r2 = Math.abs(r2);
+        var ctx = -x / 2;
+        var cty = -y / 2;
+        var cpsi = Math.cos(psai * Math.PI / 180);
+        var spsi = Math.sin(psai * Math.PI / 180);
+        var rxd = cpsi * ctx + spsi * cty;
+        var ryd = -1 * spsi * ctx + cpsi * cty;
+        var rxdd = rxd * rxd;
+        var rydd = ryd * ryd;
+        var r1x = r1 * r1;
+        var r2y = r2 * r2;
+        var lamda = rxdd / r1x + rydd / r2y;
+        var sds;
+        
+        if (lamda > 1) 
+        {
+        	r1 = Math.sqrt(lamda) * r1;
+        	r2 = Math.sqrt(lamda) * r2;
+        	sds = 0;
+        }  
+        else
+        {
+        	var seif = 1;
+            
+        	if (largeArcFlag === fS) 
+        	{
+        		seif = -1;
+        	}
+            
+        	sds = seif * Math.sqrt((r1x * r2y - r1x * rydd - r2y * rxdd) / (r1x * rydd + r2y * rxdd));
+        }
+        
+        var txd = sds * r1 * ryd / r2;
+        var tyd = -1 * sds * r2 * rxd / r1;
+        var tx = cpsi * txd - spsi * tyd + x / 2;
+        var ty = spsi * txd + cpsi * tyd + y / 2;
+        var rad = Math.atan2((ryd - tyd) / r2, (rxd - txd) / r1) - Math.atan2(0, 1);
+        var s1 = (rad >= 0) ? rad : 2 * Math.PI + rad;
+        rad = Math.atan2((-ryd - tyd) / r2, (-rxd - txd) / r1) - Math.atan2((ryd - tyd) / r2, (rxd - txd) / r1);
+        var dr = (rad >= 0) ? rad : 2 * Math.PI + rad;
+        
+        if (fS == 0 && dr > 0) 
+        {
+        	dr -= 2 * Math.PI;
+        }
+        else if (fS != 0 && dr < 0) 
+        {
+        	dr += 2 * Math.PI;
+        }
+        
+        var sse = dr * 2 / Math.PI;
+        var seg = Math.ceil(sse < 0 ? -1 * sse : sse);
+        var segr = dr / seg;
+        var t = 8/3 * Math.sin(segr / 4) * Math.sin(segr / 4) / Math.sin(segr / 2);
+        var cpsir1 = cpsi * r1;
+        var cpsir2 = cpsi * r2;
+        var spsir1 = spsi * r1;
+        var spsir2 = spsi * r2;
+        var mc = Math.cos(s1);
+        var ms = Math.sin(s1);
+        var x2 = -t * (cpsir1 * ms + spsir2 * mc);
+        var y2 = -t * (spsir1 * ms - cpsir2 * mc);
+        var x3 = 0;
+        var y3 = 0;
+
+		var result = [];
+        
+        for (var n = 0; n < seg; ++n) 
+        {
+            s1 += segr;
+            mc = Math.cos(s1);
+            ms = Math.sin(s1);
+            
+            x3 = cpsir1 * mc - spsir2 * ms + tx;
+            y3 = spsir1 * mc + cpsir2 * ms + ty;
+            var dx = -t * (cpsir1 * ms + spsir2 * mc);
+            var dy = -t * (spsir1 * ms - cpsir2 * mc);
+            
+            // CurveTo updates x0, y0 so need to restore it
+            var index = n * 6;
+            result[index] = Number(x2 + x0);
+            result[index + 1] = Number(y2 + y0);
+            result[index + 2] = Number(x3 - dx + x0);
+            result[index + 3] = Number(y3 - dy + y0);
+            result[index + 4] = Number(x3 + x0);
+            result[index + 5] = Number(y3 + y0);
+            
+			x2 = x3 + dx;
+            y2 = y3 + dy;
+        }
+        
+        return result;
+	},
+
+	/**
+	 * Function: getBoundingBox
+	 * 
+	 * Returns the bounding box for the rotated rectangle.
+	 * 
+	 * Parameters:
+	 * 
+	 * rect - <mxRectangle> to be rotated.
+	 * angle - Number that represents the angle (in degrees).
+	 * cx - Optional <mxPoint> that represents the rotation center. If no
+	 * rotation center is given then the center of rect is used.
+	 */
+	getBoundingBox: function(rect, rotation, cx)
+	{
+        var result = null;
+
+        if (rect != null && rotation != null && rotation != 0)
+        {
+            var rad = mxUtils.toRadians(rotation);
+            var cos = Math.cos(rad);
+            var sin = Math.sin(rad);
+
+            cx = (cx != null) ? cx : new mxPoint(rect.x + rect.width / 2, rect.y  + rect.height / 2);
+
+            var p1 = new mxPoint(rect.x, rect.y);
+            var p2 = new mxPoint(rect.x + rect.width, rect.y);
+            var p3 = new mxPoint(p2.x, rect.y + rect.height);
+            var p4 = new mxPoint(rect.x, p3.y);
+
+            p1 = mxUtils.getRotatedPoint(p1, cos, sin, cx);
+            p2 = mxUtils.getRotatedPoint(p2, cos, sin, cx);
+            p3 = mxUtils.getRotatedPoint(p3, cos, sin, cx);
+            p4 = mxUtils.getRotatedPoint(p4, cos, sin, cx);
+
+            result = new mxRectangle(p1.x, p1.y, 0, 0);
+            result.add(new mxRectangle(p2.x, p2.y, 0, 0));
+            result.add(new mxRectangle(p3.x, p3.y, 0, 0));
+            result.add(new mxRectangle(p4.x, p4.y, 0, 0));
+        }
+
+        return result;
+	},
+
+	/**
+	 * Function: getRotatedPoint
+	 * 
+	 * Rotates the given point by the given cos and sin.
+	 */
+	getRotatedPoint: function(pt, cos, sin, c)
+	{
+		c = (c != null) ? c : new mxPoint();
+		var x = pt.x - c.x;
+		var y = pt.y - c.y;
+
+		var x1 = x * cos - y * sin;
+		var y1 = y * cos + x * sin;
+
+		return new mxPoint(x1 + c.x, y1 + c.y);
+	},
+	
+	/**
+	 * Returns an integer mask of the port constraints of the given map
+	 * @param dict the style map to determine the port constraints for
+	 * @param defaultValue Default value to return if the key is undefined.
+	 * @return the mask of port constraint directions
+	 * 
+	 * Parameters:
+	 * 
+	 * terminal - <mxCelState> that represents the terminal.
+	 * edge - <mxCellState> that represents the edge.
+	 * source - Boolean that specifies if the terminal is the source terminal.
+	 * defaultValue - Default value to be returned.
+	 */
+	getPortConstraints: function(terminal, edge, source, defaultValue)
+	{
+		var value = mxUtils.getValue(terminal.style, mxConstants.STYLE_PORT_CONSTRAINT,
+			mxUtils.getValue(edge.style, (source) ? mxConstants.STYLE_SOURCE_PORT_CONSTRAINT :
+				mxConstants.STYLE_TARGET_PORT_CONSTRAINT, null));
+		
+		if (value == null)
+		{
+			return defaultValue;
+		}
+		else
+		{
+			var directions = value.toString();
+			var returnValue = mxConstants.DIRECTION_MASK_NONE;
+			var constraintRotationEnabled = mxUtils.getValue(terminal.style, mxConstants.STYLE_PORT_CONSTRAINT_ROTATION, 0);
+			var rotation = 0;
+			
+			if (constraintRotationEnabled == 1)
+			{
+				rotation = mxUtils.getValue(terminal.style, mxConstants.STYLE_ROTATION, 0);
+			}
+			
+			var quad = 0;
+
+			if (rotation > 45)
+			{
+				quad = 1;
+				
+				if (rotation >= 135)
+				{
+					quad = 2;
+				}
+			}
+			else if (rotation < -45)
+			{
+				quad = 3;
+				
+				if (rotation <= -135)
+				{
+					quad = 2;
+				}
+			}
+
+			if (directions.indexOf(mxConstants.DIRECTION_NORTH) >= 0)
+			{
+				switch (quad)
+				{
+					case 0:
+						returnValue |= mxConstants.DIRECTION_MASK_NORTH;
+						break;
+					case 1:
+						returnValue |= mxConstants.DIRECTION_MASK_EAST;
+						break;
+					case 2:
+						returnValue |= mxConstants.DIRECTION_MASK_SOUTH;
+						break;
+					case 3:
+						returnValue |= mxConstants.DIRECTION_MASK_WEST;
+						break;
+				}
+			}
+			if (directions.indexOf(mxConstants.DIRECTION_WEST) >= 0)
+			{
+				switch (quad)
+				{
+					case 0:
+						returnValue |= mxConstants.DIRECTION_MASK_WEST;
+						break;
+					case 1:
+						returnValue |= mxConstants.DIRECTION_MASK_NORTH;
+						break;
+					case 2:
+						returnValue |= mxConstants.DIRECTION_MASK_EAST;
+						break;
+					case 3:
+						returnValue |= mxConstants.DIRECTION_MASK_SOUTH;
+						break;
+				}
+			}
+			if (directions.indexOf(mxConstants.DIRECTION_SOUTH) >= 0)
+			{
+				switch (quad)
+				{
+					case 0:
+						returnValue |= mxConstants.DIRECTION_MASK_SOUTH;
+						break;
+					case 1:
+						returnValue |= mxConstants.DIRECTION_MASK_WEST;
+						break;
+					case 2:
+						returnValue |= mxConstants.DIRECTION_MASK_NORTH;
+						break;
+					case 3:
+						returnValue |= mxConstants.DIRECTION_MASK_EAST;
+						break;
+				}
+			}
+			if (directions.indexOf(mxConstants.DIRECTION_EAST) >= 0)
+			{
+				switch (quad)
+				{
+					case 0:
+						returnValue |= mxConstants.DIRECTION_MASK_EAST;
+						break;
+					case 1:
+						returnValue |= mxConstants.DIRECTION_MASK_SOUTH;
+						break;
+					case 2:
+						returnValue |= mxConstants.DIRECTION_MASK_WEST;
+						break;
+					case 3:
+						returnValue |= mxConstants.DIRECTION_MASK_NORTH;
+						break;
+				}
+			}
+
+			return returnValue;
+		}
+	},
+	
+	/**
+	 * Function: reversePortConstraints
+	 * 
+	 * Reverse the port constraint bitmask. For example, north | east
+	 * becomes south | west
+	 */
+	reversePortConstraints: function(constraint)
+	{
+		var result = 0;
+		
+		result = (constraint & mxConstants.DIRECTION_MASK_WEST) << 3;
+		result |= (constraint & mxConstants.DIRECTION_MASK_NORTH) << 1;
+		result |= (constraint & mxConstants.DIRECTION_MASK_SOUTH) >> 1;
+		result |= (constraint & mxConstants.DIRECTION_MASK_EAST) >> 3;
+		
+		return result;
+	},
+	
+	/**
+	 * Function: findNearestSegment
+	 * 
+	 * Finds the index of the nearest segment on the given cell state for
+	 * the specified coordinate pair.
+	 */
+	findNearestSegment: function(state, x, y)
+	{
+		var index = -1;
+		
+		if (state.absolutePoints.length > 0)
+		{
+			var last = state.absolutePoints[0];
+			var min = null;
+			
+			for (var i = 1; i < state.absolutePoints.length; i++)
+			{
+				var current = state.absolutePoints[i];
+				var dist = mxUtils.ptSegDistSq(last.x, last.y,
+					current.x, current.y, x, y);
+				
+				if (min == null || dist < min)
+				{
+					min = dist;
+					index = i - 1;
+				}
+
+				last = current;
+			}
+		}
+		
+		return index;
+	},
+
+	/**
+	 * Function: getDirectedBounds
+	 * 
+	 * Adds the given margins to the given rectangle and rotates and flips the
+	 * rectangle according to the respective styles in style.
+	 */
+	getDirectedBounds: function (rect, m, style, flipH, flipV)
+	{
+		var d = mxUtils.getValue(style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST);
+		flipH = (flipH != null) ? flipH : mxUtils.getValue(style, mxConstants.STYLE_FLIPH, false);
+		flipV = (flipV != null) ? flipV : mxUtils.getValue(style, mxConstants.STYLE_FLIPV, false);
+
+		m.x = Math.round(Math.max(0, Math.min(rect.width, m.x)));
+		m.y = Math.round(Math.max(0, Math.min(rect.height, m.y)));
+		m.width = Math.round(Math.max(0, Math.min(rect.width, m.width)));
+		m.height = Math.round(Math.max(0, Math.min(rect.height, m.height)));
+		
+		if ((flipV && (d == mxConstants.DIRECTION_SOUTH || d == mxConstants.DIRECTION_NORTH)) ||
+			(flipH && (d == mxConstants.DIRECTION_EAST || d == mxConstants.DIRECTION_WEST)))
+		{
+			var tmp = m.x;
+			m.x = m.width;
+			m.width = tmp;
+		}
+			
+		if ((flipH && (d == mxConstants.DIRECTION_SOUTH || d == mxConstants.DIRECTION_NORTH)) ||
+			(flipV && (d == mxConstants.DIRECTION_EAST || d == mxConstants.DIRECTION_WEST)))
+		{
+			var tmp = m.y;
+			m.y = m.height;
+			m.height = tmp;
+		}
+		
+		var m2 = mxRectangle.fromRectangle(m);
+		
+		if (d == mxConstants.DIRECTION_SOUTH)
+		{
+			m2.y = m.x;
+			m2.x = m.height;
+			m2.width = m.y;
+			m2.height = m.width;
+		}
+		else if (d == mxConstants.DIRECTION_WEST)
+		{
+			m2.y = m.height;
+			m2.x = m.width;
+			m2.width = m.x;
+			m2.height = m.y;
+		}
+		else if (d == mxConstants.DIRECTION_NORTH)
+		{
+			m2.y = m.width;
+			m2.x = m.y;
+			m2.width = m.height;
+			m2.height = m.x;
+		}
+		
+		return new mxRectangle(rect.x + m2.x, rect.y + m2.y, rect.width - m2.width - m2.x, rect.height - m2.height - m2.y);
+	},
+
+	/**
+	 * Function: getPerimeterPoint
+	 * 
+	 * Returns the intersection between the polygon defined by the array of
+	 * points and the line between center and point.
+	 */
+	getPerimeterPoint: function (pts, center, point)
+	{
+		var min = null;
+		
+		for (var i = 0; i < pts.length - 1; i++)
+		{
+			var pt = mxUtils.intersection(pts[i].x, pts[i].y, pts[i + 1].x, pts[i + 1].y,
+				center.x, center.y, point.x, point.y);
+			
+			if (pt != null)
+			{
+				var dx = point.x - pt.x;
+				var dy = point.y - pt.y;
+				var ip = {p: pt, distSq: dy * dy + dx * dx};
+				
+				if (ip != null && (min == null || min.distSq > ip.distSq))
+				{
+					min = ip;
+				}
+			}
+		}
+		
+		return (min != null) ? min.p : null;
+	},
+
+	/**
+	 * Function: rectangleIntersectsSegment
+	 * 
+	 * Returns true if the given rectangle intersects the given segment.
+	 * 
+	 * Parameters:
+	 * 
+	 * bounds - <mxRectangle> that represents the rectangle.
+	 * p1 - <mxPoint> that represents the first point of the segment.
+	 * p2 - <mxPoint> that represents the second point of the segment.
+	 */
+	rectangleIntersectsSegment: function(bounds, p1, p2)
+	{
+		var top = bounds.y;
+		var left = bounds.x;
+		var bottom = top + bounds.height;
+		var right = left + bounds.width;
+			
+		// Find min and max X for the segment
+		var minX = p1.x;
+		var maxX = p2.x;
+		
+		if (p1.x > p2.x)
+		{
+		  minX = p2.x;
+		  maxX = p1.x;
+		}
+		
+		// Find the intersection of the segment's and rectangle's x-projections
+		if (maxX > right)
+		{
+		  maxX = right;
+		}
+		
+		if (minX < left)
+		{
+		  minX = left;
+		}
+		
+		if (minX > maxX) // If their projections do not intersect return false
+		{
+		  return false;
+		}
+		
+		// Find corresponding min and max Y for min and max X we found before
+		var minY = p1.y;
+		var maxY = p2.y;
+		var dx = p2.x - p1.x;
+		
+		if (Math.abs(dx) > 0.0000001)
+		{
+		  var a = (p2.y - p1.y) / dx;
+		  var b = p1.y - a * p1.x;
+		  minY = a * minX + b;
+		  maxY = a * maxX + b;
+		}
+		
+		if (minY > maxY)
+		{
+		  var tmp = maxY;
+		  maxY = minY;
+		  minY = tmp;
+		}
+		
+		// Find the intersection of the segment's and rectangle's y-projections
+		if (maxY > bottom)
+		{
+		  maxY = bottom;
+		}
+		
+		if (minY < top)
+		{
+		  minY = top;
+		}
+		
+		if (minY > maxY) // If Y-projections do not intersect return false
+		{
+		  return false;
+		}
+		
+		return true;
+	},
+	
+	/**
+	 * Function: contains
+	 * 
+	 * Returns true if the specified point (x, y) is contained in the given rectangle.
+	 * 
+	 * Parameters:
+	 * 
+	 * bounds - <mxRectangle> that represents the area.
+	 * x - X-coordinate of the point.
+	 * y - Y-coordinate of the point.
+	 */
+	contains: function(bounds, x, y)
+	{
+		return (bounds.x <= x && bounds.x + bounds.width >= x &&
+				bounds.y <= y && bounds.y + bounds.height >= y);
+	},
+
+	/**
+	 * Function: intersects
+	 * 
+	 * Returns true if the two rectangles intersect.
+	 * 
+	 * Parameters:
+	 * 
+	 * a - <mxRectangle> to be checked for intersection.
+	 * b - <mxRectangle> to be checked for intersection.
+	 */
+	intersects: function(a, b)
+	{
+		var tw = a.width;
+		var th = a.height;
+		var rw = b.width;
+		var rh = b.height;
+		
+		if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0)
+		{
+		    return false;
+		}
+		
+		var tx = a.x;
+		var ty = a.y;
+		var rx = b.x;
+		var ry = b.y;
+		
+		rw += rx;
+		rh += ry;
+		tw += tx;
+		th += ty;
+
+		return ((rw < rx || rw > tx) &&
+			(rh < ry || rh > ty) &&
+			(tw < tx || tw > rx) &&
+			(th < ty || th > ry));
+	},
+
+	/**
+	 * Function: intersects
+	 * 
+	 * Returns true if the two rectangles intersect.
+	 * 
+	 * Parameters:
+	 * 
+	 * a - <mxRectangle> to be checked for intersection.
+	 * b - <mxRectangle> to be checked for intersection.
+	 */
+	intersectsHotspot: function(state, x, y, hotspot, min, max)
+	{
+		hotspot = (hotspot != null) ? hotspot : 1;
+		min = (min != null) ? min : 0;
+		max = (max != null) ? max : 0;
+		
+		if (hotspot > 0)
+		{
+			var cx = state.getCenterX();
+			var cy = state.getCenterY();
+			var w = state.width;
+			var h = state.height;
+			
+			var start = mxUtils.getValue(state.style, mxConstants.STYLE_STARTSIZE) * state.view.scale;
+
+			if (start > 0)
+			{
+				if (mxUtils.getValue(state.style, mxConstants.STYLE_HORIZONTAL, true))
+				{
+					cy = state.y + start / 2;
+					h = start;
+				}
+				else
+				{
+					cx = state.x + start / 2;
+					w = start;
+				}
+			}
+
+			w = Math.max(min, w * hotspot);
+			h = Math.max(min, h * hotspot);
+			
+			if (max > 0)
+			{
+				w = Math.min(w, max);
+				h = Math.min(h, max);
+			}
+			
+			var rect = new mxRectangle(cx - w / 2, cy - h / 2, w, h);
+			var alpha = mxUtils.toRadians(mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0);
+			
+			if (alpha != 0)
+			{
+				var cos = Math.cos(-alpha);
+				var sin = Math.sin(-alpha);
+				var cx = new mxPoint(state.getCenterX(), state.getCenterY());
+				var pt = mxUtils.getRotatedPoint(new mxPoint(x, y), cos, sin, cx);
+				x = pt.x;
+				y = pt.y;
+			}
+			
+			return mxUtils.contains(rect, x, y);			
+		}
+		
+		return true;
+	},
+
+	/**
+	 * Function: getOffset
+	 * 
+	 * Returns the offset for the specified container as an <mxPoint>. The
+	 * offset is the distance from the top left corner of the container to the
+	 * top left corner of the document.
+	 * 
+	 * Parameters:
+	 * 
+	 * container - DOM node to return the offset for.
+	 * scollOffset - Optional boolean to add the scroll offset of the document.
+	 * Default is false.
+	 */
+	getOffset: function(container, scrollOffset)
+	{
+		var offsetLeft = 0;
+		var offsetTop = 0;
+		
+		// Ignores document scroll origin for fixed elements
+		var fixed = false;
+		var node = container;
+		var b = document.body;
+		var d = document.documentElement;
+
+		while (node != null && node != b && node != d && !fixed)
+		{
+			var style = mxUtils.getCurrentStyle(node);
+			
+			if (style != null)
+			{
+				fixed = fixed || style.position == 'fixed';
+			}
+			
+			node = node.parentNode;
+		}
+		
+		if (!scrollOffset && !fixed)
+		{
+			var offset = mxUtils.getDocumentScrollOrigin(container.ownerDocument);
+			offsetLeft += offset.x;
+			offsetTop += offset.y;
+		}
+		
+		var r = container.getBoundingClientRect();
+		
+		if (r != null)
+		{
+			offsetLeft += r.left;
+			offsetTop += r.top;
+		}
+		
+		return new mxPoint(offsetLeft, offsetTop);
+	},
+
+	/**
+	 * Function: getDocumentScrollOrigin
+	 * 
+	 * Returns the scroll origin of the given document or the current document
+	 * if no document is given.
+	 */
+	getDocumentScrollOrigin: function(doc)
+	{
+		if (mxClient.IS_QUIRKS)
+		{
+			return new mxPoint(doc.body.scrollLeft, doc.body.scrollTop);
+		}
+		else
+		{
+			var wnd = doc.defaultView || doc.parentWindow;
+			
+			var x = (wnd != null && window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
+			var y = (wnd != null && window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
+			
+			return new mxPoint(x, y);
+		}
+	},
+	
+	/**
+	 * Function: getScrollOrigin
+	 * 
+	 * Returns the top, left corner of the viewrect as an <mxPoint>.
+	 * 
+	 * Parameters:
+	 * 
+	 * node - DOM node whose scroll origin should be returned.
+	 * includeAncestors - Whether the scroll origin of the ancestors should be
+	 * included. Default is false.
+	 * includeDocument - Whether the scroll origin of the document should be
+	 * included. Default is true.
+	 */
+	getScrollOrigin: function(node, includeAncestors, includeDocument)
+	{
+		includeAncestors = (includeAncestors != null) ? includeAncestors : false;
+		includeDocument = (includeDocument != null) ? includeDocument : true;
+		
+		var doc = (node != null) ? node.ownerDocument : document;
+		var b = doc.body;
+		var d = doc.documentElement;
+		var result = new mxPoint();
+		var fixed = false;
+
+		while (node != null && node != b && node != d)
+		{
+			if (!isNaN(node.scrollLeft) && !isNaN(node.scrollTop))
+			{
+				result.x += node.scrollLeft;
+				result.y += node.scrollTop;
+			}
+			
+			var style = mxUtils.getCurrentStyle(node);
+			
+			if (style != null)
+			{
+				fixed = fixed || style.position == 'fixed';
+			}
+
+			node = (includeAncestors) ? node.parentNode : null;
+		}
+
+		if (!fixed && includeDocument)
+		{
+			var origin = mxUtils.getDocumentScrollOrigin(doc);
+
+			result.x += origin.x;
+			result.y += origin.y;
+		}
+		
+		return result;
+	},
+
+	/**
+	 * Function: convertPoint
+	 * 
+	 * Converts the specified point (x, y) using the offset of the specified
+	 * container and returns a new <mxPoint> with the result.
+	 * 
+	 * (code)
+	 * var pt = mxUtils.convertPoint(graph.container,
+	 *   mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+	 * (end)
+	 * 
+	 * Parameters:
+	 * 
+	 * container - DOM node to use for the offset.
+	 * x - X-coordinate of the point to be converted.
+	 * y - Y-coordinate of the point to be converted.
+	 */
+	convertPoint: function(container, x, y)
+	{
+		var origin = mxUtils.getScrollOrigin(container, false);
+		var offset = mxUtils.getOffset(container);
+
+		offset.x -= origin.x;
+		offset.y -= origin.y;
+		
+		return new mxPoint(x - offset.x, y - offset.y);
+	},
+	
+	/**
+	 * Function: ltrim
+	 * 
+	 * Strips all whitespaces from the beginning of the string. Without the
+	 * second parameter, this will trim these characters:
+	 * 
+	 * - " " (ASCII 32 (0x20)), an ordinary space
+	 * - "\t" (ASCII 9 (0x09)), a tab
+	 * - "\n" (ASCII 10 (0x0A)), a new line (line feed)
+	 * - "\r" (ASCII 13 (0x0D)), a carriage return
+	 * - "\0" (ASCII 0 (0x00)), the NUL-byte
+	 * - "\x0B" (ASCII 11 (0x0B)), a vertical tab
+	 */
+	ltrim: function(str, chars)
+	{
+		chars = chars || "\\s";
+		
+		return (str != null) ? str.replace(new RegExp("^[" + chars + "]+", "g"), "") : null;
+	},
+	
+	/**
+	 * Function: rtrim
+	 * 
+	 * Strips all whitespaces from the end of the string. Without the second
+	 * parameter, this will trim these characters:
+	 * 
+	 * - " " (ASCII 32 (0x20)), an ordinary space
+	 * - "\t" (ASCII 9 (0x09)), a tab
+	 * - "\n" (ASCII 10 (0x0A)), a new line (line feed)
+	 * - "\r" (ASCII 13 (0x0D)), a carriage return
+	 * - "\0" (ASCII 0 (0x00)), the NUL-byte
+	 * - "\x0B" (ASCII 11 (0x0B)), a vertical tab
+	 */
+	rtrim: function(str, chars)
+	{
+		chars = chars || "\\s";
+		
+		return (str != null) ? str.replace(new RegExp("[" + chars + "]+$", "g"), "") : null;
+	},
+	
+	/**
+	 * Function: trim
+	 * 
+	 * Strips all whitespaces from both end of the string.
+	 * Without the second parameter, Javascript function will trim these
+	 * characters:
+	 * 
+	 * - " " (ASCII 32 (0x20)), an ordinary space
+	 * - "\t" (ASCII 9 (0x09)), a tab
+	 * - "\n" (ASCII 10 (0x0A)), a new line (line feed)
+	 * - "\r" (ASCII 13 (0x0D)), a carriage return
+	 * - "\0" (ASCII 0 (0x00)), the NUL-byte
+	 * - "\x0B" (ASCII 11 (0x0B)), a vertical tab
+	 */
+	trim: function(str, chars)
+	{
+		return mxUtils.ltrim(mxUtils.rtrim(str, chars), chars);
+	},
+	
+	/**
+	 * Function: isNumeric
+	 * 
+	 * Returns true if the specified value is numeric, that is, if it is not
+	 * null, not an empty string, not a HEX number and isNaN returns false.
+	 * 
+	 * Parameters:
+	 * 
+	 * n - String representing the possibly numeric value.
+	 */
+	isNumeric: function(n)
+	{
+		return !isNaN(parseFloat(n)) && isFinite(n) && (typeof(n) != 'string' || n.toLowerCase().indexOf('0x') < 0);
+	},
+
+	/**
+	 * Function: isInteger
+	 * 
+	 * Returns true if the given value is an valid integer number.
+	 * 
+	 * Parameters:
+	 * 
+	 * n - String representing the possibly numeric value.
+	 */
+	isInteger: function(n)
+	{
+		return String(parseInt(n)) === String(n);
+	},
+
+	/**
+	 * Function: mod
+	 * 
+	 * Returns the remainder of division of n by m. You should use this instead
+	 * of the built-in operation as the built-in operation does not properly
+	 * handle negative numbers.
+	 */
+	mod: function(n, m)
+	{
+		return ((n % m) + m) % m;
+	},
+
+	/**
+	 * Function: intersection
+	 * 
+	 * Returns the intersection of two lines as an <mxPoint>.
+	 * 
+	 * Parameters:
+	 * 
+	 * x0 - X-coordinate of the first line's startpoint.
+	 * y0 - X-coordinate of the first line's startpoint.
+	 * x1 - X-coordinate of the first line's endpoint.
+	 * y1 - Y-coordinate of the first line's endpoint.
+	 * x2 - X-coordinate of the second line's startpoint.
+	 * y2 - Y-coordinate of the second line's startpoint.
+	 * x3 - X-coordinate of the second line's endpoint.
+	 * y3 - Y-coordinate of the second line's endpoint.
+	 */
+	intersection: function (x0, y0, x1, y1, x2, y2, x3, y3)
+	{
+		var denom = ((y3 - y2) * (x1 - x0)) - ((x3 - x2) * (y1 - y0));
+		var nume_a = ((x3 - x2) * (y0 - y2)) - ((y3 - y2) * (x0 - x2));
+		var nume_b = ((x1 - x0) * (y0 - y2)) - ((y1 - y0) * (x0 - x2));
+
+		var ua = nume_a / denom;
+		var ub = nume_b / denom;
+		
+		if(ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0)
+		{
+			// Get the intersection point
+			var x = x0 + ua * (x1 - x0);
+			var y = y0 + ua * (y1 - y0);
+			
+			return new mxPoint(x, y);
+		}
+		
+		// No intersection
+		return null;
+	},
+	
+	/**
+	 * Function: ptSegDistSq
+	 * 
+	 * Returns the square distance between a segment and a point. To get the
+	 * distance between a point and a line (with infinite length) use
+	 * <mxUtils.ptLineDist>.
+	 * 
+	 * Parameters:
+	 * 
+	 * x1 - X-coordinate of the startpoint of the segment.
+	 * y1 - Y-coordinate of the startpoint of the segment.
+	 * x2 - X-coordinate of the endpoint of the segment.
+	 * y2 - Y-coordinate of the endpoint of the segment.
+	 * px - X-coordinate of the point.
+	 * py - Y-coordinate of the point.
+	 */
+	ptSegDistSq: function(x1, y1, x2, y2, px, py)
+    {
+		x2 -= x1;
+		y2 -= y1;
+
+		px -= x1;
+		py -= y1;
+
+		var dotprod = px * x2 + py * y2;
+		var projlenSq;
+
+		if (dotprod <= 0.0)
+		{
+		    projlenSq = 0.0;
+		}
+		else
+		{
+		    px = x2 - px;
+		    py = y2 - py;
+		    dotprod = px * x2 + py * y2;
+
+		    if (dotprod <= 0.0)
+		    {
+				projlenSq = 0.0;
+		    }
+		    else
+		    {
+				projlenSq = dotprod * dotprod / (x2 * x2 + y2 * y2);
+		    }
+		}
+
+		var lenSq = px * px + py * py - projlenSq;
+		
+		if (lenSq < 0)
+		{
+		    lenSq = 0;
+		}
+		
+		return lenSq;
+    },
+	
+	/**
+	 * Function: ptLineDist
+	 * 
+	 * Returns the distance between a line defined by two points and a point.
+	 * To get the distance between a point and a segment (with a specific
+	 * length) use <mxUtils.ptSeqDistSq>.
+	 * 
+	 * Parameters:
+	 * 
+	 * x1 - X-coordinate of point 1 of the line.
+	 * y1 - Y-coordinate of point 1 of the line.
+	 * x2 - X-coordinate of point 1 of the line.
+	 * y2 - Y-coordinate of point 1 of the line.
+	 * px - X-coordinate of the point.
+	 * py - Y-coordinate of the point.
+	 */
+    ptLineDist: function(x1, y1, x2, y2, px, py)
+    {
+		return Math.abs((y2 - y1) * px - (x2 - x1) * py + x2 * y1 - y2 * x1) /
+			Math.sqrt((y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1));
+    },
+    	
+	/**
+	 * Function: relativeCcw
+	 * 
+	 * Returns 1 if the given point on the right side of the segment, 0 if its
+	 * on the segment, and -1 if the point is on the left side of the segment.
+	 * 
+	 * Parameters:
+	 * 
+	 * x1 - X-coordinate of the startpoint of the segment.
+	 * y1 - Y-coordinate of the startpoint of the segment.
+	 * x2 - X-coordinate of the endpoint of the segment.
+	 * y2 - Y-coordinate of the endpoint of the segment.
+	 * px - X-coordinate of the point.
+	 * py - Y-coordinate of the point.
+	 */
+	relativeCcw: function(x1, y1, x2, y2, px, py)
+    {
+		x2 -= x1;
+		y2 -= y1;
+		px -= x1;
+		py -= y1;
+		var ccw = px * y2 - py * x2;
+		
+		if (ccw == 0.0)
+		{
+		    ccw = px * x2 + py * y2;
+		    
+		    if (ccw > 0.0)
+		    {
+				px -= x2;
+				py -= y2;
+				ccw = px * x2 + py * y2;
+				
+				if (ccw < 0.0)
+				{
+				    ccw = 0.0;
+				}
+		    }
+		}
+		
+		return (ccw < 0.0) ? -1 : ((ccw > 0.0) ? 1 : 0);
+    },
+    
+	/**
+	 * Function: animateChanges
+	 * 
+	 * See <mxEffects.animateChanges>. This is for backwards compatibility and
+	 * will be removed later.
+	 */
+	animateChanges: function(graph, changes)
+	{
+		// LATER: Deprecated, remove this function
+    	mxEffects.animateChanges.apply(this, arguments);
+	},
+    
+	/**
+	 * Function: cascadeOpacity
+	 * 
+	 * See <mxEffects.cascadeOpacity>. This is for backwards compatibility and
+	 * will be removed later.
+	 */
+    cascadeOpacity: function(graph, cell, opacity)
+	{
+		mxEffects.cascadeOpacity.apply(this, arguments);
+	},
+
+	/**
+	 * Function: fadeOut
+	 * 
+	 * See <mxEffects.fadeOut>. This is for backwards compatibility and
+	 * will be removed later.
+	 */
+	fadeOut: function(node, from, remove, step, delay, isEnabled)
+	{
+		mxEffects.fadeOut.apply(this, arguments);
+	},
+	
+	/**
+	 * Function: setOpacity
+	 * 
+	 * Sets the opacity of the specified DOM node to the given value in %.
+	 * 
+	 * Parameters:
+	 * 
+	 * node - DOM node to set the opacity for.
+	 * value - Opacity in %. Possible values are between 0 and 100.
+	 */
+	setOpacity: function(node, value)
+	{
+		if (mxUtils.isVml(node))
+		{
+	    	if (value >= 100)
+	    	{
+	    		node.style.filter = '';
+	    	}
+	    	else
+	    	{
+	    		// TODO: Why is the division by 5 needed in VML?
+			    node.style.filter = 'alpha(opacity=' + (value/5) + ')';
+	    	}
+		}
+		else if (mxClient.IS_IE && (typeof(document.documentMode) === 'undefined' || document.documentMode < 9))
+	    {
+	    	if (value >= 100)
+	    	{
+	    		node.style.filter = '';
+	    	}
+	    	else
+	    	{
+			    node.style.filter = 'alpha(opacity=' + value + ')';
+	    	}
+		}
+		else
+		{
+		    node.style.opacity = (value / 100);
+		}
+	},
+
+	/**
+	 * Function: createImage
+	 * 
+	 * Creates and returns an image (IMG node) or VML image (v:image) in IE6 in
+	 * quirks mode.
+	 * 
+	 * Parameters:
+	 * 
+	 * src - URL that points to the image to be displayed.
+	 */
+	createImage: function(src)
+	{
+        var imageNode = null;
+        
+		if (mxClient.IS_IE6 && document.compatMode != 'CSS1Compat')
+		{
+        	imageNode = document.createElement(mxClient.VML_PREFIX + ':image');
+        	imageNode.setAttribute('src', src);
+        	imageNode.style.borderStyle = 'none';
+        }
+		else
+		{
+			imageNode = document.createElement('img');
+			imageNode.setAttribute('src', src);
+			imageNode.setAttribute('border', '0');
+		}
+		
+		return imageNode;
+	},
+
+	/**
+	 * Function: sortCells
+	 * 
+	 * Sorts the given cells according to the order in the cell hierarchy.
+	 * Ascending is optional and defaults to true.
+	 */
+	sortCells: function(cells, ascending)
+	{
+		ascending = (ascending != null) ? ascending : true;
+		var lookup = new mxDictionary();
+		cells.sort(function(o1, o2)
+		{
+			var p1 = lookup.get(o1);
+			
+			if (p1 == null)
+			{
+				p1 = mxCellPath.create(o1).split(mxCellPath.PATH_SEPARATOR);
+				lookup.put(o1, p1);
+			}
+			
+			var p2 = lookup.get(o2);
+			
+			if (p2 == null)
+			{
+				p2 = mxCellPath.create(o2).split(mxCellPath.PATH_SEPARATOR);
+				lookup.put(o2, p2);
+			}
+			
+			var comp = mxCellPath.compare(p1, p2);
+			
+			return (comp == 0) ? 0 : (((comp > 0) == ascending) ? 1 : -1);
+		});
+		
+		return cells;
+	},
+
+	/**
+	 * Function: getStylename
+	 * 
+	 * Returns the stylename in a style of the form [(stylename|key=value);] or
+	 * an empty string if the given style does not contain a stylename.
+	 * 
+	 * Parameters:
+	 * 
+	 * style - String of the form [(stylename|key=value);].
+	 */
+	getStylename: function(style)
+	{
+		if (style != null)
+		{
+			var pairs = style.split(';');
+			var stylename = pairs[0];
+			
+			if (stylename.indexOf('=') < 0)
+			{
+				return stylename;
+			}
+		}
+				
+		return '';
+	},
+
+	/**
+	 * Function: getStylenames
+	 * 
+	 * Returns the stylenames in a style of the form [(stylename|key=value);]
+	 * or an empty array if the given style does not contain any stylenames.
+	 * 
+	 * Parameters:
+	 * 
+	 * style - String of the form [(stylename|key=value);].
+	 */
+	getStylenames: function(style)
+	{
+		var result = [];
+		
+		if (style != null)
+		{
+			var pairs = style.split(';');
+			
+			for (var i = 0; i < pairs.length; i++)
+			{
+				if (pairs[i].indexOf('=') < 0)
+				{
+					result.push(pairs[i]);
+				}
+			}
+		}
+				
+		return result;
+	},
+
+	/**
+	 * Function: indexOfStylename
+	 * 
+	 * Returns the index of the given stylename in the given style. This
+	 * returns -1 if the given stylename does not occur (as a stylename) in the
+	 * given style, otherwise it returns the index of the first character.
+	 */
+	indexOfStylename: function(style, stylename)
+	{
+		if (style != null && stylename != null)
+		{
+			var tokens = style.split(';');
+			var pos = 0;
+			
+			for (var i = 0; i < tokens.length; i++)
+			{
+				if (tokens[i] == stylename)
+				{
+					return pos;
+				}
+				
+				pos += tokens[i].length + 1;
+			}
+		}
+
+		return -1;
+	},
+	
+	/**
+	 * Function: addStylename
+	 * 
+	 * Adds the specified stylename to the given style if it does not already
+	 * contain the stylename.
+	 */
+	addStylename: function(style, stylename)
+	{
+		if (mxUtils.indexOfStylename(style, stylename) < 0)
+		{
+			if (style == null)
+			{
+				style = '';
+			}
+			else if (style.length > 0 && style.charAt(style.length - 1) != ';')
+			{
+				style += ';';
+			}
+			
+			style += stylename;
+		}
+		
+		return style;
+	},
+	
+	/**
+	 * Function: removeStylename
+	 * 
+	 * Removes all occurrences of the specified stylename in the given style
+	 * and returns the updated style. Trailing semicolons are not preserved.
+	 */
+	removeStylename: function(style, stylename)
+	{
+		var result = [];
+		
+		if (style != null)
+		{
+			var tokens = style.split(';');
+			
+			for (var i = 0; i < tokens.length; i++)
+			{
+				if (tokens[i] != stylename)
+				{
+					result.push(tokens[i]);
+				}
+			}
+		}
+		
+		return result.join(';');
+	},
+	
+	/**
+	 * Function: removeAllStylenames
+	 * 
+	 * Removes all stylenames from the given style and returns the updated
+	 * style.
+	 */
+	removeAllStylenames: function(style)
+	{
+		var result = [];
+		
+		if (style != null)
+		{
+			var tokens = style.split(';');
+			
+			for (var i = 0; i < tokens.length; i++)
+			{
+				// Keeps the key, value assignments
+				if (tokens[i].indexOf('=') >= 0)
+				{
+					result.push(tokens[i]);
+				}
+			}
+		}
+		
+		return result.join(';');
+	},
+
+	/**
+	 * Function: setCellStyles
+	 * 
+	 * Assigns the value for the given key in the styles of the given cells, or
+	 * removes the key from the styles if the value is null.
+	 * 
+	 * Parameters:
+	 * 
+	 * model - <mxGraphModel> to execute the transaction in.
+	 * cells - Array of <mxCells> to be updated.
+	 * key - Key of the style to be changed.
+	 * value - New value for the given key.
+	 */
+	setCellStyles: function(model, cells, key, value)
+	{
+		if (cells != null && cells.length > 0)
+		{
+			model.beginUpdate();
+			try
+			{
+				for (var i = 0; i < cells.length; i++)
+				{
+					if (cells[i] != null)
+					{
+						var style = mxUtils.setStyle(model.getStyle(cells[i]), key, value);
+						model.setStyle(cells[i], style);
+					}
+				}
+			}
+			finally
+			{
+				model.endUpdate();
+			}
+		}
+	},
+	
+	/**
+	 * Function: setStyle
+	 * 
+	 * Adds or removes the given key, value pair to the style and returns the
+	 * new style. If value is null or zero length then the key is removed from
+	 * the style. This is for cell styles, not for CSS styles.
+	 * 
+	 * Parameters:
+	 * 
+	 * style - String of the form [(stylename|key=value);].
+	 * key - Key of the style to be changed.
+	 * value - New value for the given key.
+	 */
+	setStyle: function(style, key, value)
+	{
+		var isValue = value != null && (typeof(value.length) == 'undefined' || value.length > 0);
+		
+		if (style == null || style.length == 0)
+		{
+			if (isValue)
+			{
+				style = key + '=' + value + ';';
+			}
+		}
+		else
+		{
+			if (style.substring(0, key.length + 1) == key + '=')
+			{
+				var next = style.indexOf(';');
+				
+				if (isValue)
+				{
+					style = key + '=' + value + ((next < 0) ? ';' : style.substring(next));
+				}
+				else
+				{
+					style = (next < 0 || next == style.length - 1) ? '' : style.substring(next + 1);
+				}
+			}
+			else
+			{
+				var index = style.indexOf(';' + key + '=');
+				
+				if (index < 0)
+				{
+					if (isValue)
+					{
+						var sep = (style.charAt(style.length - 1) == ';') ? '' : ';';
+						style = style + sep + key + '=' + value + ';';
+					}
+				}
+				else
+				{
+					var next = style.indexOf(';', index + 1);
+					
+					if (isValue)
+					{
+						style = style.substring(0, index + 1) + key + '=' + value + ((next < 0) ? ';' : style.substring(next));
+					}
+					else
+					{
+						style = style.substring(0, index) + ((next < 0) ? ';' : style.substring(next));
+					}
+				}
+			}
+		}
+		
+		return style;
+	},
+
+	/**
+	 * Function: setCellStyleFlags
+	 * 
+	 * Sets or toggles the flag bit for the given key in the cell's styles.
+	 * If value is null then the flag is toggled.
+	 * 
+	 * Example:
+	 * 
+	 * (code)
+	 * var cells = graph.getSelectionCells();
+	 * mxUtils.setCellStyleFlags(graph.model,
+	 * 			cells,
+	 * 			mxConstants.STYLE_FONTSTYLE,
+	 * 			mxConstants.FONT_BOLD);
+	 * (end)
+	 * 
+	 * Toggles the bold font style.
+	 * 
+	 * Parameters:
+	 * 
+	 * model - <mxGraphModel> that contains the cells.
+	 * cells - Array of <mxCells> to change the style for.
+	 * key - Key of the style to be changed.
+	 * flag - Integer for the bit to be changed.
+	 * value - Optional boolean value for the flag.
+	 */
+	setCellStyleFlags: function(model, cells, key, flag, value)
+	{
+		if (cells != null && cells.length > 0)
+		{
+			model.beginUpdate();
+			try
+			{
+				for (var i = 0; i < cells.length; i++)
+				{
+					if (cells[i] != null)
+					{
+						var style = mxUtils.setStyleFlag(
+							model.getStyle(cells[i]),
+							key, flag, value);
+						model.setStyle(cells[i], style);
+					}
+				}
+			}
+			finally
+			{
+				model.endUpdate();
+			}
+		}
+	},
+	
+	/**
+	 * Function: setStyleFlag
+	 * 
+	 * Sets or removes the given key from the specified style and returns the
+	 * new style. If value is null then the flag is toggled.
+	 * 
+	 * Parameters:
+	 * 
+	 * style - String of the form [(stylename|key=value);].
+	 * key - Key of the style to be changed.
+	 * flag - Integer for the bit to be changed.
+	 * value - Optional boolean value for the given flag.
+	 */
+	setStyleFlag: function(style, key, flag, value)
+	{
+		if (style == null || style.length == 0)
+		{
+			if (value || value == null)
+			{
+				style = key+'='+flag;
+			}
+			else
+			{
+				style = key+'=0';
+			}
+		}
+		else
+		{
+			var index = style.indexOf(key+'=');
+			
+			if (index < 0)
+			{
+				var sep = (style.charAt(style.length-1) == ';') ? '' : ';';
+
+				if (value || value == null)
+				{
+					style = style + sep + key + '=' + flag;
+				}
+				else
+				{
+					style = style + sep + key + '=0';
+				}
+			}
+			else
+			{
+				var cont = style.indexOf(';', index);
+				var tmp = '';
+				
+				if (cont < 0)
+				{
+					tmp  = style.substring(index+key.length+1);
+				}
+				else
+				{
+					tmp = style.substring(index+key.length+1, cont);
+				}
+				
+				if (value == null)
+				{
+					tmp = parseInt(tmp) ^ flag;
+				}
+				else if (value)
+				{
+					tmp = parseInt(tmp) | flag;
+				}
+				else
+				{
+					tmp = parseInt(tmp) & ~flag;
+				}
+				
+				style = style.substring(0, index) + key + '=' + tmp +
+					((cont >= 0) ? style.substring(cont) : '');
+			}
+		}
+		
+		return style;
+	},
+	
+	/**
+	 * Function: getAlignmentAsPoint
+	 * 
+	 * Returns an <mxPoint> that represents the horizontal and vertical alignment
+	 * for numeric computations. X is -0.5 for center, -1 for right and 0 for
+	 * left alignment. Y is -0.5 for middle, -1 for bottom and 0 for top
+	 * alignment. Default values for missing arguments is top, left.
+	 */
+	getAlignmentAsPoint: function(align, valign)
+	{
+		var dx = 0;
+		var dy = 0;
+		
+		// Horizontal alignment
+		if (align == mxConstants.ALIGN_CENTER)
+		{
+			dx = -0.5;
+		}
+		else if (align == mxConstants.ALIGN_RIGHT)
+		{
+			dx = -1;
+		}
+
+		// Vertical alignment
+		if (valign == mxConstants.ALIGN_MIDDLE)
+		{
+			dy = -0.5;
+		}
+		else if (valign == mxConstants.ALIGN_BOTTOM)
+		{
+			dy = -1;
+		}
+		
+		return new mxPoint(dx, dy);
+	},
+	
+	/**
+	 * Function: getSizeForString
+	 * 
+	 * Returns an <mxRectangle> with the size (width and height in pixels) of
+	 * the given string. The string may contain HTML markup. Newlines should be
+	 * converted to <br> before calling this method. The caller is responsible
+	 * for sanitizing the HTML markup.
+	 * 
+	 * Example:
+	 * 
+	 * (code)
+	 * var label = graph.getLabel(cell).replace(/\n/g, "<br>");
+	 * var size = graph.getSizeForString(label);
+	 * (end)
+	 * 
+	 * Parameters:
+	 * 
+	 * text - String whose size should be returned.
+	 * fontSize - Integer that specifies the font size in pixels. Default is
+	 * <mxConstants.DEFAULT_FONTSIZE>.
+	 * fontFamily - String that specifies the name of the font family. Default
+	 * is <mxConstants.DEFAULT_FONTFAMILY>.
+	 * textWidth - Optional width for text wrapping.
+	 */
+	getSizeForString: function(text, fontSize, fontFamily, textWidth)
+	{
+		fontSize = (fontSize != null) ? fontSize : mxConstants.DEFAULT_FONTSIZE;
+		fontFamily = (fontFamily != null) ? fontFamily : mxConstants.DEFAULT_FONTFAMILY;
+		var div = document.createElement('div');
+		
+		// Sets the font size and family
+		div.style.fontFamily = fontFamily;
+		div.style.fontSize = Math.round(fontSize) + 'px';
+		div.style.lineHeight = Math.round(fontSize * mxConstants.LINE_HEIGHT) + 'px';
+		
+		// Disables block layout and outside wrapping and hides the div
+		div.style.position = 'absolute';
+		div.style.visibility = 'hidden';
+		div.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block';
+		div.style.zoom = '1';
+		
+		if (textWidth != null)
+		{
+			div.style.width = textWidth + 'px';
+			div.style.whiteSpace = 'normal';
+		}
+		else
+		{
+			div.style.whiteSpace = 'nowrap';
+		}
+		
+		// Adds the text and inserts into DOM for updating of size
+		div.innerHTML = text;
+		document.body.appendChild(div);
+		
+		// Gets the size and removes from DOM
+		var size = new mxRectangle(0, 0, div.offsetWidth, div.offsetHeight);
+		document.body.removeChild(div);
+		
+		return size;
+	},
+	
+	/**
+	 * Function: getViewXml
+	 */
+	getViewXml: function(graph, scale, cells, x0, y0)
+	{
+		x0 = (x0 != null) ? x0 : 0;
+		y0 = (y0 != null) ? y0 : 0;
+		scale = (scale != null) ? scale : 1;
+
+		if (cells == null)
+		{
+			var model = graph.getModel();
+			cells = [model.getRoot()];
+		}
+		
+		var view = graph.getView();
+		var result = null;
+
+		// Disables events on the view
+		var eventsEnabled = view.isEventsEnabled();
+		view.setEventsEnabled(false);
+
+		// Workaround for label bounds not taken into account for image export.
+		// Creates a temporary draw pane which is used for rendering the text.
+		// Text rendering is required for finding the bounds of the labels.
+		var drawPane = view.drawPane;
+		var overlayPane = view.overlayPane;
+
+		if (graph.dialect == mxConstants.DIALECT_SVG)
+		{
+			view.drawPane = document.createElementNS(mxConstants.NS_SVG, 'g');
+			view.canvas.appendChild(view.drawPane);
+
+			// Redirects cell overlays into temporary container
+			view.overlayPane = document.createElementNS(mxConstants.NS_SVG, 'g');
+			view.canvas.appendChild(view.overlayPane);
+		}
+		else
+		{
+			view.drawPane = view.drawPane.cloneNode(false);
+			view.canvas.appendChild(view.drawPane);
+			
+			// Redirects cell overlays into temporary container
+			view.overlayPane = view.overlayPane.cloneNode(false);
+			view.canvas.appendChild(view.overlayPane);
+		}
+
+		// Resets the translation
+		var translate = view.getTranslate();
+		view.translate = new mxPoint(x0, y0);
+
+		// Creates the temporary cell states in the view
+		var temp = new mxTemporaryCellStates(graph.getView(), scale, cells);
+
+		try
+		{
+			var enc = new mxCodec();
+			result = enc.encode(graph.getView());
+		}
+		finally
+		{
+			temp.destroy();
+			view.translate = translate;
+			view.canvas.removeChild(view.drawPane);
+			view.canvas.removeChild(view.overlayPane);
+			view.drawPane = drawPane;
+			view.overlayPane = overlayPane;
+			view.setEventsEnabled(eventsEnabled);
+		}
+
+		return result;
+	},
+	
+	/**
+	 * Function: getScaleForPageCount
+	 * 
+	 * Returns the scale to be used for printing the graph with the given
+	 * bounds across the specifies number of pages with the given format. The
+	 * scale is always computed such that it given the given amount or fewer
+	 * pages in the print output. See <mxPrintPreview> for an example.
+	 * 
+	 * Parameters:
+	 * 
+	 * pageCount - Specifies the number of pages in the print output.
+	 * graph - <mxGraph> that should be printed.
+	 * pageFormat - Optional <mxRectangle> that specifies the page format.
+	 * Default is <mxConstants.PAGE_FORMAT_A4_PORTRAIT>.
+	 * border - The border along each side of every page.
+	 */
+	getScaleForPageCount: function(pageCount, graph, pageFormat, border)
+	{
+		if (pageCount < 1)
+		{
+			// We can't work with less than 1 page, return no scale
+			// change
+			return 1;
+		}
+		
+		pageFormat = (pageFormat != null) ? pageFormat : mxConstants.PAGE_FORMAT_A4_PORTRAIT;
+		border = (border != null) ? border : 0;
+		
+		var availablePageWidth = pageFormat.width - (border * 2);
+		var availablePageHeight = pageFormat.height - (border * 2);
+
+		// Work out the number of pages required if the
+		// graph is not scaled.
+		var graphBounds = graph.getGraphBounds().clone();
+		var sc = graph.getView().getScale();
+		graphBounds.width /= sc;
+		graphBounds.height /= sc;
+		var graphWidth = graphBounds.width;
+		var graphHeight = graphBounds.height;
+
+		var scale = 1;
+		
+		// The ratio of the width/height for each printer page
+		var pageFormatAspectRatio = availablePageWidth / availablePageHeight;
+		// The ratio of the width/height for the graph to be printer
+		var graphAspectRatio = graphWidth / graphHeight;
+		
+		// The ratio of horizontal pages / vertical pages for this 
+		// graph to maintain its aspect ratio on this page format
+		var pagesAspectRatio = graphAspectRatio / pageFormatAspectRatio;
+		
+		// Factor the square root of the page count up and down 
+		// by the pages aspect ratio to obtain a horizontal and 
+		// vertical page count that adds up to the page count
+		// and has the correct aspect ratio
+		var pageRoot = Math.sqrt(pageCount);
+		var pagesAspectRatioSqrt = Math.sqrt(pagesAspectRatio);
+		var numRowPages = pageRoot * pagesAspectRatioSqrt;
+		var numColumnPages = pageRoot / pagesAspectRatioSqrt;
+
+		// These value are rarely more than 2 rounding downs away from
+		// a total that meets the page count. In cases of one being less 
+		// than 1 page, the other value can be too high and take more iterations 
+		// In this case, just change that value to be the page count, since 
+		// we know the other value is 1
+		if (numRowPages < 1 && numColumnPages > pageCount)
+		{
+			var scaleChange = numColumnPages / pageCount;
+			numColumnPages = pageCount;
+			numRowPages /= scaleChange;
+		}
+		
+		if (numColumnPages < 1 && numRowPages > pageCount)
+		{
+			var scaleChange = numRowPages / pageCount;
+			numRowPages = pageCount;
+			numColumnPages /= scaleChange;
+		}		
+
+		var currentTotalPages = Math.ceil(numRowPages) * Math.ceil(numColumnPages);
+
+		var numLoops = 0;
+		
+		// Iterate through while the rounded up number of pages comes to
+		// a total greater than the required number
+		while (currentTotalPages > pageCount)
+		{
+			// Round down the page count (rows or columns) that is
+			// closest to its next integer down in percentage terms.
+			// i.e. Reduce the page total by reducing the total
+			// page area by the least possible amount
+
+			var roundRowDownProportion = Math.floor(numRowPages) / numRowPages;
+			var roundColumnDownProportion = Math.floor(numColumnPages) / numColumnPages;
+			
+			// If the round down proportion is, work out the proportion to
+			// round down to 1 page less
+			if (roundRowDownProportion == 1)
+			{
+				roundRowDownProportion = Math.floor(numRowPages-1) / numRowPages;
+			}
+			if (roundColumnDownProportion == 1)
+			{
+				roundColumnDownProportion = Math.floor(numColumnPages-1) / numColumnPages;
+			}
+			
+			// Check which rounding down is smaller, but in the case of very small roundings
+			// try the other dimension instead
+			var scaleChange = 1;
+			
+			// Use the higher of the two values
+			if (roundRowDownProportion > roundColumnDownProportion)
+			{
+				scaleChange = roundRowDownProportion;
+			}
+			else
+			{
+				scaleChange = roundColumnDownProportion;
+			}
+
+			numRowPages = numRowPages * scaleChange;
+			numColumnPages = numColumnPages * scaleChange;
+			currentTotalPages = Math.ceil(numRowPages) * Math.ceil(numColumnPages);
+			
+			numLoops++;
+			
+			if (numLoops > 10)
+			{
+				break;
+			}
+		}
+
+		// Work out the scale from the number of row pages required
+		// The column pages will give the same value
+		var posterWidth = availablePageWidth * numRowPages;
+		scale = posterWidth / graphWidth;
+		
+		// Allow for rounding errors
+		return scale * 0.99999;
+	},
+	
+	/**
+	 * Function: show
+	 * 
+	 * Copies the styles and the markup from the graph's container into the
+	 * given document and removes all cursor styles. The document is returned.
+	 * 
+	 * This function should be called from within the document with the graph.
+	 * If you experience problems with missing stylesheets in IE then try adding
+	 * the domain to the trusted sites.
+	 * 
+	 * Parameters:
+	 * 
+	 * graph - <mxGraph> to be copied.
+	 * doc - Document where the new graph is created.
+	 * x0 - X-coordinate of the graph view origin. Default is 0.
+	 * y0 - Y-coordinate of the graph view origin. Default is 0.
+	 * w - Optional width of the graph view.
+	 * h - Optional height of the graph view.
+	 */
+	show: function(graph, doc, x0, y0, w, h)
+	{
+		x0 = (x0 != null) ? x0 : 0;
+		y0 = (y0 != null) ? y0 : 0;
+		
+		if (doc == null)
+		{
+			var wnd = window.open();
+			doc = wnd.document;
+		}
+		else
+		{
+			doc.open();
+		}
+
+		// Workaround for missing print output in IE9 standards
+		if (document.documentMode == 9)
+		{
+			doc.writeln('<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=9"><![endif]-->');
+		}
+		
+		var bounds = graph.getGraphBounds();
+		var dx = Math.ceil(x0 - bounds.x);
+		var dy = Math.ceil(y0 - bounds.y);
+		
+		if (w == null)
+		{
+			w = Math.ceil(bounds.width + x0) + Math.ceil(Math.ceil(bounds.x) - bounds.x);
+		}
+		
+		if (h == null)
+		{
+			h = Math.ceil(bounds.height + y0) + Math.ceil(Math.ceil(bounds.y) - bounds.y);
+		}
+		
+		// Needs a special way of creating the page so that no click is required
+		// to refresh the contents after the external CSS styles have been loaded.
+		// To avoid a click or programmatic refresh, the styleSheets[].cssText
+		// property is copied over from the original document.
+		if (mxClient.IS_IE || document.documentMode == 11)
+		{
+			var html = '<html><head>';
+
+			var base = document.getElementsByTagName('base');
+			
+			for (var i = 0; i < base.length; i++)
+			{
+				html += base[i].outerHTML;
+			}
+
+			html += '<style>';
+
+			// Copies the stylesheets without having to load them again
+			for (var i = 0; i < document.styleSheets.length; i++)
+			{
+				try
+				{
+					html += document.styleSheets[i].cssText;
+				}
+				catch (e)
+				{
+					// ignore security exception
+				}
+			}
+
+			html += '</style></head><body style="margin:0px;">';
+			
+			// Copies the contents of the graph container
+			html += '<div style="position:absolute;overflow:hidden;width:' + w + 'px;height:' + h + 'px;"><div style="position:relative;left:' + dx + 'px;top:' + dy + 'px;">';
+			html += graph.container.innerHTML;
+			html += '</div></div></body><html>';
+
+			doc.writeln(html);
+			doc.close();
+		}
+		else
+		{
+			doc.writeln('<html><head>');
+			
+			var base = document.getElementsByTagName('base');
+			
+			for (var i = 0; i < base.length; i++)
+			{
+				doc.writeln(mxUtils.getOuterHtml(base[i]));
+			}
+			
+			var links = document.getElementsByTagName('link');
+			
+			for (var i = 0; i < links.length; i++)
+			{
+				doc.writeln(mxUtils.getOuterHtml(links[i]));
+			}
+	
+			var styles = document.getElementsByTagName('style');
+			
+			for (var i = 0; i < styles.length; i++)
+			{
+				doc.writeln(mxUtils.getOuterHtml(styles[i]));
+			}
+
+			doc.writeln('</head><body style="margin:0px;"></body></html>');
+			doc.close();
+
+			var outer = doc.createElement('div');
+			outer.position = 'absolute';
+			outer.overflow = 'hidden';
+			outer.style.width = w + 'px';
+			outer.style.height = h + 'px';
+
+			// Required for HTML labels if foreignObjects are disabled
+			var div = doc.createElement('div');
+			div.style.position = 'absolute';
+			div.style.left = dx + 'px';
+			div.style.top = dy + 'px';
+
+			var node = graph.container.firstChild;
+			var svg = null;
+			
+			while (node != null)
+			{
+				var clone = node.cloneNode(true);
+				
+				if (node == graph.view.drawPane.ownerSVGElement)
+				{
+					outer.appendChild(clone);
+					svg = clone;
+				}
+				else
+				{
+					div.appendChild(clone);
+				}
+				
+				node = node.nextSibling;
+			}
+
+			doc.body.appendChild(outer);
+			
+			if (div.firstChild != null)
+			{
+				doc.body.appendChild(div);
+			}
+						
+			if (svg != null)
+			{
+				svg.style.minWidth = '';
+				svg.style.minHeight = '';
+				svg.firstChild.setAttribute('transform', 'translate(' + dx + ',' + dy + ')');
+			}
+		}
+		
+		mxUtils.removeCursors(doc.body);
+	
+		return doc;
+	},
+	
+	/**
+	 * Function: printScreen
+	 * 
+	 * Prints the specified graph using a new window and the built-in print
+	 * dialog.
+	 * 
+	 * This function should be called from within the document with the graph.
+	 * 
+	 * Parameters:
+	 * 
+	 * graph - <mxGraph> to be printed.
+	 */
+	printScreen: function(graph)
+	{
+		var wnd = window.open();
+		var bounds = graph.getGraphBounds();
+		mxUtils.show(graph, wnd.document);
+		
+		var print = function()
+		{
+			wnd.focus();
+			wnd.print();
+			wnd.close();
+		};
+		
+		// Workaround for Google Chrome which needs a bit of a
+		// delay in order to render the SVG contents
+		if (mxClient.IS_GC)
+		{
+			wnd.setTimeout(print, 500);
+		}
+		else
+		{
+			print();
+		}
+	},
+	
+	/**
+	 * Function: popup
+	 * 
+	 * Shows the specified text content in a new <mxWindow> or a new browser
+	 * window if isInternalWindow is false.
+	 * 
+	 * Parameters:
+	 * 
+	 * content - String that specifies the text to be displayed.
+	 * isInternalWindow - Optional boolean indicating if an mxWindow should be
+	 * used instead of a new browser window. Default is false.
+	 */
+	popup: function(content, isInternalWindow)
+	{
+	   	if (isInternalWindow)
+	   	{
+			var div = document.createElement('div');
+			
+			div.style.overflow = 'scroll';
+			div.style.width = '636px';
+			div.style.height = '460px';
+			
+			var pre = document.createElement('pre');
+		    pre.innerHTML = mxUtils.htmlEntities(content, false).
+		    	replace(/\n/g,'<br>').replace(/ /g, '&nbsp;');
+			
+			div.appendChild(pre);
+			
+			var w = document.body.clientWidth;
+			var h = Math.max(document.body.clientHeight || 0, document.documentElement.clientHeight)
+			var wnd = new mxWindow('Popup Window', div,
+				w/2-320, h/2-240, 640, 480, false, true);
+
+			wnd.setClosable(true);
+			wnd.setVisible(true);
+		}
+		else
+		{
+			// Wraps up the XML content in a textarea
+			if (mxClient.IS_NS)
+			{
+			    var wnd = window.open();
+				wnd.document.writeln('<pre>'+mxUtils.htmlEntities(content)+'</pre');
+			   	wnd.document.close();
+			}
+			else
+			{
+			    var wnd = window.open();
+			    var pre = wnd.document.createElement('pre');
+			    pre.innerHTML = mxUtils.htmlEntities(content, false).
+			    	replace(/\n/g,'<br>').replace(/ /g, '&nbsp;');
+			   	wnd.document.body.appendChild(pre);
+			}
+	   	}
+	},
+	
+	/**
+	 * Function: alert
+	 * 
+	 * Displayss the given alert in a new dialog. This implementation uses the
+	 * built-in alert function. This is used to display validation errors when
+	 * connections cannot be changed or created.
+	 * 
+	 * Parameters:
+	 * 
+	 * message - String specifying the message to be displayed.
+	 */
+	alert: function(message)
+	{
+		alert(message);
+	},
+	
+	/**
+	 * Function: prompt
+	 * 
+	 * Displays the given message in a prompt dialog. This implementation uses
+	 * the built-in prompt function.
+	 * 
+	 * Parameters:
+	 * 
+	 * message - String specifying the message to be displayed.
+	 * defaultValue - Optional string specifying the default value.
+	 */
+	prompt: function(message, defaultValue)
+	{
+		return prompt(message, (defaultValue != null) ? defaultValue : '');
+	},
+	
+	/**
+	 * Function: confirm
+	 * 
+	 * Displays the given message in a confirm dialog. This implementation uses
+	 * the built-in confirm function.
+	 * 
+	 * Parameters:
+	 * 
+	 * message - String specifying the message to be displayed.
+	 */
+	confirm: function(message)
+	{
+		return confirm(message);
+	},
+
+	/**
+	 * Function: error
+	 * 
+	 * Displays the given error message in a new <mxWindow> of the given width.
+	 * If close is true then an additional close button is added to the window.
+	 * The optional icon specifies the icon to be used for the window. Default
+	 * is <mxUtils.errorImage>.
+	 * 
+	 * Parameters:
+	 * 
+	 * message - String specifying the message to be displayed.
+	 * width - Integer specifying the width of the window.
+	 * close - Optional boolean indicating whether to add a close button.
+	 * icon - Optional icon for the window decoration.
+	 */
+	error: function(message, width, close, icon)
+	{
+		var div = document.createElement('div');
+		div.style.padding = '20px';
+
+		var img = document.createElement('img');
+		img.setAttribute('src', icon || mxUtils.errorImage);
+		img.setAttribute('valign', 'bottom');
+		img.style.verticalAlign = 'middle';
+		div.appendChild(img);
+
+		div.appendChild(document.createTextNode('\u00a0')); // &nbsp;
+		div.appendChild(document.createTextNode('\u00a0')); // &nbsp;
+		div.appendChild(document.createTextNode('\u00a0')); // &nbsp;
+		mxUtils.write(div, message);
+
+		var w = document.body.clientWidth;
+		var h = (document.body.clientHeight || document.documentElement.clientHeight);
+		var warn = new mxWindow(mxResources.get(mxUtils.errorResource) ||
+			mxUtils.errorResource, div, (w-width)/2, h/4, width, null,
+			false, true);
+
+		if (close)
+		{
+			mxUtils.br(div);
+			
+			var tmp = document.createElement('p');
+			var button = document.createElement('button');
+
+			if (mxClient.IS_IE)
+			{
+				button.style.cssText = 'float:right';
+			}
+			else
+			{
+				button.setAttribute('style', 'float:right');
+			}
+
+			mxEvent.addListener(button, 'click', function(evt)
+			{
+				warn.destroy();
+			});
+
+			mxUtils.write(button, mxResources.get(mxUtils.closeResource) ||
+				mxUtils.closeResource);
+			
+			tmp.appendChild(button);
+			div.appendChild(tmp);
+			
+			mxUtils.br(div);
+			
+			warn.setClosable(true);
+		}
+		
+		warn.setVisible(true);
+		
+		return warn;
+	},
+
+	/**
+	 * Function: makeDraggable
+	 * 
+	 * Configures the given DOM element to act as a drag source for the
+	 * specified graph. Returns a a new <mxDragSource>. If
+	 * <mxDragSource.guideEnabled> is enabled then the x and y arguments must
+	 * be used in funct to match the preview location.
+	 * 
+	 * Example:
+	 * 
+	 * (code)
+	 * var funct = function(graph, evt, cell, x, y)
+	 * {
+	 *   if (graph.canImportCell(cell))
+	 *   {
+	 *     var parent = graph.getDefaultParent();
+	 *     var vertex = null;
+	 *     
+	 *     graph.getModel().beginUpdate();
+	 *     try
+	 *     {
+	 * 	     vertex = graph.insertVertex(parent, null, 'Hello', x, y, 80, 30);
+	 *     }
+	 *     finally
+	 *     {
+	 *       graph.getModel().endUpdate();
+	 *     }
+	 *
+	 *     graph.setSelectionCell(vertex);
+	 *   }
+	 * }
+	 * 
+	 * var img = document.createElement('img');
+	 * img.setAttribute('src', 'editors/images/rectangle.gif');
+	 * img.style.position = 'absolute';
+	 * img.style.left = '0px';
+	 * img.style.top = '0px';
+	 * img.style.width = '16px';
+	 * img.style.height = '16px';
+	 * 
+	 * var dragImage = img.cloneNode(true);
+	 * dragImage.style.width = '32px';
+	 * dragImage.style.height = '32px';
+	 * mxUtils.makeDraggable(img, graph, funct, dragImage);
+	 * document.body.appendChild(img);
+	 * (end)
+	 * 
+	 * Parameters:
+	 * 
+	 * element - DOM element to make draggable.
+	 * graphF - <mxGraph> that acts as the drop target or a function that takes a
+	 * mouse event and returns the current <mxGraph>.
+	 * funct - Function to execute on a successful drop.
+	 * dragElement - Optional DOM node to be used for the drag preview.
+	 * dx - Optional horizontal offset between the cursor and the drag
+	 * preview.
+	 * dy - Optional vertical offset between the cursor and the drag
+	 * preview.
+	 * autoscroll - Optional boolean that specifies if autoscroll should be
+	 * used. Default is mxGraph.autoscroll.
+	 * scalePreview - Optional boolean that specifies if the preview element
+	 * should be scaled according to the graph scale. If this is true, then
+	 * the offsets will also be scaled. Default is false.
+	 * highlightDropTargets - Optional boolean that specifies if dropTargets
+	 * should be highlighted. Default is true.
+	 * getDropTarget - Optional function to return the drop target for a given
+	 * location (x, y). Default is mxGraph.getCellAt.
+	 */
+	makeDraggable: function(element, graphF, funct, dragElement, dx, dy, autoscroll,
+			scalePreview, highlightDropTargets, getDropTarget)
+	{
+		var dragSource = new mxDragSource(element, funct);
+		dragSource.dragOffset = new mxPoint((dx != null) ? dx : 0,
+			(dy != null) ? dy : mxConstants.TOOLTIP_VERTICAL_OFFSET);
+		dragSource.autoscroll = autoscroll;
+		
+		// Cannot enable this by default. This needs to be enabled in the caller
+		// if the funct argument uses the new x- and y-arguments.
+		dragSource.setGuidesEnabled(false);
+		
+		if (highlightDropTargets != null)
+		{
+			dragSource.highlightDropTargets = highlightDropTargets;
+		}
+		
+		// Overrides function to find drop target cell
+		if (getDropTarget != null)
+		{
+			dragSource.getDropTarget = getDropTarget;
+		}
+		
+		// Overrides function to get current graph
+		dragSource.getGraphForEvent = function(evt)
+		{
+			return (typeof(graphF) == 'function') ? graphF(evt) : graphF;
+		};
+		
+		// Translates switches into dragSource customizations
+		if (dragElement != null)
+		{
+			dragSource.createDragElement = function()
+			{
+				return dragElement.cloneNode(true);
+			};
+			
+			if (scalePreview)
+			{
+				dragSource.createPreviewElement = function(graph)
+				{
+					var elt = dragElement.cloneNode(true);
+
+					var w = parseInt(elt.style.width);
+					var h = parseInt(elt.style.height);
+					elt.style.width = Math.round(w * graph.view.scale) + 'px';
+					elt.style.height = Math.round(h * graph.view.scale) + 'px';
+					
+					return elt;
+				};
+			}
+		}
+		
+		return dragSource;
+	}
+
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+ var mxConstants =
+ {
+	/**
+	 * Class: mxConstants
+	 * 
+	 * Defines various global constants.
+	 * 
+	 * Variable: DEFAULT_HOTSPOT
+	 * 
+	 * Defines the portion of the cell which is to be used as a connectable
+	 * region. Default is 0.3. Possible values are 0 < x <= 1. 
+	 */
+	DEFAULT_HOTSPOT: 0.3,
+
+	/**
+	 * Variable: MIN_HOTSPOT_SIZE
+	 * 
+	 * Defines the minimum size in pixels of the portion of the cell which is
+	 * to be used as a connectable region. Default is 8.
+	 */
+	MIN_HOTSPOT_SIZE: 8,
+
+	/**
+	 * Variable: MAX_HOTSPOT_SIZE
+	 * 
+	 * Defines the maximum size in pixels of the portion of the cell which is
+	 * to be used as a connectable region. Use 0 for no maximum. Default is 0.
+	 */
+	MAX_HOTSPOT_SIZE: 0,
+
+	/**
+	 * Variable: RENDERING_HINT_EXACT
+	 * 
+	 * Defines the exact rendering hint.
+	 */
+	RENDERING_HINT_EXACT: 'exact',
+
+	/**
+	 * Variable: RENDERING_HINT_FASTER
+	 * 
+	 * Defines the faster rendering hint.
+	 */
+	RENDERING_HINT_FASTER: 'faster',
+
+	/**
+	 * Variable: RENDERING_HINT_FASTEST
+	 * 
+	 * Defines the fastest rendering hint.
+	 */
+	RENDERING_HINT_FASTEST: 'fastest',
+
+	/**
+	 * Variable: DIALECT_SVG
+	 * 
+	 * Defines the SVG display dialect name.
+	 */
+	DIALECT_SVG: 'svg',
+
+	/**
+	 * Variable: DIALECT_VML
+	 * 
+	 * Defines the VML display dialect name.
+	 */
+	DIALECT_VML: 'vml',
+
+	/**
+	 * Variable: DIALECT_MIXEDHTML
+	 * 
+	 * Defines the mixed HTML display dialect name.
+	 */
+	DIALECT_MIXEDHTML: 'mixedHtml',
+
+	/**
+	 * Variable: DIALECT_PREFERHTML
+	 * 
+	 * Defines the preferred HTML display dialect name.
+	 */
+	DIALECT_PREFERHTML: 'preferHtml',
+
+	/**
+	 * Variable: DIALECT_STRICTHTML
+	 * 
+	 * Defines the strict HTML display dialect.
+	 */
+	DIALECT_STRICTHTML: 'strictHtml',
+
+	/**
+	 * Variable: NS_SVG
+	 * 
+	 * Defines the SVG namespace.
+	 */
+	NS_SVG: 'http://www.w3.org/2000/svg',
+
+	/**
+	 * Variable: NS_XHTML
+	 * 
+	 * Defines the XHTML namespace.
+	 */
+	NS_XHTML: 'http://www.w3.org/1999/xhtml',
+
+	/**
+	 * Variable: NS_XLINK
+	 * 
+	 * Defines the XLink namespace.
+	 */
+	NS_XLINK: 'http://www.w3.org/1999/xlink',
+
+	/**
+	 * Variable: SHADOWCOLOR
+	 * 
+	 * Defines the color to be used to draw shadows in shapes and windows.
+	 * Default is gray.
+	 */
+	SHADOWCOLOR: 'gray',
+
+	/**
+	 * Variable: VML_SHADOWCOLOR
+	 * 
+	 * Used for shadow color in filters where transparency is not supported
+	 * (Microsoft Internet Explorer). Default is gray.
+	 */
+	VML_SHADOWCOLOR: 'gray',
+
+	/**
+	 * Variable: SHADOW_OFFSET_X
+	 * 
+	 * Specifies the x-offset of the shadow. Default is 2.
+	 */
+	SHADOW_OFFSET_X: 2,
+
+	/**
+	 * Variable: SHADOW_OFFSET_Y
+	 * 
+	 * Specifies the y-offset of the shadow. Default is 3.
+	 */
+	SHADOW_OFFSET_Y: 3,
+	
+	/**
+	 * Variable: SHADOW_OPACITY
+	 * 
+	 * Defines the opacity for shadows. Default is 1.
+	 */
+	SHADOW_OPACITY: 1,
+ 
+	/**
+	 * Variable: NODETYPE_ELEMENT
+	 * 
+	 * DOM node of type ELEMENT.
+	 */
+	NODETYPE_ELEMENT: 1,
+
+	/**
+	 * Variable: NODETYPE_ATTRIBUTE
+	 * 
+	 * DOM node of type ATTRIBUTE.
+	 */
+	NODETYPE_ATTRIBUTE: 2,
+
+	/**
+	 * Variable: NODETYPE_TEXT
+	 * 
+	 * DOM node of type TEXT.
+	 */
+	NODETYPE_TEXT: 3,
+
+	/**
+	 * Variable: NODETYPE_CDATA
+	 * 
+	 * DOM node of type CDATA.
+	 */
+	NODETYPE_CDATA: 4,
+	
+	/**
+	 * Variable: NODETYPE_ENTITY_REFERENCE
+	 * 
+	 * DOM node of type ENTITY_REFERENCE.
+	 */
+	NODETYPE_ENTITY_REFERENCE: 5,
+
+	/**
+	 * Variable: NODETYPE_ENTITY
+	 * 
+	 * DOM node of type ENTITY.
+	 */
+	NODETYPE_ENTITY: 6,
+
+	/**
+	 * Variable: NODETYPE_PROCESSING_INSTRUCTION
+	 * 
+	 * DOM node of type PROCESSING_INSTRUCTION.
+	 */
+	NODETYPE_PROCESSING_INSTRUCTION: 7,
+
+	/**
+	 * Variable: NODETYPE_COMMENT
+	 * 
+	 * DOM node of type COMMENT.
+	 */
+	NODETYPE_COMMENT: 8,
+		
+	/**
+	 * Variable: NODETYPE_DOCUMENT
+	 * 
+	 * DOM node of type DOCUMENT.
+	 */
+	NODETYPE_DOCUMENT: 9,
+
+	/**
+	 * Variable: NODETYPE_DOCUMENTTYPE
+	 * 
+	 * DOM node of type DOCUMENTTYPE.
+	 */
+	NODETYPE_DOCUMENTTYPE: 10,
+
+	/**
+	 * Variable: NODETYPE_DOCUMENT_FRAGMENT
+	 * 
+	 * DOM node of type DOCUMENT_FRAGMENT.
+	 */
+	NODETYPE_DOCUMENT_FRAGMENT: 11,
+
+	/**
+	 * Variable: NODETYPE_NOTATION
+	 * 
+	 * DOM node of type NOTATION.
+	 */
+	NODETYPE_NOTATION: 12,
+	
+	/**
+	 * Variable: TOOLTIP_VERTICAL_OFFSET
+	 * 
+	 * Defines the vertical offset for the tooltip.
+	 * Default is 16.
+	 */
+	TOOLTIP_VERTICAL_OFFSET: 16,
+
+	/**
+	 * Variable: DEFAULT_VALID_COLOR
+	 * 
+	 * Specifies the default valid color. Default is #0000FF.
+	 */
+	DEFAULT_VALID_COLOR: '#00FF00',
+
+	/**
+	 * Variable: DEFAULT_INVALID_COLOR
+	 * 
+	 * Specifies the default invalid color. Default is #FF0000.
+	 */
+	DEFAULT_INVALID_COLOR: '#FF0000',
+
+	/**
+	 * Variable: OUTLINE_HIGHLIGHT_COLOR
+	 * 
+	 * Specifies the default highlight color for shape outlines.
+	 * Default is #0000FF. This is used in <mxEdgeHandler>.
+	 */
+	OUTLINE_HIGHLIGHT_COLOR: '#00FF00',
+
+	/**
+	 * Variable: OUTLINE_HIGHLIGHT_COLOR
+	 * 
+	 * Defines the strokewidth to be used for shape outlines.
+	 * Default is 5. This is used in <mxEdgeHandler>.
+	 */
+	OUTLINE_HIGHLIGHT_STROKEWIDTH: 5,
+
+	/**
+	 * Variable: HIGHLIGHT_STROKEWIDTH
+	 * 
+	 * Defines the strokewidth to be used for the highlights.
+	 * Default is 3.
+	 */
+	HIGHLIGHT_STROKEWIDTH: 3,
+
+	/**
+	 * Variable: CONSTRAINT_HIGHLIGHT_SIZE
+	 * 
+	 * Size of the constraint highlight (in px). Default is 2.
+	 */
+	HIGHLIGHT_SIZE: 2,
+	
+	/**
+	 * Variable: HIGHLIGHT_OPACITY
+	 * 
+	 * Opacity (in %) used for the highlights (including outline).
+	 * Default is 100.
+	 */
+	HIGHLIGHT_OPACITY: 100,
+	
+	/**
+	 * Variable: CURSOR_MOVABLE_VERTEX
+	 * 
+	 * Defines the cursor for a movable vertex. Default is 'move'.
+	 */
+	CURSOR_MOVABLE_VERTEX: 'move',
+	
+	/**
+	 * Variable: CURSOR_MOVABLE_EDGE
+	 * 
+	 * Defines the cursor for a movable edge. Default is 'move'.
+	 */
+	CURSOR_MOVABLE_EDGE: 'move',
+	
+	/**
+	 * Variable: CURSOR_LABEL_HANDLE
+	 * 
+	 * Defines the cursor for a movable label. Default is 'default'.
+	 */
+	CURSOR_LABEL_HANDLE: 'default',
+	
+	/**
+	 * Variable: CURSOR_TERMINAL_HANDLE
+	 * 
+	 * Defines the cursor for a terminal handle. Default is 'pointer'.
+	 */
+	CURSOR_TERMINAL_HANDLE: 'pointer',
+	
+	/**
+	 * Variable: CURSOR_BEND_HANDLE
+	 * 
+	 * Defines the cursor for a movable bend. Default is 'crosshair'.
+	 */
+	CURSOR_BEND_HANDLE: 'crosshair',
+
+	/**
+	 * Variable: CURSOR_VIRTUAL_BEND_HANDLE
+	 * 
+	 * Defines the cursor for a movable bend. Default is 'crosshair'.
+	 */
+	CURSOR_VIRTUAL_BEND_HANDLE: 'crosshair',
+	
+	/**
+	 * Variable: CURSOR_CONNECT
+	 * 
+	 * Defines the cursor for a connectable state. Default is 'pointer'.
+	 */
+	CURSOR_CONNECT: 'pointer',
+
+	/**
+	 * Variable: HIGHLIGHT_COLOR
+	 * 
+	 * Defines the color to be used for the cell highlighting.
+	 * Use 'none' for no color. Default is #00FF00.
+	 */
+	HIGHLIGHT_COLOR: '#00FF00',
+
+	/**
+	 * Variable: TARGET_HIGHLIGHT_COLOR
+	 * 
+	 * Defines the color to be used for highlighting a target cell for a new
+	 * or changed connection. Note that this may be either a source or
+	 * target terminal in the graph. Use 'none' for no color.
+	 * Default is #0000FF.
+	 */
+	CONNECT_TARGET_COLOR: '#0000FF',
+
+	/**
+	 * Variable: INVALID_CONNECT_TARGET_COLOR
+	 * 
+	 * Defines the color to be used for highlighting a invalid target cells
+	 * for a new or changed connections. Note that this may be either a source
+	 * or target terminal in the graph. Use 'none' for no color. Default is
+	 * #FF0000.
+	 */
+	INVALID_CONNECT_TARGET_COLOR: '#FF0000',
+
+	/**
+	 * Variable: DROP_TARGET_COLOR
+	 * 
+	 * Defines the color to be used for the highlighting target parent cells
+	 * (for drag and drop). Use 'none' for no color. Default is #0000FF.
+	 */
+	DROP_TARGET_COLOR: '#0000FF',
+
+	/**
+	 * Variable: VALID_COLOR
+	 * 
+	 * Defines the color to be used for the coloring valid connection
+	 * previews. Use 'none' for no color. Default is #FF0000.
+	 */
+	VALID_COLOR: '#00FF00',
+
+	/**
+	 * Variable: INVALID_COLOR
+	 * 
+	 * Defines the color to be used for the coloring invalid connection
+	 * previews. Use 'none' for no color. Default is #FF0000.
+	 */
+	INVALID_COLOR: '#FF0000',
+
+	/**
+	 * Variable: EDGE_SELECTION_COLOR
+	 * 
+	 * Defines the color to be used for the selection border of edges. Use
+	 * 'none' for no color. Default is #00FF00.
+	 */
+	EDGE_SELECTION_COLOR: '#00FF00',
+
+	/**
+	 * Variable: VERTEX_SELECTION_COLOR
+	 * 
+	 * Defines the color to be used for the selection border of vertices. Use
+	 * 'none' for no color. Default is #00FF00.
+	 */
+	VERTEX_SELECTION_COLOR: '#00FF00',
+
+	/**
+	 * Variable: VERTEX_SELECTION_STROKEWIDTH
+	 * 
+	 * Defines the strokewidth to be used for vertex selections.
+	 * Default is 1.
+	 */
+	VERTEX_SELECTION_STROKEWIDTH: 1,
+
+	/**
+	 * Variable: EDGE_SELECTION_STROKEWIDTH
+	 * 
+	 * Defines the strokewidth to be used for edge selections.
+	 * Default is 1.
+	 */
+	EDGE_SELECTION_STROKEWIDTH: 1,
+
+	/**
+	 * Variable: SELECTION_DASHED
+	 * 
+	 * Defines the dashed state to be used for the vertex selection
+	 * border. Default is true.
+	 */
+	VERTEX_SELECTION_DASHED: true,
+
+	/**
+	 * Variable: SELECTION_DASHED
+	 * 
+	 * Defines the dashed state to be used for the edge selection
+	 * border. Default is true.
+	 */
+	EDGE_SELECTION_DASHED: true,
+
+	/**
+	 * Variable: GUIDE_COLOR
+	 * 
+	 * Defines the color to be used for the guidelines in mxGraphHandler.
+	 * Default is #FF0000.
+	 */
+	GUIDE_COLOR: '#FF0000',
+
+	/**
+	 * Variable: GUIDE_STROKEWIDTH
+	 * 
+	 * Defines the strokewidth to be used for the guidelines in mxGraphHandler.
+	 * Default is 1.
+	 */
+	GUIDE_STROKEWIDTH: 1,
+
+	/**
+	 * Variable: OUTLINE_COLOR
+	 * 
+	 * Defines the color to be used for the outline rectangle
+	 * border.  Use 'none' for no color. Default is #0099FF.
+	 */
+	OUTLINE_COLOR: '#0099FF',
+
+	/**
+	 * Variable: OUTLINE_STROKEWIDTH
+	 * 
+	 * Defines the strokewidth to be used for the outline rectangle
+	 * stroke width. Default is 3.
+	 */
+	OUTLINE_STROKEWIDTH: (mxClient.IS_IE) ? 2 : 3,
+
+	/**
+	 * Variable: HANDLE_SIZE
+	 * 
+	 * Defines the default size for handles. Default is 6.
+	 */
+	HANDLE_SIZE: 6,
+
+	/**
+	 * Variable: LABEL_HANDLE_SIZE
+	 * 
+	 * Defines the default size for label handles. Default is 4.
+	 */
+	LABEL_HANDLE_SIZE: 4,
+
+	/**
+	 * Variable: HANDLE_FILLCOLOR
+	 * 
+	 * Defines the color to be used for the handle fill color. Use 'none' for
+	 * no color. Default is #00FF00 (green).
+	 */
+	HANDLE_FILLCOLOR: '#00FF00',
+
+	/**
+	 * Variable: HANDLE_STROKECOLOR
+	 * 
+	 * Defines the color to be used for the handle stroke color. Use 'none' for
+	 * no color. Default is black.
+	 */
+	HANDLE_STROKECOLOR: 'black',
+
+	/**
+	 * Variable: LABEL_HANDLE_FILLCOLOR
+	 * 
+	 * Defines the color to be used for the label handle fill color. Use 'none'
+	 * for no color. Default is yellow.
+	 */
+	LABEL_HANDLE_FILLCOLOR: 'yellow',
+
+	/**
+	 * Variable: CONNECT_HANDLE_FILLCOLOR
+	 * 
+	 * Defines the color to be used for the connect handle fill color. Use
+	 * 'none' for no color. Default is #0000FF (blue).
+	 */
+	CONNECT_HANDLE_FILLCOLOR: '#0000FF',
+
+	/**
+	 * Variable: LOCKED_HANDLE_FILLCOLOR
+	 * 
+	 * Defines the color to be used for the locked handle fill color. Use
+	 * 'none' for no color. Default is #FF0000 (red).
+	 */
+	LOCKED_HANDLE_FILLCOLOR: '#FF0000',
+
+	/**
+	 * Variable: OUTLINE_HANDLE_FILLCOLOR
+	 * 
+	 * Defines the color to be used for the outline sizer fill color. Use
+	 * 'none' for no color. Default is #00FFFF.
+	 */
+	OUTLINE_HANDLE_FILLCOLOR: '#00FFFF',
+
+	/**
+	 * Variable: OUTLINE_HANDLE_STROKECOLOR
+	 * 
+	 * Defines the color to be used for the outline sizer stroke color. Use
+	 * 'none' for no color. Default is #0033FF.
+	 */
+	OUTLINE_HANDLE_STROKECOLOR: '#0033FF',
+
+	/**
+	 * Variable: DEFAULT_FONTFAMILY
+	 * 
+	 * Defines the default family for all fonts. Default is Arial,Helvetica.
+	 */
+	DEFAULT_FONTFAMILY: 'Arial,Helvetica',
+
+	/**
+	 * Variable: DEFAULT_FONTSIZE
+	 * 
+	 * Defines the default size (in px). Default is 11.
+	 */
+	DEFAULT_FONTSIZE: 11,
+
+	/**
+	 * Variable: DEFAULT_TEXT_DIRECTION
+	 * 
+	 * Defines the default value for the <STYLE_TEXT_DIRECTION> if no value is
+	 * defined for it in the style. Default value is an empty string which means
+	 * the default system setting is used and no direction is set.
+	 */
+	DEFAULT_TEXT_DIRECTION: '',
+
+	/**
+	 * Variable: LINE_HEIGHT
+	 * 
+	 * Defines the default line height for text labels. Default is 1.2.
+	 */
+	LINE_HEIGHT: 1.2,
+
+	/**
+	 * Variable: WORD_WRAP
+	 * 
+	 * Defines the CSS value for the word-wrap property. Default is "normal".
+	 * Change this to "break-word" to allow long words to be able to be broken
+	 * and wrap onto the next line.
+	 */
+	WORD_WRAP: 'normal',
+
+	/**
+	 * Variable: ABSOLUTE_LINE_HEIGHT
+	 * 
+	 * Specifies if absolute line heights should be used (px) in CSS. Default
+	 * is false. Set this to true for backwards compatibility.
+	 */
+	ABSOLUTE_LINE_HEIGHT: false,
+
+	/**
+	 * Variable: DEFAULT_FONTSTYLE
+	 * 
+	 * Defines the default style for all fonts. Default is 0. This can be set
+	 * to any combination of font styles as follows.
+	 * 
+	 * (code)
+	 * mxConstants.DEFAULT_FONTSTYLE = mxConstants.FONT_BOLD | mxConstants.FONT_ITALIC;
+	 * (end)
+	 */
+	DEFAULT_FONTSTYLE: 0,
+
+	/**
+	 * Variable: DEFAULT_STARTSIZE
+	 * 
+	 * Defines the default start size for swimlanes. Default is 40.
+	 */
+	DEFAULT_STARTSIZE: 40,
+
+	/**
+	 * Variable: DEFAULT_MARKERSIZE
+	 * 
+	 * Defines the default size for all markers. Default is 6.
+	 */
+	DEFAULT_MARKERSIZE: 6,
+
+	/**
+	 * Variable: DEFAULT_IMAGESIZE
+	 * 
+	 * Defines the default width and height for images used in the
+	 * label shape. Default is 24.
+	 */
+	DEFAULT_IMAGESIZE: 24,
+
+	/**
+	 * Variable: ENTITY_SEGMENT
+	 * 
+	 * Defines the length of the horizontal segment of an Entity Relation.
+	 * This can be overridden using <mxConstants.STYLE_SEGMENT> style.
+	 * Default is 30.
+	 */
+	ENTITY_SEGMENT: 30,
+
+	/**
+	 * Variable: RECTANGLE_ROUNDING_FACTOR
+	 * 
+	 * Defines the rounding factor for rounded rectangles in percent between
+	 * 0 and 1. Values should be smaller than 0.5. Default is 0.15.
+	 */
+	RECTANGLE_ROUNDING_FACTOR: 0.15,
+
+	/**
+	 * Variable: LINE_ARCSIZE
+	 * 
+	 * Defines the size of the arcs for rounded edges. Default is 20.
+	 */
+	LINE_ARCSIZE: 20,
+
+	/**
+	 * Variable: ARROW_SPACING
+	 * 
+	 * Defines the spacing between the arrow shape and its terminals. Default is 0.
+	 */
+	ARROW_SPACING: 0,
+
+	/**
+	 * Variable: ARROW_WIDTH
+	 * 
+	 * Defines the width of the arrow shape. Default is 30.
+	 */
+	ARROW_WIDTH: 30,
+
+	/**
+	 * Variable: ARROW_SIZE
+	 * 
+	 * Defines the size of the arrowhead in the arrow shape. Default is 30.
+	 */
+	ARROW_SIZE: 30,
+
+	/**
+	 * Variable: PAGE_FORMAT_A4_PORTRAIT
+	 * 
+	 * Defines the rectangle for the A4 portrait page format. The dimensions
+	 * of this page format are 826x1169 pixels.
+	 */
+	PAGE_FORMAT_A4_PORTRAIT: new mxRectangle(0, 0, 827, 1169),
+
+	/**
+	 * Variable: PAGE_FORMAT_A4_PORTRAIT
+	 * 
+	 * Defines the rectangle for the A4 portrait page format. The dimensions
+	 * of this page format are 826x1169 pixels.
+	 */
+	PAGE_FORMAT_A4_LANDSCAPE: new mxRectangle(0, 0, 1169, 827),
+
+	/**
+	 * Variable: PAGE_FORMAT_LETTER_PORTRAIT
+	 * 
+	 * Defines the rectangle for the Letter portrait page format. The
+	 * dimensions of this page format are 850x1100 pixels.
+	 */
+	PAGE_FORMAT_LETTER_PORTRAIT: new mxRectangle(0, 0, 850, 1100),
+
+	/**
+	 * Variable: PAGE_FORMAT_LETTER_PORTRAIT
+	 * 
+	 * Defines the rectangle for the Letter portrait page format. The dimensions
+	 * of this page format are 850x1100 pixels.
+	 */
+	PAGE_FORMAT_LETTER_LANDSCAPE: new mxRectangle(0, 0, 1100, 850),
+
+	/**
+	 * Variable: NONE
+	 * 
+	 * Defines the value for none. Default is "none".
+	 */
+	NONE: 'none',
+
+	/**
+	 * Variable: STYLE_PERIMETER
+	 * 
+	 * Defines the key for the perimeter style. This is a function that defines
+	 * the perimeter around a particular shape. Possible values are the
+	 * functions defined in <mxPerimeter>. Alternatively, the constants in this
+	 * class that start with "PERIMETER_" may be used to access
+	 * perimeter styles in <mxStyleRegistry>. Value is "perimeter".
+	 */
+	STYLE_PERIMETER: 'perimeter',
+	
+	/**
+	 * Variable: STYLE_SOURCE_PORT
+	 * 
+	 * Defines the ID of the cell that should be used for computing the
+	 * perimeter point of the source for an edge. This allows for graphically
+	 * connecting to a cell while keeping the actual terminal of the edge.
+	 * Value is "sourcePort".
+	 */
+	STYLE_SOURCE_PORT: 'sourcePort',
+	
+	/**
+	 * Variable: STYLE_TARGET_PORT
+	 * 
+	 * Defines the ID of the cell that should be used for computing the
+	 * perimeter point of the target for an edge. This allows for graphically
+	 * connecting to a cell while keeping the actual terminal of the edge.
+	 * Value is "targetPort".
+	 */
+	STYLE_TARGET_PORT: 'targetPort',
+
+	/**
+	 * Variable: STYLE_PORT_CONSTRAINT
+	 * 
+	 * Defines the direction(s) that edges are allowed to connect to cells in.
+	 * Possible values are "DIRECTION_NORTH, DIRECTION_SOUTH, 
+	 * DIRECTION_EAST" and "DIRECTION_WEST". Value is
+	 * "portConstraint".
+	 */
+	STYLE_PORT_CONSTRAINT: 'portConstraint',
+
+	/**
+	 * Variable: STYLE_PORT_CONSTRAINT_ROTATION
+	 * 
+	 * Define whether port constraint directions are rotated with vertex
+	 * rotation. 0 (default) causes port constraints to remain absolute, 
+	 * relative to the graph, 1 causes the constraints to rotate with
+	 * the vertex. Value is "portConstraintRotation".
+	 */
+	STYLE_PORT_CONSTRAINT_ROTATION: 'portConstraintRotation',
+
+	/**
+	 * Variable: STYLE_SOURCE_PORT_CONSTRAINT
+	 * 
+	 * Defines the direction(s) that edges are allowed to connect to sources in.
+	 * Possible values are "DIRECTION_NORTH, DIRECTION_SOUTH, DIRECTION_EAST"
+	 * and "DIRECTION_WEST". Value is "sourcePortConstraint".
+	 */
+	STYLE_SOURCE_PORT_CONSTRAINT: 'sourcePortConstraint',
+
+	/**
+	 * Variable: STYLE_TARGET_PORT_CONSTRAINT
+	 * 
+	 * Defines the direction(s) that edges are allowed to connect to targets in.
+	 * Possible values are "DIRECTION_NORTH, DIRECTION_SOUTH, DIRECTION_EAST"
+	 * and "DIRECTION_WEST". Value is "targetPortConstraint".
+	 */
+	STYLE_TARGET_PORT_CONSTRAINT: 'targetPortConstraint',
+
+	/**
+	 * Variable: STYLE_OPACITY
+	 * 
+	 * Defines the key for the opacity style. The type of the value is 
+	 * numeric and the possible range is 0-100. Value is "opacity".
+	 */
+	STYLE_OPACITY: 'opacity',
+
+	/**
+	 * Variable: STYLE_FILL_OPACITY
+	 * 
+	 * Defines the key for the fill opacity style. The type of the value is 
+	 * numeric and the possible range is 0-100. Value is "fillOpacity".
+	 */
+	STYLE_FILL_OPACITY: 'fillOpacity',
+
+	/**
+	 * Variable: STYLE_STROKE_OPACITY
+	 * 
+	 * Defines the key for the stroke opacity style. The type of the value is 
+	 * numeric and the possible range is 0-100. Value is "strokeOpacity".
+	 */
+	STYLE_STROKE_OPACITY: 'strokeOpacity',
+
+	/**
+	 * Variable: STYLE_TEXT_OPACITY
+	 * 
+	 * Defines the key for the text opacity style. The type of the value is 
+	 * numeric and the possible range is 0-100. Value is "textOpacity".
+	 */
+	STYLE_TEXT_OPACITY: 'textOpacity',
+
+	/**
+	 * Variable: STYLE_TEXT_DIRECTION
+	 * 
+	 * Defines the key for the text direction style. Possible values are
+	 * "TEXT_DIRECTION_DEFAULT, TEXT_DIRECTION_AUTO, TEXT_DIRECTION_LTR"
+	 * and "TEXT_DIRECTION_RTL". Value is "textDirection".
+	 * The default value for the style is defined in <DEFAULT_TEXT_DIRECTION>.
+	 * It is used is no value is defined for this key in a given style. This is
+	 * an experimental style that is currently ignored in the backends.
+	 */
+	STYLE_TEXT_DIRECTION: 'textDirection',
+
+	/**
+	 * Variable: STYLE_OVERFLOW
+	 * 
+	 * Defines the key for the overflow style. Possible values are 'visible',
+	 * 'hidden', 'fill' and 'width'. The default value is 'visible'. This value
+	 * specifies how overlapping vertex labels are handled. A value of
+	 * 'visible' will show the complete label. A value of 'hidden' will clip
+	 * the label so that it does not overlap the vertex bounds. A value of
+	 * 'fill' will use the vertex bounds and a value of 'width' will use the
+	 * the vertex width for the label. See <mxGraph.isLabelClipped>. Note that
+	 * the vertical alignment is ignored for overflow fill and for horizontal
+	 * alignment, left should be used to avoid pixel offsets in Internet Explorer
+	 * 11 and earlier or if foreignObjects are disabled. Value is "overflow".
+	 */
+	STYLE_OVERFLOW: 'overflow',
+
+	/**
+	 * Variable: STYLE_ORTHOGONAL
+	 * 
+	 * Defines if the connection points on either end of the edge should be
+	 * computed so that the edge is vertical or horizontal if possible and
+	 * if the point is not at a fixed location. Default is false. This is
+	 * used in <mxGraph.isOrthogonal>, which also returns true if the edgeStyle
+	 * of the edge is an elbow or entity. Value is "orthogonal".
+	 */
+	STYLE_ORTHOGONAL: 'orthogonal',
+
+	/**
+	 * Variable: STYLE_EXIT_X
+	 * 
+	 * Defines the key for the horizontal relative coordinate connection point
+	 * of an edge with its source terminal. Value is "exitX".
+	 */
+	STYLE_EXIT_X: 'exitX',
+
+	/**
+	 * Variable: STYLE_EXIT_Y
+	 * 
+	 * Defines the key for the vertical relative coordinate connection point
+	 * of an edge with its source terminal. Value is "exitY".
+	 */
+	STYLE_EXIT_Y: 'exitY',
+
+	/**
+	 * Variable: STYLE_EXIT_PERIMETER
+	 * 
+	 * Defines if the perimeter should be used to find the exact entry point
+	 * along the perimeter of the source. Possible values are 0 (false) and
+	 * 1 (true). Default is 1 (true). Value is "exitPerimeter".
+	 */
+	STYLE_EXIT_PERIMETER: 'exitPerimeter',
+
+	/**
+	 * Variable: STYLE_ENTRY_X
+	 * 
+	 * Defines the key for the horizontal relative coordinate connection point
+	 * of an edge with its target terminal. Value is "entryX".
+	 */
+	STYLE_ENTRY_X: 'entryX',
+
+	/**
+	 * Variable: STYLE_ENTRY_Y
+	 * 
+	 * Defines the key for the vertical relative coordinate connection point
+	 * of an edge with its target terminal. Value is "entryY".
+	 */
+	STYLE_ENTRY_Y: 'entryY',
+
+	/**
+	 * Variable: STYLE_ENTRY_PERIMETER
+	 * 
+	 * Defines if the perimeter should be used to find the exact entry point
+	 * along the perimeter of the target. Possible values are 0 (false) and
+	 * 1 (true). Default is 1 (true). Value is "entryPerimeter".
+	 */
+	STYLE_ENTRY_PERIMETER: 'entryPerimeter',
+
+	/**
+	 * Variable: STYLE_WHITE_SPACE
+	 * 
+	 * Defines the key for the white-space style. Possible values are 'nowrap'
+	 * and 'wrap'. The default value is 'nowrap'. This value specifies how
+	 * white-space inside a HTML vertex label should be handled. A value of
+	 * 'nowrap' means the text will never wrap to the next line until a
+	 * linefeed is encountered. A value of 'wrap' means text will wrap when
+	 * necessary. This style is only used for HTML labels.
+	 * See <mxGraph.isWrapping>. Value is "whiteSpace".
+	 */
+	STYLE_WHITE_SPACE: 'whiteSpace',
+
+	/**
+	 * Variable: STYLE_ROTATION
+	 * 
+	 * Defines the key for the rotation style. The type of the value is 
+	 * numeric and the possible range is 0-360. Value is "rotation".
+	 */
+	STYLE_ROTATION: 'rotation',
+
+	/**
+	 * Variable: STYLE_FILLCOLOR
+	 * 
+	 * Defines the key for the fill color. Possible values are all HTML color
+	 * names or HEX codes, as well as special keywords such as 'swimlane,
+	 * 'inherit' or 'indicated' to use the color code of a related cell or the
+	 * indicator shape. Value is "fillColor".
+	 */
+	STYLE_FILLCOLOR: 'fillColor',
+
+	/**
+	 * Variable: STYLE_POINTER_EVENTS
+	 * 
+	 * Specifies if pointer events should be fired on transparent backgrounds.
+	 * This style is currently only supported in <mxRectangleShape>. Default
+	 * is true. Value is "pointerEvents". This is typically set to
+	 * false in groups where the transparent part should allow any underlying
+	 * cells to be clickable.
+	 */
+	STYLE_POINTER_EVENTS: 'pointerEvents',
+
+	/**
+	 * Variable: STYLE_SWIMLANE_FILLCOLOR
+	 * 
+	 * Defines the key for the fill color of the swimlane background. Possible
+	 * values are all HTML color names or HEX codes. Default is no background.
+	 * Value is "swimlaneFillColor".
+	 */
+	STYLE_SWIMLANE_FILLCOLOR: 'swimlaneFillColor',
+
+	/**
+	 * Variable: STYLE_MARGIN
+	 * 
+	 * Defines the key for the margin between the ellipses in the double ellipse shape.
+	 * Possible values are all positive numbers. Value is "margin".
+	 */
+	STYLE_MARGIN: 'margin',
+
+	/**
+	 * Variable: STYLE_GRADIENTCOLOR
+	 * 
+	 * Defines the key for the gradient color. Possible values are all HTML color
+	 * names or HEX codes, as well as special keywords such as 'swimlane,
+	 * 'inherit' or 'indicated' to use the color code of a related cell or the
+	 * indicator shape. This is ignored if no fill color is defined. Value is
+	 * "gradientColor".
+	 */
+	STYLE_GRADIENTCOLOR: 'gradientColor',
+
+	/**
+	 * Variable: STYLE_GRADIENT_DIRECTION
+	 * 
+	 * Defines the key for the gradient direction. Possible values are
+	 * <DIRECTION_EAST>, <DIRECTION_WEST>, <DIRECTION_NORTH> and
+	 * <DIRECTION_SOUTH>. Default is <DIRECTION_SOUTH>. Generally, and by
+	 * default in mxGraph, gradient painting is done from the value of
+	 * <STYLE_FILLCOLOR> to the value of <STYLE_GRADIENTCOLOR>. Taking the
+	 * example of <DIRECTION_NORTH>, this means <STYLE_FILLCOLOR> color at the 
+	 * bottom of paint pattern and <STYLE_GRADIENTCOLOR> at top, with a
+	 * gradient in-between. Value is "gradientDirection".
+	 */
+	STYLE_GRADIENT_DIRECTION: 'gradientDirection',
+
+	/**
+	 * Variable: STYLE_STROKECOLOR
+	 * 
+	 * Defines the key for the strokeColor style. Possible values are all HTML
+	 * color names or HEX codes, as well as special keywords such as 'swimlane,
+	 * 'inherit', 'indicated' to use the color code of a related cell or the
+	 * indicator shape or 'none' for no color. Value is "strokeColor".
+	 */
+	STYLE_STROKECOLOR: 'strokeColor',
+
+	/**
+	 * Variable: STYLE_SEPARATORCOLOR
+	 * 
+	 * Defines the key for the separatorColor style. Possible values are all
+	 * HTML color names or HEX codes. This style is only used for
+	 * <SHAPE_SWIMLANE> shapes. Value is "separatorColor".
+	 */
+	STYLE_SEPARATORCOLOR: 'separatorColor',
+
+	/**
+	 * Variable: STYLE_STROKEWIDTH
+	 * 
+	 * Defines the key for the strokeWidth style. The type of the value is 
+	 * numeric and the possible range is any non-negative value larger or equal
+	 * to 1. The value defines the stroke width in pixels. Note: To hide a
+	 * stroke use strokeColor none. Value is "strokeWidth".
+	 */
+	STYLE_STROKEWIDTH: 'strokeWidth',
+
+	/**
+	 * Variable: STYLE_ALIGN
+	 * 
+	 * Defines the key for the align style. Possible values are <ALIGN_LEFT>,
+	 * <ALIGN_CENTER> and <ALIGN_RIGHT>. This value defines how the lines of
+	 * the label are horizontally aligned. <ALIGN_LEFT> mean label text lines
+	 * are aligned to left of the label bounds, <ALIGN_RIGHT> to the right of
+	 * the label bounds and <ALIGN_CENTER> means the center of the text lines
+	 * are aligned in the center of the label bounds. Note this value doesn't
+	 * affect the positioning of the overall label bounds relative to the
+	 * vertex, to move the label bounds horizontally, use
+	 * <STYLE_LABEL_POSITION>. Value is "align".
+	 */
+	STYLE_ALIGN: 'align',
+
+	/**
+	 * Variable: STYLE_VERTICAL_ALIGN
+	 * 
+	 * Defines the key for the verticalAlign style. Possible values are
+	 * <ALIGN_TOP>, <ALIGN_MIDDLE> and <ALIGN_BOTTOM>. This value defines how
+	 * the lines of the label are vertically aligned. <ALIGN_TOP> means the
+	 * topmost label text line is aligned against the top of the label bounds,
+	 * <ALIGN_BOTTOM> means the bottom-most label text line is aligned against
+	 * the bottom of the label bounds and <ALIGN_MIDDLE> means there is equal
+	 * spacing between the topmost text label line and the top of the label
+	 * bounds and the bottom-most text label line and the bottom of the label
+	 * bounds. Note this value doesn't affect the positioning of the overall
+	 * label bounds relative to the vertex, to move the label bounds
+	 * vertically, use <STYLE_VERTICAL_LABEL_POSITION>. Value is "verticalAlign".
+	 */
+	STYLE_VERTICAL_ALIGN: 'verticalAlign',
+
+	/**
+	 * Variable: STYLE_LABEL_WIDTH
+	 * 
+	 * Defines the key for the width of the label if the label position is not
+	 * center. Value is "labelWidth".
+	 */
+	STYLE_LABEL_WIDTH: 'labelWidth',
+
+	/**
+	 * Variable: STYLE_LABEL_POSITION
+	 * 
+	 * Defines the key for the horizontal label position of vertices. Possible
+	 * values are <ALIGN_LEFT>, <ALIGN_CENTER> and <ALIGN_RIGHT>. Default is
+	 * <ALIGN_CENTER>. The label align defines the position of the label
+	 * relative to the cell. <ALIGN_LEFT> means the entire label bounds is
+	 * placed completely just to the left of the vertex, <ALIGN_RIGHT> means
+	 * adjust to the right and <ALIGN_CENTER> means the label bounds are
+	 * vertically aligned with the bounds of the vertex. Note this value
+	 * doesn't affect the positioning of label within the label bounds, to move
+	 * the label horizontally within the label bounds, use <STYLE_ALIGN>.
+	 * Value is "labelPosition".
+	 */
+	STYLE_LABEL_POSITION: 'labelPosition',
+
+	/**
+	 * Variable: STYLE_VERTICAL_LABEL_POSITION
+	 * 
+	 * Defines the key for the vertical label position of vertices. Possible
+	 * values are <ALIGN_TOP>, <ALIGN_BOTTOM> and <ALIGN_MIDDLE>. Default is
+	 * <ALIGN_MIDDLE>. The label align defines the position of the label
+	 * relative to the cell. <ALIGN_TOP> means the entire label bounds is
+	 * placed completely just on the top of the vertex, <ALIGN_BOTTOM> means
+	 * adjust on the bottom and <ALIGN_MIDDLE> means the label bounds are
+	 * horizontally aligned with the bounds of the vertex. Note this value
+	 * doesn't affect the positioning of label within the label bounds, to move
+	 * the label vertically within the label bounds, use
+	 * <STYLE_VERTICAL_ALIGN>. Value is "verticalLabelPosition".
+	 */
+	STYLE_VERTICAL_LABEL_POSITION: 'verticalLabelPosition',
+	
+	/**
+	 * Variable: STYLE_IMAGE_ASPECT
+	 * 
+	 * Defines the key for the image aspect style. Possible values are 0 (do
+	 * not preserve aspect) or 1 (keep aspect). This is only used in
+	 * <mxImageShape>. Default is 1. Value is "imageAspect".
+	 */
+	STYLE_IMAGE_ASPECT: 'imageAspect',
+
+	/**
+	 * Variable: STYLE_IMAGE_ALIGN
+	 * 
+	 * Defines the key for the align style. Possible values are <ALIGN_LEFT>,
+	 * <ALIGN_CENTER> and <ALIGN_RIGHT>. The value defines how any image in the
+	 * vertex label is aligned horizontally within the label bounds of a
+	 * <SHAPE_LABEL> shape. Value is "imageAlign".
+	 */
+	STYLE_IMAGE_ALIGN: 'imageAlign',
+
+	/**
+	 * Variable: STYLE_IMAGE_VERTICAL_ALIGN
+	 * 
+	 * Defines the key for the verticalAlign style. Possible values are
+	 * <ALIGN_TOP>, <ALIGN_MIDDLE> and <ALIGN_BOTTOM>. The value defines how
+	 * any image in the vertex label is aligned vertically within the label
+	 * bounds of a <SHAPE_LABEL> shape. Value is "imageVerticalAlign".
+	 */
+	STYLE_IMAGE_VERTICAL_ALIGN: 'imageVerticalAlign',
+
+	/**
+	 * Variable: STYLE_GLASS
+	 * 
+	 * Defines the key for the glass style. Possible values are 0 (disabled) and
+	 * 1(enabled). The default value is 0. This is used in <mxLabel>. Value is
+	 * "glass".
+	 */
+	STYLE_GLASS: 'glass',
+
+	/**
+	 * Variable: STYLE_IMAGE
+	 * 
+	 * Defines the key for the image style. Possible values are any image URL,
+	 * the type of the value is String. This is the path to the image that is
+	 * to be displayed within the label of a vertex. Data URLs should use the
+	 * following format: data:image/png,xyz where xyz is the base64 encoded
+	 * data (without the "base64"-prefix). Note that Data URLs are only
+	 * supported in modern browsers. Value is "image".
+	 */
+	STYLE_IMAGE: 'image',
+
+	/**
+	 * Variable: STYLE_IMAGE_WIDTH
+	 * 
+	 * Defines the key for the imageWidth style. The type of this value is
+	 * int, the value is the image width in pixels and must be greater than 0.
+	 * Value is "imageWidth".
+	 */
+	STYLE_IMAGE_WIDTH: 'imageWidth',
+
+	/**
+	 * Variable: STYLE_IMAGE_HEIGHT
+	 * 
+	 * Defines the key for the imageHeight style. The type of this value is
+	 * int, the value is the image height in pixels and must be greater than 0.
+	 * Value is "imageHeight".
+	 */
+	STYLE_IMAGE_HEIGHT: 'imageHeight',
+
+	/**
+	 * Variable: STYLE_IMAGE_BACKGROUND
+	 * 
+	 * Defines the key for the image background color. This style is only used
+	 * in <mxImageShape>. Possible values are all HTML color names or HEX
+	 * codes. Value is "imageBackground".
+	 */
+	STYLE_IMAGE_BACKGROUND: 'imageBackground',
+
+	/**
+	 * Variable: STYLE_IMAGE_BORDER
+	 * 
+	 * Defines the key for the image border color. This style is only used in
+	 * <mxImageShape>. Possible values are all HTML color names or HEX codes.
+	 * Value is "imageBorder".
+	 */
+	STYLE_IMAGE_BORDER: 'imageBorder',
+
+	/**
+	 * Variable: STYLE_FLIPH
+	 * 
+	 * Defines the key for the horizontal image flip. This style is only used
+	 * in <mxImageShape>. Possible values are 0 and 1. Default is 0. Value is
+	 * "flipH".
+	 */
+	STYLE_FLIPH: 'flipH',
+
+	/**
+	 * Variable: STYLE_FLIPV
+	 * 
+	 * Defines the key for the vertical flip. Possible values are 0 and 1.
+	 * Default is 0. Value is "flipV".
+	 */
+	STYLE_FLIPV: 'flipV',
+
+	/**
+	 * Variable: STYLE_NOLABEL
+	 * 
+	 * Defines the key for the noLabel style. If this is true then no label is
+	 * visible for a given cell. Possible values are true or false (1 or 0).
+	 * Default is false. Value is "noLabel".
+	 */
+	STYLE_NOLABEL: 'noLabel',
+
+	/**
+	 * Variable: STYLE_NOEDGESTYLE
+	 * 
+	 * Defines the key for the noEdgeStyle style. If this is true then no edge
+	 * style is applied for a given edge. Possible values are true or false
+	 * (1 or 0). Default is false. Value is "noEdgeStyle".
+	 */
+	STYLE_NOEDGESTYLE: 'noEdgeStyle',
+
+	/**
+	 * Variable: STYLE_LABEL_BACKGROUNDCOLOR
+	 * 
+	 * Defines the key for the label background color. Possible values are all
+	 * HTML color names or HEX codes. Value is "labelBackgroundColor".
+	 */
+	STYLE_LABEL_BACKGROUNDCOLOR: 'labelBackgroundColor',
+
+	/**
+	 * Variable: STYLE_LABEL_BORDERCOLOR
+	 * 
+	 * Defines the key for the label border color. Possible values are all
+	 * HTML color names or HEX codes. Value is "labelBorderColor".
+	 */
+	STYLE_LABEL_BORDERCOLOR: 'labelBorderColor',
+
+	/**
+	 * Variable: STYLE_LABEL_PADDING
+	 * 
+	 * Defines the key for the label padding, ie. the space between the label
+	 * border and the label. Value is "labelPadding".
+	 */
+	STYLE_LABEL_PADDING: 'labelPadding',
+
+	/**
+	 * Variable: STYLE_INDICATOR_SHAPE
+	 * 
+	 * Defines the key for the indicator shape used within an <mxLabel>.
+	 * Possible values are all SHAPE_* constants or the names of any new
+	 * shapes. The indicatorShape has precedence over the indicatorImage.
+	 * Value is "indicatorShape".
+	 */
+	STYLE_INDICATOR_SHAPE: 'indicatorShape',
+
+	/**
+	 * Variable: STYLE_INDICATOR_IMAGE
+	 * 
+	 * Defines the key for the indicator image used within an <mxLabel>.
+	 * Possible values are all image URLs. The indicatorShape has
+	 * precedence over the indicatorImage. Value is "indicatorImage".
+	 */
+	STYLE_INDICATOR_IMAGE: 'indicatorImage',
+
+	/**
+	 * Variable: STYLE_INDICATOR_COLOR
+	 * 
+	 * Defines the key for the indicatorColor style. Possible values are all
+	 * HTML color names or HEX codes, as well as the special 'swimlane' keyword
+	 * to refer to the color of the parent swimlane if one exists. Value is
+	 * "indicatorColor".
+	 */
+	STYLE_INDICATOR_COLOR: 'indicatorColor',
+
+	/**
+	 * Variable: STYLE_INDICATOR_STROKECOLOR
+	 * 
+	 * Defines the key for the indicator stroke color in <mxLabel>.
+	 * Possible values are all color codes. Value is "indicatorStrokeColor".
+	 */
+	STYLE_INDICATOR_STROKECOLOR: 'indicatorStrokeColor',
+
+	/**
+	 * Variable: STYLE_INDICATOR_GRADIENTCOLOR
+	 * 
+	 * Defines the key for the indicatorGradientColor style. Possible values
+	 * are all HTML color names or HEX codes. This style is only supported in
+	 * <SHAPE_LABEL> shapes. Value is "indicatorGradientColor".
+	 */
+	STYLE_INDICATOR_GRADIENTCOLOR: 'indicatorGradientColor',
+
+	/**
+	 * Variable: STYLE_INDICATOR_SPACING
+	 * 
+	 * The defines the key for the spacing between the label and the
+	 * indicator in <mxLabel>. Possible values are in pixels. Value is
+	 * "indicatorSpacing".
+	 */
+	STYLE_INDICATOR_SPACING: 'indicatorSpacing',
+
+	/**
+	 * Variable: STYLE_INDICATOR_WIDTH
+	 * 
+	 * Defines the key for the indicator width. Possible values start at 0 (in
+	 * pixels). Value is "indicatorWidth".
+	 */
+	STYLE_INDICATOR_WIDTH: 'indicatorWidth',
+
+	/**
+	 * Variable: STYLE_INDICATOR_HEIGHT
+	 * 
+	 * Defines the key for the indicator height. Possible values start at 0 (in
+	 * pixels). Value is "indicatorHeight".
+	 */
+	STYLE_INDICATOR_HEIGHT: 'indicatorHeight',
+
+	/**
+	 * Variable: STYLE_INDICATOR_DIRECTION
+	 * 
+	 * Defines the key for the indicatorDirection style. The direction style is
+	 * used to specify the direction of certain shapes (eg. <mxTriangle>).
+	 * Possible values are <DIRECTION_EAST> (default), <DIRECTION_WEST>,
+	 * <DIRECTION_NORTH> and <DIRECTION_SOUTH>. Value is "indicatorDirection".
+	 */
+	STYLE_INDICATOR_DIRECTION: 'indicatorDirection',
+
+	/**
+	 * Variable: STYLE_SHADOW
+	 * 
+	 * Defines the key for the shadow style. The type of the value is Boolean.
+	 * Value is "shadow".
+	 */
+	STYLE_SHADOW: 'shadow',
+	
+	/**
+	 * Variable: STYLE_SEGMENT
+	 * 
+	 * Defines the key for the segment style. The type of this value is float
+	 * and the value represents the size of the horizontal segment of the
+	 * entity relation style. Default is ENTITY_SEGMENT. Value is "segment".
+	 */
+	STYLE_SEGMENT: 'segment',
+	
+	/**
+	 * Variable: STYLE_ENDARROW
+	 *
+	 * Defines the key for the end arrow marker. Possible values are all
+	 * constants with an ARROW-prefix. This is only used in <mxConnector>.
+	 * Value is "endArrow".
+	 *
+	 * Example:
+	 * (code)
+	 * style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC;
+	 * (end)
+	 */
+	STYLE_ENDARROW: 'endArrow',
+
+	/**
+	 * Variable: STYLE_STARTARROW
+	 * 
+	 * Defines the key for the start arrow marker. Possible values are all
+	 * constants with an ARROW-prefix. This is only used in <mxConnector>.
+	 * See <STYLE_ENDARROW>. Value is "startArrow".
+	 */
+	STYLE_STARTARROW: 'startArrow',
+
+	/**
+	 * Variable: STYLE_ENDSIZE
+	 * 
+	 * Defines the key for the endSize style. The type of this value is numeric
+	 * and the value represents the size of the end marker in pixels. Value is
+	 * "endSize".
+	 */
+	STYLE_ENDSIZE: 'endSize',
+
+	/**
+	 * Variable: STYLE_STARTSIZE
+	 * 
+	 * Defines the key for the startSize style. The type of this value is
+	 * numeric and the value represents the size of the start marker or the
+	 * size of the swimlane title region depending on the shape it is used for.
+	 * Value is "startSize".
+	 */
+	STYLE_STARTSIZE: 'startSize',
+
+	/**
+	 * Variable: STYLE_SWIMLANE_LINE
+	 * 
+	 * Defines the key for the swimlaneLine style. This style specifies whether
+	 * the line between the title regio of a swimlane should be visible. Use 0
+	 * for hidden or 1 (default) for visible. Value is "swimlaneLine".
+	 */
+	STYLE_SWIMLANE_LINE: 'swimlaneLine',
+
+	/**
+	 * Variable: STYLE_ENDFILL
+	 * 
+	 * Defines the key for the endFill style. Use 0 for no fill or 1 (default)
+	 * for fill. (This style is only exported via <mxImageExport>.) Value is
+	 * "endFill".
+	 */
+	STYLE_ENDFILL: 'endFill',
+
+	/**
+	 * Variable: STYLE_STARTFILL
+	 * 
+	 * Defines the key for the startFill style. Use 0 for no fill or 1 (default)
+	 * for fill. (This style is only exported via <mxImageExport>.) Value is
+	 * "startFill".
+	 */
+	STYLE_STARTFILL: 'startFill',
+
+	/**
+	 * Variable: STYLE_DASHED
+	 * 
+	 * Defines the key for the dashed style. Use 0 (default) for non-dashed or 1
+	 * for dashed. Value is "dashed".
+	 */
+	STYLE_DASHED: 'dashed',
+
+	/**
+	 * Defines the key for the dashed pattern style in SVG and image exports.
+	 * The type of this value is a space separated list of numbers that specify
+	 * a custom-defined dash pattern. Dash styles are defined in terms of the
+	 * length of the dash (the drawn part of the stroke) and the length of the
+	 * space between the dashes. The lengths are relative to the line width: a
+	 * length of "1" is equal to the line width. VML ignores this style and
+	 * uses dashStyle instead as defined in the VML specification. This style
+	 * is only used in the <mxConnector> shape. Value is "dashPattern".
+	 */
+	STYLE_DASH_PATTERN: 'dashPattern',
+
+	/**
+	 * Variable: STYLE_FIX_DASH
+	 * 
+	 * Defines the key for the fixDash style. Use 0 (default) for dash patterns
+	 * that depend on the linewidth and 1 for dash patterns that ignore the
+	 * line width. Value is "fixDash".
+	 */
+	STYLE_FIX_DASH: 'fixDash',
+
+	/**
+	 * Variable: STYLE_ROUNDED
+	 * 
+	 * Defines the key for the rounded style. The type of this value is
+	 * Boolean. For edges this determines whether or not joins between edges
+	 * segments are smoothed to a rounded finish. For vertices that have the
+	 * rectangle shape, this determines whether or not the rectangle is
+	 * rounded. Use 0 (default) for non-rounded or 1 for rounded. Value is
+	 * "rounded".
+	 */
+	STYLE_ROUNDED: 'rounded',
+
+	/**
+	 * Variable: STYLE_CURVED
+	 * 
+	 * Defines the key for the curved style. The type of this value is
+	 * Boolean. It is only applicable for connector shapes. Use 0 (default)
+	 * for non-curved or 1 for curved. Value is "curved".
+	 */
+	STYLE_CURVED: 'curved',
+
+	/**
+	 * Variable: STYLE_ARCSIZE
+	 * 
+	 * Defines the rounding factor for a rounded rectangle in percent (without
+	 * the percent sign). Possible values are between 0 and 100. If this value
+	 * is not specified then RECTANGLE_ROUNDING_FACTOR * 100 is used. For
+	 * edges, this defines the absolute size of rounded corners in pixels. If
+	 * this values is not specified then LINE_ARCSIZE is used.
+	 * (This style is only exported via <mxImageExport>.) Value is "arcSize".
+	 */
+	STYLE_ARCSIZE: 'arcSize',
+
+	/**
+	 * Variable: STYLE_ABSOLUTE_ARCSIZE
+	 * 
+	 * Defines the key for the absolute arc size style. This specifies if
+	 * arcSize for rectangles is abolute or relative. Possible values are 1
+	 * and 0 (default). Value is "absoluteArcSize".
+	 */
+	STYLE_ABSOLUTE_ARCSIZE: 'absoluteArcSize',
+
+	/**
+	 * Variable: STYLE_SOURCE_PERIMETER_SPACING
+	 * 
+	 * Defines the key for the source perimeter spacing. The type of this value
+	 * is numeric. This is the distance between the source connection point of
+	 * an edge and the perimeter of the source vertex in pixels. This style
+	 * only applies to edges. Value is "sourcePerimeterSpacing".
+	 */
+	STYLE_SOURCE_PERIMETER_SPACING: 'sourcePerimeterSpacing',
+
+	/**
+	 * Variable: STYLE_TARGET_PERIMETER_SPACING
+	 * 
+	 * Defines the key for the target perimeter spacing. The type of this value
+	 * is numeric. This is the distance between the target connection point of
+	 * an edge and the perimeter of the target vertex in pixels. This style
+	 * only applies to edges. Value is "targetPerimeterSpacing".
+	 */
+	STYLE_TARGET_PERIMETER_SPACING: 'targetPerimeterSpacing',
+
+	/**
+	 * Variable: STYLE_PERIMETER_SPACING
+	 * 
+	 * Defines the key for the perimeter spacing. This is the distance between
+	 * the connection point and the perimeter in pixels. When used in a vertex
+	 * style, this applies to all incoming edges to floating ports (edges that
+	 * terminate on the perimeter of the vertex). When used in an edge style,
+	 * this spacing applies to the source and target separately, if they
+	 * terminate in floating ports (on the perimeter of the vertex). Value is
+	 * "perimeterSpacing".
+	 */
+	STYLE_PERIMETER_SPACING: 'perimeterSpacing',
+
+	/**
+	 * Variable: STYLE_SPACING
+	 * 
+	 * Defines the key for the spacing. The value represents the spacing, in
+	 * pixels, added to each side of a label in a vertex (style applies to
+	 * vertices only). Value is "spacing".
+	 */
+	STYLE_SPACING: 'spacing',
+
+	/**
+	 * Variable: STYLE_SPACING_TOP
+	 * 
+	 * Defines the key for the spacingTop style. The value represents the
+	 * spacing, in pixels, added to the top side of a label in a vertex (style
+	 * applies to vertices only). Value is "spacingTop".
+	 */
+	STYLE_SPACING_TOP: 'spacingTop',
+
+	/**
+	 * Variable: STYLE_SPACING_LEFT
+	 * 
+	 * Defines the key for the spacingLeft style. The value represents the
+	 * spacing, in pixels, added to the left side of a label in a vertex (style
+	 * applies to vertices only). Value is "spacingLeft".
+	 */
+	STYLE_SPACING_LEFT: 'spacingLeft',
+
+	/**
+	 * Variable: STYLE_SPACING_BOTTOM
+	 * 
+	 * Defines the key for the spacingBottom style The value represents the
+	 * spacing, in pixels, added to the bottom side of a label in a vertex
+	 * (style applies to vertices only). Value is "spacingBottom".
+	 */
+	STYLE_SPACING_BOTTOM: 'spacingBottom',
+
+	/**
+	 * Variable: STYLE_SPACING_RIGHT
+	 * 
+	 * Defines the key for the spacingRight style The value represents the
+	 * spacing, in pixels, added to the right side of a label in a vertex (style
+	 * applies to vertices only). Value is "spacingRight".
+	 */
+	STYLE_SPACING_RIGHT: 'spacingRight',
+
+	/**
+	 * Variable: STYLE_HORIZONTAL
+	 * 
+	 * Defines the key for the horizontal style. Possible values are
+	 * true or false. This value only applies to vertices. If the <STYLE_SHAPE>
+	 * is "SHAPE_SWIMLANE" a value of false indicates that the
+	 * swimlane should be drawn vertically, true indicates to draw it
+	 * horizontally. If the shape style does not indicate that this vertex is a
+	 * swimlane, this value affects only whether the label is drawn
+	 * horizontally or vertically. Value is "horizontal".
+	 */
+	STYLE_HORIZONTAL: 'horizontal',
+
+	/**
+	 * Variable: STYLE_DIRECTION
+	 * 
+	 * Defines the key for the direction style. The direction style is used
+	 * to specify the direction of certain shapes (eg. <mxTriangle>).
+	 * Possible values are <DIRECTION_EAST> (default), <DIRECTION_WEST>,
+	 * <DIRECTION_NORTH> and <DIRECTION_SOUTH>. Value is "direction".
+	 */
+	STYLE_DIRECTION: 'direction',
+
+	/**
+	 * Variable: STYLE_ELBOW
+	 * 
+	 * Defines the key for the elbow style. Possible values are
+	 * <ELBOW_HORIZONTAL> and <ELBOW_VERTICAL>. Default is <ELBOW_HORIZONTAL>.
+	 * This defines how the three segment orthogonal edge style leaves its
+	 * terminal vertices. The vertical style leaves the terminal vertices at
+	 * the top and bottom sides. Value is "elbow".
+	 */
+	STYLE_ELBOW: 'elbow',
+
+	/**
+	 * Variable: STYLE_FONTCOLOR
+	 * 
+	 * Defines the key for the fontColor style. Possible values are all HTML
+	 * color names or HEX codes. Value is "fontColor".
+	 */
+	STYLE_FONTCOLOR: 'fontColor',
+
+	/**
+	 * Variable: STYLE_FONTFAMILY
+	 * 
+	 * Defines the key for the fontFamily style. Possible values are names such
+	 * as Arial; Dialog; Verdana; Times New Roman. The value is of type String.
+	 * Value is fontFamily.
+	 */
+	STYLE_FONTFAMILY: 'fontFamily',
+
+	/**
+	 * Variable: STYLE_FONTSIZE
+	 * 
+	 * Defines the key for the fontSize style (in px). The type of the value
+	 * is int. Value is "fontSize".
+	 */
+	STYLE_FONTSIZE: 'fontSize',
+
+	/**
+	 * Variable: STYLE_FONTSTYLE
+	 * 
+	 * Defines the key for the fontStyle style. Values may be any logical AND
+	 * (sum) of <FONT_BOLD>, <FONT_ITALIC> and <FONT_UNDERLINE>.
+	 * The type of the value is int. Value is "fontStyle".
+	 */
+	STYLE_FONTSTYLE: 'fontStyle',
+	
+	/**
+	 * Variable: STYLE_ASPECT
+	 * 
+	 * Defines the key for the aspect style. Possible values are empty or fixed.
+	 * If fixed is used then the aspect ratio of the cell will be maintained
+	 * when resizing. Default is empty. Value is "aspect".
+	 */
+	STYLE_ASPECT: 'aspect',
+
+	/**
+	 * Variable: STYLE_AUTOSIZE
+	 * 
+	 * Defines the key for the autosize style. This specifies if a cell should be
+	 * resized automatically if the value has changed. Possible values are 0 or 1.
+	 * Default is 0. See <mxGraph.isAutoSizeCell>. This is normally combined with
+	 * <STYLE_RESIZABLE> to disable manual sizing. Value is "autosize".
+	 */
+	STYLE_AUTOSIZE: 'autosize',
+
+	/**
+	 * Variable: STYLE_FOLDABLE
+	 * 
+	 * Defines the key for the foldable style. This specifies if a cell is foldable
+	 * using a folding icon. Possible values are 0 or 1. Default is 1. See
+	 * <mxGraph.isCellFoldable>. Value is "foldable".
+	 */
+	STYLE_FOLDABLE: 'foldable',
+
+	/**
+	 * Variable: STYLE_EDITABLE
+	 * 
+	 * Defines the key for the editable style. This specifies if the value of
+	 * a cell can be edited using the in-place editor. Possible values are 0 or
+	 * 1. Default is 1. See <mxGraph.isCellEditable>. Value is "editable".
+	 */
+	STYLE_EDITABLE: 'editable',
+
+	/**
+	 * Variable: STYLE_BENDABLE
+	 * 
+	 * Defines the key for the bendable style. This specifies if the control
+	 * points of an edge can be moved. Possible values are 0 or 1. Default is
+	 * 1. See <mxGraph.isCellBendable>. Value is "bendable".
+	 */
+	STYLE_BENDABLE: 'bendable',
+
+	/**
+	 * Variable: STYLE_MOVABLE
+	 * 
+	 * Defines the key for the movable style. This specifies if a cell can
+	 * be moved. Possible values are 0 or 1. Default is 1. See
+	 * <mxGraph.isCellMovable>. Value is "movable".
+	 */
+	STYLE_MOVABLE: 'movable',
+
+	/**
+	 * Variable: STYLE_RESIZABLE
+	 * 
+	 * Defines the key for the resizable style. This specifies if a cell can
+	 * be resized. Possible values are 0 or 1. Default is 1. See
+	 * <mxGraph.isCellResizable>. Value is "resizable".
+	 */
+	STYLE_RESIZABLE: 'resizable',
+
+	/**
+	 * Variable: STYLE_RESIZE_WIDTH
+	 * 
+	 * Defines the key for the resizeWidth style. This specifies if a cell's
+	 * width is resized if the parent is resized. If this is 1 then the width
+	 * will be resized even if the cell's geometry is relative. If this is 0
+	 * then the cell's width will not be resized. Default is not defined. Value
+	 * is "resizeWidth".
+	 */
+	STYLE_RESIZE_WIDTH: 'resizeWidth',
+
+	/**
+	 * Variable: STYLE_RESIZE_WIDTH
+	 * 
+	 * Defines the key for the resizeHeight style. This specifies if a cell's
+	 * height if resize if the parent is resized. If this is 1 then the height
+	 * will be resized even if the cell's geometry is relative. If this is 0
+	 * then the cell's height will not be resized. Default is not defined. Value
+	 * is "resizeHeight".
+	 */
+	STYLE_RESIZE_HEIGHT: 'resizeHeight',
+
+	/**
+	 * Variable: STYLE_ROTATABLE
+	 * 
+	 * Defines the key for the rotatable style. This specifies if a cell can
+	 * be rotated. Possible values are 0 or 1. Default is 1. See
+	 * <mxGraph.isCellRotatable>. Value is "rotatable".
+	 */
+	STYLE_ROTATABLE: 'rotatable',
+
+	/**
+	 * Variable: STYLE_CLONEABLE
+	 * 
+	 * Defines the key for the cloneable style. This specifies if a cell can
+	 * be cloned. Possible values are 0 or 1. Default is 1. See
+	 * <mxGraph.isCellCloneable>. Value is "cloneable".
+	 */
+	STYLE_CLONEABLE: 'cloneable',
+
+	/**
+	 * Variable: STYLE_DELETABLE
+	 * 
+	 * Defines the key for the deletable style. This specifies if a cell can be
+	 * deleted. Possible values are 0 or 1. Default is 1. See
+	 * <mxGraph.isCellDeletable>. Value is "deletable".
+	 */
+	STYLE_DELETABLE: 'deletable',
+
+	/**
+	 * Variable: STYLE_SHAPE
+	 * 
+	 * Defines the key for the shape. Possible values are all constants with
+	 * a SHAPE-prefix or any newly defined shape names. Value is "shape".
+	 */
+	STYLE_SHAPE: 'shape',
+
+	/**
+	 * Variable: STYLE_EDGE
+	 * 
+	 * Defines the key for the edge style. Possible values are the functions
+	 * defined in <mxEdgeStyle>. Value is "edgeStyle".
+	 */
+	STYLE_EDGE: 'edgeStyle',
+
+	/**
+	 * Variable: STYLE_JETTY_SIZE
+	 * 
+	 * Defines the key for the jetty size in <mxEdgeStyle.OrthConnector>.
+	 * Default is 10. Possible values are all numeric values or "auto".
+	 * Value is "jettySize".
+	 */
+	STYLE_JETTY_SIZE: 'jettySize',
+
+	/**
+	 * Variable: STYLE_SOURCE_JETTY_SIZE
+	 * 
+	 * Defines the key for the jetty size in <mxEdgeStyle.OrthConnector>.
+	 * Default is 10. Possible values are numeric values or "auto". This has
+	 * precedence over <STYLE_JETTY_SIZE>. Value is "sourceJettySize".
+	 */
+	STYLE_SOURCE_JETTY_SIZE: 'sourceJettySize',
+
+	/**
+	 * Variable: targetJettySize
+	 * 
+	 * Defines the key for the jetty size in <mxEdgeStyle.OrthConnector>.
+	 * Default is 10. Possible values are numeric values or "auto". This has
+	 * precedence over <STYLE_JETTY_SIZE>. Value is "targetJettySize".
+	 */
+	STYLE_TARGET_JETTY_SIZE: 'targetJettySize',
+
+	/**
+	 * Variable: STYLE_LOOP
+	 * 
+	 * Defines the key for the loop style. Possible values are the functions
+	 * defined in <mxEdgeStyle>. Value is "loopStyle".
+	 */
+	STYLE_LOOP: 'loopStyle',
+
+	/**
+	 * Variable: STYLE_ORTHOGONAL_LOOP
+	 * 
+	 * Defines the key for the orthogonal loop style. Possible values are 0 and
+	 * 1. Default is 0. Value is "orthogonalLoop". Use this style to specify
+	 * if loops should be routed using an orthogonal router. Currently, this
+	 * uses <mxEdgeStyle.OrthConnector> but will be replaced with a dedicated
+	 * orthogonal loop router in later releases.
+	 */
+	STYLE_ORTHOGONAL_LOOP: 'orthogonalLoop',
+
+	/**
+	 * Variable: STYLE_ROUTING_CENTER_X
+	 * 
+	 * Defines the key for the horizontal routing center. Possible values are
+	 * between -0.5 and 0.5. This is the relative offset from the center used
+	 * for connecting edges. The type of this value is numeric. Value is
+	 * "routingCenterX".
+	 */
+	STYLE_ROUTING_CENTER_X: 'routingCenterX',
+
+	/**
+	 * Variable: STYLE_ROUTING_CENTER_Y
+	 * 
+	 * Defines the key for the vertical routing center. Possible values are
+	 * between -0.5 and 0.5. This is the relative offset from the center used
+	 * for connecting edges. The type of this value is numeric. Value is
+	 * "routingCenterY".
+	 */
+	STYLE_ROUTING_CENTER_Y: 'routingCenterY',
+
+	/**
+	 * Variable: FONT_BOLD
+	 * 
+	 * Constant for bold fonts. Default is 1.
+	 */
+	FONT_BOLD: 1,
+
+	/**
+	 * Variable: FONT_ITALIC
+	 * 
+	 * Constant for italic fonts. Default is 2.
+	 */
+	FONT_ITALIC: 2,
+
+	/**
+	 * Variable: FONT_UNDERLINE
+	 * 
+	 * Constant for underlined fonts. Default is 4.
+	 */
+	FONT_UNDERLINE: 4,
+
+	/**
+	 * Variable: SHAPE_RECTANGLE
+	 * 
+	 * Name under which <mxRectangleShape> is registered in <mxCellRenderer>.
+	 * Default is rectangle.
+	 */
+	SHAPE_RECTANGLE: 'rectangle',
+
+	/**
+	 * Variable: SHAPE_ELLIPSE
+	 * 
+	 * Name under which <mxEllipse> is registered in <mxCellRenderer>.
+	 * Default is ellipse.
+	 */
+	SHAPE_ELLIPSE: 'ellipse',
+
+	/**
+	 * Variable: SHAPE_DOUBLE_ELLIPSE
+	 * 
+	 * Name under which <mxDoubleEllipse> is registered in <mxCellRenderer>.
+	 * Default is doubleEllipse.
+	 */
+	SHAPE_DOUBLE_ELLIPSE: 'doubleEllipse',
+
+	/**
+	 * Variable: SHAPE_RHOMBUS
+	 * 
+	 * Name under which <mxRhombus> is registered in <mxCellRenderer>.
+	 * Default is rhombus.
+	 */
+	SHAPE_RHOMBUS: 'rhombus',
+
+	/**
+	 * Variable: SHAPE_LINE
+	 * 
+	 * Name under which <mxLine> is registered in <mxCellRenderer>.
+	 * Default is line.
+	 */
+	SHAPE_LINE: 'line',
+
+	/**
+	 * Variable: SHAPE_IMAGE
+	 * 
+	 * Name under which <mxImageShape> is registered in <mxCellRenderer>.
+	 * Default is image.
+	 */
+	SHAPE_IMAGE: 'image',
+	
+	/**
+	 * Variable: SHAPE_ARROW
+	 * 
+	 * Name under which <mxArrow> is registered in <mxCellRenderer>.
+	 * Default is arrow.
+	 */
+	SHAPE_ARROW: 'arrow',
+	
+	/**
+	 * Variable: SHAPE_ARROW_CONNECTOR
+	 * 
+	 * Name under which <mxArrowConnector> is registered in <mxCellRenderer>.
+	 * Default is arrowConnector.
+	 */
+	SHAPE_ARROW_CONNECTOR: 'arrowConnector',
+	
+	/**
+	 * Variable: SHAPE_LABEL
+	 * 
+	 * Name under which <mxLabel> is registered in <mxCellRenderer>.
+	 * Default is label.
+	 */
+	SHAPE_LABEL: 'label',
+	
+	/**
+	 * Variable: SHAPE_CYLINDER
+	 * 
+	 * Name under which <mxCylinder> is registered in <mxCellRenderer>.
+	 * Default is cylinder.
+	 */
+	SHAPE_CYLINDER: 'cylinder',
+	
+	/**
+	 * Variable: SHAPE_SWIMLANE
+	 * 
+	 * Name under which <mxSwimlane> is registered in <mxCellRenderer>.
+	 * Default is swimlane.
+	 */
+	SHAPE_SWIMLANE: 'swimlane',
+		
+	/**
+	 * Variable: SHAPE_CONNECTOR
+	 * 
+	 * Name under which <mxConnector> is registered in <mxCellRenderer>.
+	 * Default is connector.
+	 */
+	SHAPE_CONNECTOR: 'connector',
+
+	/**
+	 * Variable: SHAPE_ACTOR
+	 * 
+	 * Name under which <mxActor> is registered in <mxCellRenderer>.
+	 * Default is actor.
+	 */
+	SHAPE_ACTOR: 'actor',
+		
+	/**
+	 * Variable: SHAPE_CLOUD
+	 * 
+	 * Name under which <mxCloud> is registered in <mxCellRenderer>.
+	 * Default is cloud.
+	 */
+	SHAPE_CLOUD: 'cloud',
+		
+	/**
+	 * Variable: SHAPE_TRIANGLE
+	 * 
+	 * Name under which <mxTriangle> is registered in <mxCellRenderer>.
+	 * Default is triangle.
+	 */
+	SHAPE_TRIANGLE: 'triangle',
+		
+	/**
+	 * Variable: SHAPE_HEXAGON
+	 * 
+	 * Name under which <mxHexagon> is registered in <mxCellRenderer>.
+	 * Default is hexagon.
+	 */
+	SHAPE_HEXAGON: 'hexagon',
+
+	/**
+	 * Variable: ARROW_CLASSIC
+	 * 
+	 * Constant for classic arrow markers.
+	 */
+	ARROW_CLASSIC: 'classic',
+
+	/**
+	 * Variable: ARROW_CLASSIC_THIN
+	 * 
+	 * Constant for thin classic arrow markers.
+	 */
+	ARROW_CLASSIC_THIN: 'classicThin',
+
+	/**
+	 * Variable: ARROW_BLOCK
+	 * 
+	 * Constant for block arrow markers.
+	 */
+	ARROW_BLOCK: 'block',
+
+	/**
+	 * Variable: ARROW_BLOCK_THIN
+	 * 
+	 * Constant for thin block arrow markers.
+	 */
+	ARROW_BLOCK_THIN: 'blockThin',
+
+	/**
+	 * Variable: ARROW_OPEN
+	 * 
+	 * Constant for open arrow markers.
+	 */
+	ARROW_OPEN: 'open',
+
+	/**
+	 * Variable: ARROW_OPEN_THIN
+	 * 
+	 * Constant for thin open arrow markers.
+	 */
+	ARROW_OPEN_THIN: 'openThin',
+
+	/**
+	 * Variable: ARROW_OVAL
+	 * 
+	 * Constant for oval arrow markers.
+	 */
+	ARROW_OVAL: 'oval',
+
+	/**
+	 * Variable: ARROW_DIAMOND
+	 * 
+	 * Constant for diamond arrow markers.
+	 */
+	ARROW_DIAMOND: 'diamond',
+
+	/**
+	 * Variable: ARROW_DIAMOND_THIN
+	 * 
+	 * Constant for thin diamond arrow markers.
+	 */
+	ARROW_DIAMOND_THIN: 'diamondThin',
+
+	/**
+	 * Variable: ALIGN_LEFT
+	 * 
+	 * Constant for left horizontal alignment. Default is left.
+	 */
+	ALIGN_LEFT: 'left',
+
+	/**
+	 * Variable: ALIGN_CENTER
+	 * 
+	 * Constant for center horizontal alignment. Default is center.
+	 */
+	ALIGN_CENTER: 'center',
+
+	/**
+	 * Variable: ALIGN_RIGHT
+	 * 
+	 * Constant for right horizontal alignment. Default is right.
+	 */
+	ALIGN_RIGHT: 'right',
+
+	/**
+	 * Variable: ALIGN_TOP
+	 * 
+	 * Constant for top vertical alignment. Default is top.
+	 */
+	ALIGN_TOP: 'top',
+
+	/**
+	 * Variable: ALIGN_MIDDLE
+	 * 
+	 * Constant for middle vertical alignment. Default is middle.
+	 */
+	ALIGN_MIDDLE: 'middle',
+
+	/**
+	 * Variable: ALIGN_BOTTOM
+	 * 
+	 * Constant for bottom vertical alignment. Default is bottom.
+	 */
+	ALIGN_BOTTOM: 'bottom',
+
+	/**
+	 * Variable: DIRECTION_NORTH
+	 * 
+	 * Constant for direction north. Default is north.
+	 */
+	DIRECTION_NORTH: 'north',
+
+	/**
+	 * Variable: DIRECTION_SOUTH
+	 * 
+	 * Constant for direction south. Default is south.
+	 */
+	DIRECTION_SOUTH: 'south',
+
+	/**
+	 * Variable: DIRECTION_EAST
+	 * 
+	 * Constant for direction east. Default is east.
+	 */
+	DIRECTION_EAST: 'east',
+
+	/**
+	 * Variable: DIRECTION_WEST
+	 * 
+	 * Constant for direction west. Default is west.
+	 */
+	DIRECTION_WEST: 'west',
+
+	/**
+	 * Variable: TEXT_DIRECTION_DEFAULT
+	 * 
+	 * Constant for text direction default. Default is an empty string. Use
+	 * this value to use the default text direction of the operating system. 
+	 */
+	TEXT_DIRECTION_DEFAULT: '',
+
+	/**
+	 * Variable: TEXT_DIRECTION_AUTO
+	 * 
+	 * Constant for text direction automatic. Default is auto. Use this value
+	 * to find the direction for a given text with <mxText.getAutoDirection>. 
+	 */
+	TEXT_DIRECTION_AUTO: 'auto',
+
+	/**
+	 * Variable: TEXT_DIRECTION_LTR
+	 * 
+	 * Constant for text direction left to right. Default is ltr. Use this
+	 * value for left to right text direction.
+	 */
+	TEXT_DIRECTION_LTR: 'ltr',
+
+	/**
+	 * Variable: TEXT_DIRECTION_RTL
+	 * 
+	 * Constant for text direction right to left. Default is rtl. Use this
+	 * value for right to left text direction.
+	 */
+	TEXT_DIRECTION_RTL: 'rtl',
+
+	/**
+	 * Variable: DIRECTION_MASK_NONE
+	 * 
+	 * Constant for no direction.
+	 */
+	DIRECTION_MASK_NONE: 0,
+
+	/**
+	 * Variable: DIRECTION_MASK_WEST
+	 * 
+	 * Bitwise mask for west direction.
+	 */
+	DIRECTION_MASK_WEST: 1,
+	
+	/**
+	 * Variable: DIRECTION_MASK_NORTH
+	 * 
+	 * Bitwise mask for north direction.
+	 */
+	DIRECTION_MASK_NORTH: 2,
+
+	/**
+	 * Variable: DIRECTION_MASK_SOUTH
+	 * 
+	 * Bitwise mask for south direction.
+	 */
+	DIRECTION_MASK_SOUTH: 4,
+
+	/**
+	 * Variable: DIRECTION_MASK_EAST
+	 * 
+	 * Bitwise mask for east direction.
+	 */
+	DIRECTION_MASK_EAST: 8,
+	
+	/**
+	 * Variable: DIRECTION_MASK_ALL
+	 * 
+	 * Bitwise mask for all directions.
+	 */
+	DIRECTION_MASK_ALL: 15,
+
+	/**
+	 * Variable: ELBOW_VERTICAL
+	 * 
+	 * Constant for elbow vertical. Default is horizontal.
+	 */
+	ELBOW_VERTICAL: 'vertical',
+
+	/**
+	 * Variable: ELBOW_HORIZONTAL
+	 * 
+	 * Constant for elbow horizontal. Default is horizontal.
+	 */
+	ELBOW_HORIZONTAL: 'horizontal',
+
+	/**
+	 * Variable: EDGESTYLE_ELBOW
+	 * 
+	 * Name of the elbow edge style. Can be used as a string value
+	 * for the STYLE_EDGE style.
+	 */
+	EDGESTYLE_ELBOW: 'elbowEdgeStyle',
+
+	/**
+	 * Variable: EDGESTYLE_ENTITY_RELATION
+	 * 
+	 * Name of the entity relation edge style. Can be used as a string value
+	 * for the STYLE_EDGE style.
+	 */
+	EDGESTYLE_ENTITY_RELATION: 'entityRelationEdgeStyle',
+
+	/**
+	 * Variable: EDGESTYLE_LOOP
+	 * 
+	 * Name of the loop edge style. Can be used as a string value
+	 * for the STYLE_EDGE style.
+	 */
+	EDGESTYLE_LOOP: 'loopEdgeStyle',
+
+	/**
+	 * Variable: EDGESTYLE_SIDETOSIDE
+	 * 
+	 * Name of the side to side edge style. Can be used as a string value
+	 * for the STYLE_EDGE style.
+	 */
+	EDGESTYLE_SIDETOSIDE: 'sideToSideEdgeStyle',
+
+	/**
+	 * Variable: EDGESTYLE_TOPTOBOTTOM
+	 * 
+	 * Name of the top to bottom edge style. Can be used as a string value
+	 * for the STYLE_EDGE style.
+	 */
+	EDGESTYLE_TOPTOBOTTOM: 'topToBottomEdgeStyle',
+
+	/**
+	 * Variable: EDGESTYLE_ORTHOGONAL
+	 * 
+	 * Name of the generic orthogonal edge style. Can be used as a string value
+	 * for the STYLE_EDGE style.
+	 */
+	EDGESTYLE_ORTHOGONAL: 'orthogonalEdgeStyle',
+
+	/**
+	 * Variable: EDGESTYLE_SEGMENT
+	 * 
+	 * Name of the generic segment edge style. Can be used as a string value
+	 * for the STYLE_EDGE style.
+	 */
+	EDGESTYLE_SEGMENT: 'segmentEdgeStyle',
+ 
+	/**
+	 * Variable: PERIMETER_ELLIPSE
+	 * 
+	 * Name of the ellipse perimeter. Can be used as a string value
+	 * for the STYLE_PERIMETER style.
+	 */
+	PERIMETER_ELLIPSE: 'ellipsePerimeter',
+
+	/**
+	 * Variable: PERIMETER_RECTANGLE
+	 *
+	 * Name of the rectangle perimeter. Can be used as a string value
+	 * for the STYLE_PERIMETER style.
+	 */
+	PERIMETER_RECTANGLE: 'rectanglePerimeter',
+
+	/**
+	 * Variable: PERIMETER_RHOMBUS
+	 * 
+	 * Name of the rhombus perimeter. Can be used as a string value
+	 * for the STYLE_PERIMETER style.
+	 */
+	PERIMETER_RHOMBUS: 'rhombusPerimeter',
+
+	/**
+	 * Variable: PERIMETER_HEXAGON
+	 * 
+	 * Name of the hexagon perimeter. Can be used as a string value 
+	 * for the STYLE_PERIMETER style.
+	 */
+	PERIMETER_HEXAGON: 'hexagonPerimeter',
+
+	/**
+	 * Variable: PERIMETER_TRIANGLE
+	 * 
+	 * Name of the triangle perimeter. Can be used as a string value
+	 * for the STYLE_PERIMETER style.
+	 */
+	PERIMETER_TRIANGLE: 'trianglePerimeter'
+
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxEventObject
+ * 
+ * The mxEventObject is a wrapper for all properties of a single event.
+ * Additionally, it also offers functions to consume the event and check if it
+ * was consumed as follows:
+ * 
+ * (code)
+ * evt.consume();
+ * INV: evt.isConsumed() == true
+ * (end)
+ * 
+ * Constructor: mxEventObject
+ *
+ * Constructs a new event object with the specified name. An optional
+ * sequence of key, value pairs can be appended to define properties.
+ * 
+ * Example:
+ *
+ * (code)
+ * new mxEventObject("eventName", key1, val1, .., keyN, valN)
+ * (end)
+ */
+function mxEventObject(name)
+{
+	this.name = name;
+	this.properties = [];
+	
+	for (var i = 1; i < arguments.length; i += 2)
+	{
+		if (arguments[i + 1] != null)
+		{
+			this.properties[arguments[i]] = arguments[i + 1];
+		}
+	}
+};
+
+/**
+ * Variable: name
+ *
+ * Holds the name.
+ */
+mxEventObject.prototype.name = null;
+
+/**
+ * Variable: properties
+ *
+ * Holds the properties as an associative array.
+ */
+mxEventObject.prototype.properties = null;
+
+/**
+ * Variable: consumed
+ *
+ * Holds the consumed state. Default is false.
+ */
+mxEventObject.prototype.consumed = false;
+
+/**
+ * Function: getName
+ * 
+ * Returns <name>.
+ */
+mxEventObject.prototype.getName = function()
+{
+	return this.name;
+};
+
+/**
+ * Function: getProperties
+ * 
+ * Returns <properties>.
+ */
+mxEventObject.prototype.getProperties = function()
+{
+	return this.properties;
+};
+
+/**
+ * Function: getProperty
+ * 
+ * Returns the property for the given key.
+ */
+mxEventObject.prototype.getProperty = function(key)
+{
+	return this.properties[key];
+};
+
+/**
+ * Function: isConsumed
+ *
+ * Returns true if the event has been consumed.
+ */
+mxEventObject.prototype.isConsumed = function()
+{
+	return this.consumed;
+};
+
+/**
+ * Function: consume
+ *
+ * Consumes the event.
+ */
+mxEventObject.prototype.consume = function()
+{
+	this.consumed = true;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxMouseEvent
+ * 
+ * Base class for all mouse events in mxGraph. A listener for this event should
+ * implement the following methods:
+ * 
+ * (code)
+ * graph.addMouseListener(
+ * {
+ *   mouseDown: function(sender, evt)
+ *   {
+ *     mxLog.debug('mouseDown');
+ *   },
+ *   mouseMove: function(sender, evt)
+ *   {
+ *     mxLog.debug('mouseMove');
+ *   },
+ *   mouseUp: function(sender, evt)
+ *   {
+ *     mxLog.debug('mouseUp');
+ *   }
+ * });
+ * (end)
+ * 
+ * Constructor: mxMouseEvent
+ *
+ * Constructs a new event object for the given arguments.
+ * 
+ * Parameters:
+ * 
+ * evt - Native mouse event.
+ * state - Optional <mxCellState> under the mouse.
+ * 
+ */
+function mxMouseEvent(evt, state)
+{
+	this.evt = evt;
+	this.state = state;
+	this.sourceState = state;
+};
+
+/**
+ * Variable: consumed
+ *
+ * Holds the consumed state of this event.
+ */
+mxMouseEvent.prototype.consumed = false;
+
+/**
+ * Variable: evt
+ *
+ * Holds the inner event object.
+ */
+mxMouseEvent.prototype.evt = null;
+
+/**
+ * Variable: graphX
+ *
+ * Holds the x-coordinate of the event in the graph. This value is set in
+ * <mxGraph.fireMouseEvent>.
+ */
+mxMouseEvent.prototype.graphX = null;
+
+/**
+ * Variable: graphY
+ *
+ * Holds the y-coordinate of the event in the graph. This value is set in
+ * <mxGraph.fireMouseEvent>.
+ */
+mxMouseEvent.prototype.graphY = null;
+
+/**
+ * Variable: state
+ *
+ * Holds the optional <mxCellState> associated with this event.
+ */
+mxMouseEvent.prototype.state = null;
+
+/**
+ * Variable: sourceState
+ * 
+ * Holds the <mxCellState> that was passed to the constructor. This can be
+ * different from <state> depending on the result of <mxGraph.getEventState>.
+ */
+mxMouseEvent.prototype.sourceState = null;
+
+/**
+ * Function: getEvent
+ * 
+ * Returns <evt>.
+ */
+mxMouseEvent.prototype.getEvent = function()
+{
+	return this.evt;
+};
+
+/**
+ * Function: getSource
+ * 
+ * Returns the target DOM element using <mxEvent.getSource> for <evt>.
+ */
+mxMouseEvent.prototype.getSource = function()
+{
+	return mxEvent.getSource(this.evt);
+};
+
+/**
+ * Function: isSource
+ * 
+ * Returns true if the given <mxShape> is the source of <evt>.
+ */
+mxMouseEvent.prototype.isSource = function(shape)
+{
+	if (shape != null)
+	{
+		return mxUtils.isAncestorNode(shape.node, this.getSource());
+	}
+	
+	return false;
+};
+
+/**
+ * Function: getX
+ * 
+ * Returns <evt.clientX>.
+ */
+mxMouseEvent.prototype.getX = function()
+{
+	return mxEvent.getClientX(this.getEvent());
+};
+
+/**
+ * Function: getY
+ * 
+ * Returns <evt.clientY>.
+ */
+mxMouseEvent.prototype.getY = function()
+{
+	return mxEvent.getClientY(this.getEvent());
+};
+
+/**
+ * Function: getGraphX
+ * 
+ * Returns <graphX>.
+ */
+mxMouseEvent.prototype.getGraphX = function()
+{
+	return this.graphX;
+};
+
+/**
+ * Function: getGraphY
+ * 
+ * Returns <graphY>.
+ */
+mxMouseEvent.prototype.getGraphY = function()
+{
+	return this.graphY;
+};
+
+/**
+ * Function: getState
+ * 
+ * Returns <state>.
+ */
+mxMouseEvent.prototype.getState = function()
+{
+	return this.state;
+};
+
+/**
+ * Function: getCell
+ * 
+ * Returns the <mxCell> in <state> is not null.
+ */
+mxMouseEvent.prototype.getCell = function()
+{
+	var state = this.getState();
+	
+	if (state != null)
+	{
+		return state.cell;
+	}
+	
+	return null;
+};
+
+/**
+ * Function: isPopupTrigger
+ *
+ * Returns true if the event is a popup trigger.
+ */
+mxMouseEvent.prototype.isPopupTrigger = function()
+{
+	return mxEvent.isPopupTrigger(this.getEvent());
+};
+
+/**
+ * Function: isConsumed
+ *
+ * Returns <consumed>.
+ */
+mxMouseEvent.prototype.isConsumed = function()
+{
+	return this.consumed;
+};
+
+/**
+ * Function: consume
+ *
+ * Sets <consumed> to true and invokes preventDefault on the native event
+ * if such a method is defined. This is used mainly to avoid the cursor from
+ * being changed to a text cursor in Webkit. You can use the preventDefault
+ * flag to disable this functionality.
+ * 
+ * Parameters:
+ * 
+ * preventDefault - Specifies if the native event should be canceled. Default
+ * is true.
+ */
+mxMouseEvent.prototype.consume = function(preventDefault)
+{
+	preventDefault = (preventDefault != null) ? preventDefault : true;
+	
+	if (preventDefault && this.evt.preventDefault)
+	{
+		this.evt.preventDefault();
+	}
+
+	// Workaround for images being dragged in IE
+	// Does not change returnValue in Opera
+	if (mxClient.IS_IE)
+	{
+		this.evt.returnValue = true;
+	}
+
+	// Sets local consumed state
+	this.consumed = true;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxEventSource
+ *
+ * Base class for objects that dispatch named events. To create a subclass that
+ * inherits from mxEventSource, the following code is used.
+ *
+ * (code)
+ * function MyClass() { };
+ *
+ * MyClass.prototype = new mxEventSource();
+ * MyClass.prototype.constructor = MyClass;
+ * (end)
+ *
+ * Known Subclasses:
+ *
+ * <mxGraphModel>, <mxGraph>, <mxGraphView>, <mxEditor>, <mxCellOverlay>,
+ * <mxToolbar>, <mxWindow>
+ * 
+ * Constructor: mxEventSource
+ *
+ * Constructs a new event source.
+ */
+function mxEventSource(eventSource)
+{
+	this.setEventSource(eventSource);
+};
+
+/**
+ * Variable: eventListeners
+ *
+ * Holds the event names and associated listeners in an array. The array
+ * contains the event name followed by the respective listener for each
+ * registered listener.
+ */
+mxEventSource.prototype.eventListeners = null;
+
+/**
+ * Variable: eventsEnabled
+ *
+ * Specifies if events can be fired. Default is true.
+ */
+mxEventSource.prototype.eventsEnabled = true;
+
+/**
+ * Variable: eventSource
+ *
+ * Optional source for events. Default is null.
+ */
+mxEventSource.prototype.eventSource = null;
+
+/**
+ * Function: isEventsEnabled
+ * 
+ * Returns <eventsEnabled>.
+ */
+mxEventSource.prototype.isEventsEnabled = function()
+{
+	return this.eventsEnabled;
+};
+
+/**
+ * Function: setEventsEnabled
+ * 
+ * Sets <eventsEnabled>.
+ */
+mxEventSource.prototype.setEventsEnabled = function(value)
+{
+	this.eventsEnabled = value;
+};
+
+/**
+ * Function: getEventSource
+ * 
+ * Returns <eventSource>.
+ */
+mxEventSource.prototype.getEventSource = function()
+{
+	return this.eventSource;
+};
+
+/**
+ * Function: setEventSource
+ * 
+ * Sets <eventSource>.
+ */
+mxEventSource.prototype.setEventSource = function(value)
+{
+	this.eventSource = value;
+};
+
+/**
+ * Function: addListener
+ *
+ * Binds the specified function to the given event name. If no event name
+ * is given, then the listener is registered for all events.
+ * 
+ * The parameters of the listener are the sender and an <mxEventObject>.
+ */
+mxEventSource.prototype.addListener = function(name, funct)
+{
+	if (this.eventListeners == null)
+	{
+		this.eventListeners = [];
+	}
+	
+	this.eventListeners.push(name);
+	this.eventListeners.push(funct);
+};
+
+/**
+ * Function: removeListener
+ *
+ * Removes all occurrences of the given listener from <eventListeners>.
+ */
+mxEventSource.prototype.removeListener = function(funct)
+{
+	if (this.eventListeners != null)
+	{
+		var i = 0;
+		
+		while (i < this.eventListeners.length)
+		{
+			if (this.eventListeners[i+1] == funct)
+			{
+				this.eventListeners.splice(i, 2);
+			}
+			else
+			{
+				i += 2;
+			}
+		}
+	}
+};
+
+/**
+ * Function: fireEvent
+ *
+ * Dispatches the given event to the listeners which are registered for
+ * the event. The sender argument is optional. The current execution scope
+ * ("this") is used for the listener invocation (see <mxUtils.bind>).
+ *
+ * Example:
+ *
+ * (code)
+ * fireEvent(new mxEventObject("eventName", key1, val1, .., keyN, valN))
+ * (end)
+ * 
+ * Parameters:
+ *
+ * evt - <mxEventObject> that represents the event.
+ * sender - Optional sender to be passed to the listener. Default value is
+ * the return value of <getEventSource>.
+ */
+mxEventSource.prototype.fireEvent = function(evt, sender)
+{
+	if (this.eventListeners != null && this.isEventsEnabled())
+	{
+		if (evt == null)
+		{
+			evt = new mxEventObject();
+		}
+		
+		if (sender == null)
+		{
+			sender = this.getEventSource();
+		}
+
+		if (sender == null)
+		{
+			sender = this;
+		}
+
+		var args = [sender, evt];
+		
+		for (var i = 0; i < this.eventListeners.length; i += 2)
+		{
+			var listen = this.eventListeners[i];
+			
+			if (listen == null || listen == evt.getName())
+			{
+				this.eventListeners[i+1].apply(this, args);
+			}
+		}
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxEvent =
+{
+
+	/**
+	 * Class: mxEvent
+	 * 
+	 * Cross-browser DOM event support. For internal event handling,
+	 * <mxEventSource> and the graph event dispatch loop in <mxGraph> are used.
+	 * 
+	 * Memory Leaks:
+	 * 
+	 * Use this class for adding and removing listeners to/from DOM nodes. The
+	 * <removeAllListeners> function is provided to remove all listeners that
+	 * have been added using <addListener>. The function should be invoked when
+	 * the last reference is removed in the JavaScript code, typically when the
+	 * referenced DOM node is removed from the DOM, and helps to reduce memory
+	 * leaks in IE6.
+	 * 
+	 * Variable: objects
+	 * 
+	 * Contains all objects where any listener was added using <addListener>.
+	 * This is used to reduce memory leaks in IE, see <mxClient.dispose>.
+	 */
+	objects: [],
+
+	 /**
+	  * Function: addListener
+	  * 
+	  * Binds the function to the specified event on the given element. Use
+	  * <mxUtils.bind> in order to bind the "this" keyword inside the function
+	  * to a given execution scope.
+	  */
+	addListener: function()
+	{
+		var updateListenerList = function(element, eventName, funct)
+		{
+			if (element.mxListenerList == null)
+			{
+				element.mxListenerList = [];
+				mxEvent.objects.push(element);
+			}
+			
+			var entry = {name: eventName, f: funct};
+			element.mxListenerList.push(entry);
+		};
+		
+		if (window.addEventListener)
+		{
+			return function(element, eventName, funct)
+			{
+				element.addEventListener(eventName, funct, false);
+				updateListenerList(element, eventName, funct);
+			};
+		}
+		else
+		{
+			return function(element, eventName, funct)
+			{
+				element.attachEvent('on' + eventName, funct);
+				updateListenerList(element, eventName, funct);				
+			};
+		}
+	}(),
+
+	/**
+	 * Function: removeListener
+	 *
+	 * Removes the specified listener from the given element.
+	 */
+	removeListener: function()
+	{
+		var updateListener = function(element, eventName, funct)
+		{
+			if (element.mxListenerList != null)
+			{
+				var listenerCount = element.mxListenerList.length;
+				
+				for (var i = 0; i < listenerCount; i++)
+				{
+					var entry = element.mxListenerList[i];
+					
+					if (entry.f == funct)
+					{
+						element.mxListenerList.splice(i, 1);
+						break;
+					}
+				}
+				
+				if (element.mxListenerList.length == 0)
+				{
+					element.mxListenerList = null;
+					
+					var idx = mxUtils.indexOf(mxEvent.objects, element);
+					
+					if (idx >= 0)
+					{
+						mxEvent.objects.splice(idx, 1);
+					}
+				}
+			}
+		};
+		
+		if (window.removeEventListener)
+		{
+			return function(element, eventName, funct)
+			{
+				element.removeEventListener(eventName, funct, false);
+				updateListener(element, eventName, funct);
+			};
+		}
+		else
+		{
+			return function(element, eventName, funct)
+			{
+				element.detachEvent('on' + eventName, funct);
+				updateListener(element, eventName, funct);
+			};
+		}
+	}(),
+
+	/**
+	 * Function: removeAllListeners
+	 * 
+	 * Removes all listeners from the given element.
+	 */
+	removeAllListeners: function(element)
+	{
+		var list = element.mxListenerList;
+
+		if (list != null)
+		{
+			while (list.length > 0)
+			{
+				var entry = list[0];
+				mxEvent.removeListener(element, entry.name, entry.f);
+			}
+		}
+	},
+	
+	/**
+	 * Function: addGestureListeners
+	 * 
+	 * Adds the given listeners for touch, mouse and/or pointer events. If
+	 * <mxClient.IS_POINTER> is true then pointer events will be registered,
+	 * else the respective mouse events will be registered. If <mxClient.IS_POINTER>
+	 * is false and <mxClient.IS_TOUCH> is true then the respective touch events
+	 * will be registered as well as the mouse events.
+	 */
+	addGestureListeners: function(node, startListener, moveListener, endListener)
+	{
+		if (startListener != null)
+		{
+			mxEvent.addListener(node, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown', startListener);
+		}
+		
+		if (moveListener != null)
+		{
+			mxEvent.addListener(node, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', moveListener);
+		}
+		
+		if (endListener != null)
+		{
+			mxEvent.addListener(node, (mxClient.IS_POINTER) ? 'pointerup' : 'mouseup', endListener);
+		}
+		
+		if (!mxClient.IS_POINTER && mxClient.IS_TOUCH)
+		{
+			if (startListener != null)
+			{
+				mxEvent.addListener(node, 'touchstart', startListener);
+			}
+			
+			if (moveListener != null)
+			{
+				mxEvent.addListener(node, 'touchmove', moveListener);
+			}
+			
+			if (endListener != null)
+			{
+				mxEvent.addListener(node, 'touchend', endListener);
+			}
+		}
+	},
+	
+	/**
+	 * Function: removeGestureListeners
+	 * 
+	 * Removes the given listeners from mousedown, mousemove, mouseup and the
+	 * respective touch events if <mxClient.IS_TOUCH> is true.
+	 */
+	removeGestureListeners: function(node, startListener, moveListener, endListener)
+	{
+		if (startListener != null)
+		{
+			mxEvent.removeListener(node, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown', startListener);
+		}
+		
+		if (moveListener != null)
+		{
+			mxEvent.removeListener(node, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', moveListener);
+		}
+		
+		if (endListener != null)
+		{
+			mxEvent.removeListener(node, (mxClient.IS_POINTER) ? 'pointerup' : 'mouseup', endListener);
+		}
+		
+		if (!mxClient.IS_POINTER && mxClient.IS_TOUCH)
+		{
+			if (startListener != null)
+			{
+				mxEvent.removeListener(node, 'touchstart', startListener);
+			}
+			
+			if (moveListener != null)
+			{
+				mxEvent.removeListener(node, 'touchmove', moveListener);
+			}
+			
+			if (endListener != null)
+			{
+				mxEvent.removeListener(node, 'touchend', endListener);
+			}
+		}
+	},
+	
+	/**
+	 * Function: redirectMouseEvents
+	 *
+	 * Redirects the mouse events from the given DOM node to the graph dispatch
+	 * loop using the event and given state as event arguments. State can
+	 * either be an instance of <mxCellState> or a function that returns an
+	 * <mxCellState>. The down, move, up and dblClick arguments are optional
+	 * functions that take the trigger event as arguments and replace the
+	 * default behaviour.
+	 */
+	redirectMouseEvents: function(node, graph, state, down, move, up, dblClick)
+	{
+		var getState = function(evt)
+		{
+			return (typeof(state) == 'function') ? state(evt) : state;
+		};
+		
+		mxEvent.addGestureListeners(node, function (evt)
+		{
+			if (down != null)
+			{
+				down(evt);
+			}
+			else if (!mxEvent.isConsumed(evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, getState(evt)));
+			}
+		},
+		function (evt)
+		{
+			if (move != null)
+			{
+				move(evt);
+			}
+			else if (!mxEvent.isConsumed(evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, getState(evt)));
+			}
+		},
+		function (evt)
+		{
+			if (up != null)
+			{
+				up(evt);
+			}
+			else if (!mxEvent.isConsumed(evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, getState(evt)));
+			}
+		});
+
+		mxEvent.addListener(node, 'dblclick', function (evt)
+		{
+			if (dblClick != null)
+			{
+				dblClick(evt);
+			}
+			else if (!mxEvent.isConsumed(evt))
+			{
+				var tmp = getState(evt);
+				graph.dblClick(evt, (tmp != null) ? tmp.cell : null);
+			}
+		});
+	},
+
+	/**
+	 * Function: release
+	 * 
+	 * Removes the known listeners from the given DOM node and its descendants.
+	 * 
+	 * Parameters:
+	 * 
+	 * element - DOM node to remove the listeners from.
+	 */
+	release: function(element)
+	{
+		if (element != null)
+		{
+			mxEvent.removeAllListeners(element);
+			
+			var children = element.childNodes;
+			
+			if (children != null)
+			{
+		        var childCount = children.length;
+		        
+		        for (var i = 0; i < childCount; i += 1)
+		        {
+		        	mxEvent.release(children[i]);
+		        }
+		    }
+		}
+	},
+
+	/**
+	 * Function: addMouseWheelListener
+	 * 
+	 * Installs the given function as a handler for mouse wheel events. The
+	 * function has two arguments: the mouse event and a boolean that specifies
+	 * if the wheel was moved up or down.
+	 * 
+	 * This has been tested with IE 6 and 7, Firefox (all versions), Opera and
+	 * Safari. It does currently not work on Safari for Mac.
+	 * 
+	 * Example:
+	 * 
+	 * (code)
+	 * mxEvent.addMouseWheelListener(function (evt, up)
+	 * {
+	 *   mxLog.show();
+	 *   mxLog.debug('mouseWheel: up='+up);
+	 * });
+	 *(end)
+	 * 
+	 * Parameters:
+	 * 
+	 * funct - Handler function that takes the event argument and a boolean up
+	 * argument for the mousewheel direction.
+	 */
+	addMouseWheelListener: function(funct)
+	{
+		if (funct != null)
+		{
+			var wheelHandler = function(evt)
+			{
+				// IE does not give an event object but the
+				// global event object is the mousewheel event
+				// at this point in time.
+				if (evt == null)
+				{
+					evt = window.event;
+				}
+			
+				var delta = 0;
+				
+				if (mxClient.IS_FF)
+				{
+					delta = -evt.detail / 2;
+				}
+				else
+				{
+					delta = evt.wheelDelta / 120;
+				}
+				
+				// Handles the event using the given function
+				if (delta != 0)
+				{
+					funct(evt, delta > 0);
+				}
+			};
+	
+			// Webkit has NS event API, but IE event name and details 
+			if (mxClient.IS_NS && document.documentMode == null)
+			{
+				var eventName = (mxClient.IS_SF || 	mxClient.IS_GC) ? 'mousewheel' : 'DOMMouseScroll';
+				mxEvent.addListener(window, eventName, wheelHandler);
+			}
+			else
+			{
+				mxEvent.addListener(document, 'mousewheel', wheelHandler);
+			}
+		}
+	},
+	
+	/**
+	 * Function: disableContextMenu
+	 *
+	 * Disables the context menu for the given element.
+	 */
+	disableContextMenu: function()
+	{
+		if (mxClient.IS_IE && (typeof(document.documentMode) === 'undefined' || document.documentMode < 9))
+		{
+			return function(element)
+			{
+				mxEvent.addListener(element, 'contextmenu', function()
+				{
+					return false;
+				});
+			};
+		}
+		else
+		{
+			return function(element)
+			{
+				element.setAttribute('oncontextmenu', 'return false;');
+			};		
+		}
+	}(),
+	
+	/**
+	 * Function: getSource
+	 * 
+	 * Returns the event's target or srcElement depending on the browser.
+	 */
+	getSource: function(evt)
+	{
+		return (evt.srcElement != null) ? evt.srcElement : evt.target;
+	},
+
+	/**
+	 * Function: isConsumed
+	 * 
+	 * Returns true if the event has been consumed using <consume>.
+	 */
+	isConsumed: function(evt)
+	{
+		return evt.isConsumed != null && evt.isConsumed;
+	},
+
+	/**
+	 * Function: isTouchEvent
+	 * 
+	 * Returns true if the event was generated using a touch device (not a pen or mouse).
+	 */
+	isTouchEvent: function(evt)
+	{
+		return (evt.pointerType != null) ? (evt.pointerType == 'touch' || evt.pointerType ===
+			evt.MSPOINTER_TYPE_TOUCH) : ((evt.mozInputSource != null) ?
+					evt.mozInputSource == 5 : evt.type.indexOf('touch') == 0);
+	},
+
+	/**
+	 * Function: isPenEvent
+	 * 
+	 * Returns true if the event was generated using a pen (not a touch device or mouse).
+	 */
+	isPenEvent: function(evt)
+	{
+		return (evt.pointerType != null) ? (evt.pointerType == 'pen' || evt.pointerType ===
+			evt.MSPOINTER_TYPE_PEN) : ((evt.mozInputSource != null) ?
+					evt.mozInputSource == 2 : evt.type.indexOf('pen') == 0);
+	},
+
+	/**
+	 * Function: isMultiTouchEvent
+	 * 
+	 * Returns true if the event was generated using a touch device (not a pen or mouse).
+	 */
+	isMultiTouchEvent: function(evt)
+	{
+		return (evt.type != null && evt.type.indexOf('touch') == 0 && evt.touches != null && evt.touches.length > 1);
+	},
+
+	/**
+	 * Function: isMouseEvent
+	 * 
+	 * Returns true if the event was generated using a mouse (not a pen or touch device).
+	 */
+	isMouseEvent: function(evt)
+	{
+		return (evt.pointerType != null) ? (evt.pointerType == 'mouse' || evt.pointerType ===
+			evt.MSPOINTER_TYPE_MOUSE) : ((evt.mozInputSource != null) ?
+				evt.mozInputSource == 1 : evt.type.indexOf('mouse') == 0);
+	},
+	
+	/**
+	 * Function: isLeftMouseButton
+	 * 
+	 * Returns true if the left mouse button is pressed for the given event.
+	 * To check if a button is pressed during a mouseMove you should use the
+	 * <mxGraph.isMouseDown> property. Note that this returns true in Firefox
+	 * for control+left-click on the Mac.
+	 */
+	isLeftMouseButton: function(evt)
+	{
+		// Special case for mousemove and mousedown we check the buttons
+		// if it exists because which is 0 even if no button is pressed
+		if ('buttons' in evt && (evt.type == 'mousedown' || evt.type == 'mousemove'))
+		{
+			return evt.buttons == 1;
+		}
+		else if ('which' in evt)
+		{
+	        return evt.which === 1;
+	    }
+		else
+		{
+	        return evt.button === 1;
+	    }
+	},
+	
+	/**
+	 * Function: isMiddleMouseButton
+	 * 
+	 * Returns true if the middle mouse button is pressed for the given event.
+	 * To check if a button is pressed during a mouseMove you should use the
+	 * <mxGraph.isMouseDown> property.
+	 */
+	isMiddleMouseButton: function(evt)
+	{
+		if ('which' in evt)
+		{
+	        return evt.which === 2;
+	    }
+		else
+		{
+	        return evt.button === 4;
+	    }
+	},
+	
+	/**
+	 * Function: isRightMouseButton
+	 * 
+	 * Returns true if the right mouse button was pressed. Note that this
+	 * button might not be available on some systems. For handling a popup
+	 * trigger <isPopupTrigger> should be used.
+	 */
+	isRightMouseButton: function(evt)
+	{
+		if ('which' in evt)
+		{
+	        return evt.which === 3;
+	    }
+		else
+		{
+	        return evt.button === 2;
+	    }
+	},
+
+	/**
+	 * Function: isPopupTrigger
+	 * 
+	 * Returns true if the event is a popup trigger. This implementation
+	 * returns true if the right button or the left button and control was
+	 * pressed on a Mac.
+	 */
+	isPopupTrigger: function(evt)
+	{
+		return mxEvent.isRightMouseButton(evt) || (mxClient.IS_MAC && mxEvent.isControlDown(evt) &&
+			!mxEvent.isShiftDown(evt) && !mxEvent.isMetaDown(evt) && !mxEvent.isAltDown(evt));
+	},
+
+	/**
+	 * Function: isShiftDown
+	 * 
+	 * Returns true if the shift key is pressed for the given event.
+	 */
+	isShiftDown: function(evt)
+	{
+		return (evt != null) ? evt.shiftKey : false;
+	},
+
+	/**
+	 * Function: isAltDown
+	 * 
+	 * Returns true if the alt key is pressed for the given event.
+	 */
+	isAltDown: function(evt)
+	{
+		return (evt != null) ? evt.altKey : false;
+	},
+
+	/**
+	 * Function: isControlDown
+	 * 
+	 * Returns true if the control key is pressed for the given event.
+	 */
+	isControlDown: function(evt)
+	{
+		return (evt != null) ? evt.ctrlKey : false;
+	},
+
+	/**
+	 * Function: isMetaDown
+	 * 
+	 * Returns true if the meta key is pressed for the given event.
+	 */
+	isMetaDown: function(evt)
+	{
+		return (evt != null) ? evt.metaKey : false;
+	},
+
+	/**
+	 * Function: getMainEvent
+	 * 
+	 * Returns the touch or mouse event that contains the mouse coordinates.
+	 */
+	getMainEvent: function(e)
+	{
+		if ((e.type == 'touchstart' || e.type == 'touchmove') && e.touches != null && e.touches[0] != null)
+		{
+			e = e.touches[0];
+		}
+		else if (e.type == 'touchend' && e.changedTouches != null && e.changedTouches[0] != null)
+		{
+			e = e.changedTouches[0];
+		}
+		
+		return e;
+	},
+	
+	/**
+	 * Function: getClientX
+	 * 
+	 * Returns true if the meta key is pressed for the given event.
+	 */
+	getClientX: function(e)
+	{
+		return mxEvent.getMainEvent(e).clientX;
+	},
+
+	/**
+	 * Function: getClientY
+	 * 
+	 * Returns true if the meta key is pressed for the given event.
+	 */
+	getClientY: function(e)
+	{
+		return mxEvent.getMainEvent(e).clientY;
+	},
+
+	/**
+	 * Function: consume
+	 * 
+	 * Consumes the given event.
+	 * 
+	 * Parameters:
+	 * 
+	 * evt - Native event to be consumed.
+	 * preventDefault - Optional boolean to prevent the default for the event.
+	 * Default is true.
+	 * stopPropagation - Option boolean to stop event propagation. Default is
+	 * true.
+	 */
+	consume: function(evt, preventDefault, stopPropagation)
+	{
+		preventDefault = (preventDefault != null) ? preventDefault : true;
+		stopPropagation = (stopPropagation != null) ? stopPropagation : true;
+		
+		if (preventDefault)
+		{
+			if (evt.preventDefault)
+			{
+				if (stopPropagation)
+				{
+					evt.stopPropagation();
+				}
+				
+				evt.preventDefault();
+			}
+			else if (stopPropagation)
+			{
+				evt.cancelBubble = true;
+			}
+		}
+
+		// Opera
+		evt.isConsumed = true;
+
+		// Other browsers
+		if (!evt.preventDefault)
+		{
+			evt.returnValue = false;
+		}
+	},
+	
+	//
+	// Special handles in mouse events
+	//
+	
+	/**
+	 * Variable: LABEL_HANDLE
+	 * 
+	 * Index for the label handle in an mxMouseEvent. This should be a negative
+	 * value that does not interfere with any possible handle indices. Default
+	 * is -1.
+	 */
+	LABEL_HANDLE: -1,
+	
+	/**
+	 * Variable: ROTATION_HANDLE
+	 * 
+	 * Index for the rotation handle in an mxMouseEvent. This should be a
+	 * negative value that does not interfere with any possible handle indices.
+	 * Default is -2.
+	 */
+	ROTATION_HANDLE: -2,
+	
+	/**
+	 * Variable: CUSTOM_HANDLE
+	 * 
+	 * Start index for the custom handles in an mxMouseEvent. This should be a
+	 * negative value and is the start index which is decremented for each
+	 * custom handle. Default is -100.
+	 */
+	CUSTOM_HANDLE: -100,
+	
+	/**
+	 * Variable: VIRTUAL_HANDLE
+	 * 
+	 * Start index for the virtual handles in an mxMouseEvent. This should be a
+	 * negative value and is the start index which is decremented for each
+	 * virtual handle. Default is -100000. This assumes that there are no more
+	 * than VIRTUAL_HANDLE - CUSTOM_HANDLE custom handles.
+	 * 
+	 */
+	VIRTUAL_HANDLE: -100000,
+	
+	//
+	// Event names
+	//
+	
+	/**
+	 * Variable: MOUSE_DOWN
+	 *
+	 * Specifies the event name for mouseDown.
+	 */
+	MOUSE_DOWN: 'mouseDown',
+	
+	/**
+	 * Variable: MOUSE_MOVE
+	 *
+	 * Specifies the event name for mouseMove. 
+	 */
+	MOUSE_MOVE: 'mouseMove',
+	
+	/**
+	 * Variable: MOUSE_UP
+	 *
+	 * Specifies the event name for mouseUp. 
+	 */
+	MOUSE_UP: 'mouseUp',
+
+	/**
+	 * Variable: ACTIVATE
+	 *
+	 * Specifies the event name for activate.
+	 */
+	ACTIVATE: 'activate',
+
+	/**
+	 * Variable: RESIZE_START
+	 *
+	 * Specifies the event name for resizeStart.
+	 */
+	RESIZE_START: 'resizeStart',
+
+	/**
+	 * Variable: RESIZE
+	 *
+	 * Specifies the event name for resize.
+	 */
+	RESIZE: 'resize',
+
+	/**
+	 * Variable: RESIZE_END
+	 *
+	 * Specifies the event name for resizeEnd.
+	 */
+	RESIZE_END: 'resizeEnd',
+
+	/**
+	 * Variable: MOVE_START
+	 *
+	 * Specifies the event name for moveStart.
+	 */
+	MOVE_START: 'moveStart',
+
+	/**
+	 * Variable: MOVE
+	 *
+	 * Specifies the event name for move.
+	 */
+	MOVE: 'move',
+
+	/**
+	 * Variable: MOVE_END
+	 *
+	 * Specifies the event name for moveEnd.
+	 */
+	MOVE_END: 'moveEnd',
+
+	/**
+	 * Variable: PAN_START
+	 *
+	 * Specifies the event name for panStart.
+	 */
+	PAN_START: 'panStart',
+
+	/**
+	 * Variable: PAN
+	 *
+	 * Specifies the event name for pan.
+	 */
+	PAN: 'pan',
+
+	/**
+	 * Variable: PAN_END
+	 *
+	 * Specifies the event name for panEnd.
+	 */
+	PAN_END: 'panEnd',
+
+	/**
+	 * Variable: MINIMIZE
+	 *
+	 * Specifies the event name for minimize.
+	 */
+	MINIMIZE: 'minimize',
+
+	/**
+	 * Variable: NORMALIZE
+	 *
+	 * Specifies the event name for normalize.
+	 */
+	NORMALIZE: 'normalize',
+
+	/**
+	 * Variable: MAXIMIZE
+	 *
+	 * Specifies the event name for maximize.
+	 */
+	MAXIMIZE: 'maximize',
+
+	/**
+	 * Variable: HIDE
+	 *
+	 * Specifies the event name for hide.
+	 */
+	HIDE: 'hide',
+
+	/**
+	 * Variable: SHOW
+	 *
+	 * Specifies the event name for show.
+	 */
+	SHOW: 'show',
+
+	/**
+	 * Variable: CLOSE
+	 *
+	 * Specifies the event name for close.
+	 */
+	CLOSE: 'close',
+
+	/**
+	 * Variable: DESTROY
+	 *
+	 * Specifies the event name for destroy.
+	 */
+	DESTROY: 'destroy',
+
+	/**
+	 * Variable: REFRESH
+	 *
+	 * Specifies the event name for refresh.
+	 */
+	REFRESH: 'refresh',
+
+	/**
+	 * Variable: SIZE
+	 *
+	 * Specifies the event name for size.
+	 */
+	SIZE: 'size',
+	
+	/**
+	 * Variable: SELECT
+	 *
+	 * Specifies the event name for select.
+	 */
+	SELECT: 'select',
+
+	/**
+	 * Variable: FIRED
+	 *
+	 * Specifies the event name for fired.
+	 */
+	FIRED: 'fired',
+
+	/**
+	 * Variable: FIRE_MOUSE_EVENT
+	 *
+	 * Specifies the event name for fireMouseEvent.
+	 */
+	FIRE_MOUSE_EVENT: 'fireMouseEvent',
+
+	/**
+	 * Variable: GESTURE
+	 *
+	 * Specifies the event name for gesture.
+	 */
+	GESTURE: 'gesture',
+
+	/**
+	 * Variable: TAP_AND_HOLD
+	 *
+	 * Specifies the event name for tapAndHold.
+	 */
+	TAP_AND_HOLD: 'tapAndHold',
+
+	/**
+	 * Variable: GET
+	 *
+	 * Specifies the event name for get.
+	 */
+	GET: 'get',
+
+	/**
+	 * Variable: RECEIVE
+	 *
+	 * Specifies the event name for receive.
+	 */
+	RECEIVE: 'receive',
+
+	/**
+	 * Variable: CONNECT
+	 *
+	 * Specifies the event name for connect.
+	 */
+	CONNECT: 'connect',
+
+	/**
+	 * Variable: DISCONNECT
+	 *
+	 * Specifies the event name for disconnect.
+	 */
+	DISCONNECT: 'disconnect',
+
+	/**
+	 * Variable: SUSPEND
+	 *
+	 * Specifies the event name for suspend.
+	 */
+	SUSPEND: 'suspend',
+
+	/**
+	 * Variable: RESUME
+	 *
+	 * Specifies the event name for suspend.
+	 */
+	RESUME: 'resume',
+
+	/**
+	 * Variable: MARK
+	 *
+	 * Specifies the event name for mark.
+	 */
+	MARK: 'mark',
+
+	/**
+	 * Variable: ROOT
+	 *
+	 * Specifies the event name for root.
+	 */
+	ROOT: 'root',
+
+	/**
+	 * Variable: POST
+	 *
+	 * Specifies the event name for post.
+	 */
+	POST: 'post',
+
+	/**
+	 * Variable: OPEN
+	 *
+	 * Specifies the event name for open.
+	 */
+	OPEN: 'open',
+
+	/**
+	 * Variable: SAVE
+	 *
+	 * Specifies the event name for open.
+	 */
+	SAVE: 'save',
+
+	/**
+	 * Variable: BEFORE_ADD_VERTEX
+	 *
+	 * Specifies the event name for beforeAddVertex.
+	 */
+	BEFORE_ADD_VERTEX: 'beforeAddVertex',
+
+	/**
+	 * Variable: ADD_VERTEX
+	 *
+	 * Specifies the event name for addVertex.
+	 */
+	ADD_VERTEX: 'addVertex',
+
+	/**
+	 * Variable: AFTER_ADD_VERTEX
+	 *
+	 * Specifies the event name for afterAddVertex.
+	 */
+	AFTER_ADD_VERTEX: 'afterAddVertex',
+
+	/**
+	 * Variable: DONE
+	 *
+	 * Specifies the event name for done.
+	 */
+	DONE: 'done',
+
+	/**
+	 * Variable: EXECUTE
+	 *
+	 * Specifies the event name for execute.
+	 */
+	EXECUTE: 'execute',
+
+	/**
+	 * Variable: EXECUTED
+	 *
+	 * Specifies the event name for executed.
+	 */
+	EXECUTED: 'executed',
+
+	/**
+	 * Variable: BEGIN_UPDATE
+	 *
+	 * Specifies the event name for beginUpdate.
+	 */
+	BEGIN_UPDATE: 'beginUpdate',
+
+	/**
+	 * Variable: START_EDIT
+	 *
+	 * Specifies the event name for startEdit.
+	 */
+	START_EDIT: 'startEdit',
+
+	/**
+	 * Variable: END_UPDATE
+	 *
+	 * Specifies the event name for endUpdate.
+	 */
+	END_UPDATE: 'endUpdate',
+
+	/**
+	 * Variable: END_EDIT
+	 *
+	 * Specifies the event name for endEdit.
+	 */
+	END_EDIT: 'endEdit',
+
+	/**
+	 * Variable: BEFORE_UNDO
+	 *
+	 * Specifies the event name for beforeUndo.
+	 */
+	BEFORE_UNDO: 'beforeUndo',
+
+	/**
+	 * Variable: UNDO
+	 *
+	 * Specifies the event name for undo.
+	 */
+	UNDO: 'undo',
+
+	/**
+	 * Variable: REDO
+	 *
+	 * Specifies the event name for redo.
+	 */
+	REDO: 'redo',
+
+	/**
+	 * Variable: CHANGE
+	 *
+	 * Specifies the event name for change.
+	 */
+	CHANGE: 'change',
+
+	/**
+	 * Variable: NOTIFY
+	 *
+	 * Specifies the event name for notify.
+	 */
+	NOTIFY: 'notify',
+
+	/**
+	 * Variable: LAYOUT_CELLS
+	 *
+	 * Specifies the event name for layoutCells.
+	 */
+	LAYOUT_CELLS: 'layoutCells',
+
+	/**
+	 * Variable: CLICK
+	 *
+	 * Specifies the event name for click.
+	 */
+	CLICK: 'click',
+
+	/**
+	 * Variable: SCALE
+	 *
+	 * Specifies the event name for scale.
+	 */
+	SCALE: 'scale',
+
+	/**
+	 * Variable: TRANSLATE
+	 *
+	 * Specifies the event name for translate.
+	 */
+	TRANSLATE: 'translate',
+
+	/**
+	 * Variable: SCALE_AND_TRANSLATE
+	 *
+	 * Specifies the event name for scaleAndTranslate.
+	 */
+	SCALE_AND_TRANSLATE: 'scaleAndTranslate',
+
+	/**
+	 * Variable: UP
+	 *
+	 * Specifies the event name for up.
+	 */
+	UP: 'up',
+
+	/**
+	 * Variable: DOWN
+	 *
+	 * Specifies the event name for down.
+	 */
+	DOWN: 'down',
+
+	/**
+	 * Variable: ADD
+	 *
+	 * Specifies the event name for add.
+	 */
+	ADD: 'add',
+
+	/**
+	 * Variable: REMOVE
+	 *
+	 * Specifies the event name for remove.
+	 */
+	REMOVE: 'remove',
+	
+	/**
+	 * Variable: CLEAR
+	 *
+	 * Specifies the event name for clear.
+	 */
+	CLEAR: 'clear',
+
+	/**
+	 * Variable: ADD_CELLS
+	 *
+	 * Specifies the event name for addCells.
+	 */
+	ADD_CELLS: 'addCells',
+
+	/**
+	 * Variable: CELLS_ADDED
+	 *
+	 * Specifies the event name for cellsAdded.
+	 */
+	CELLS_ADDED: 'cellsAdded',
+
+	/**
+	 * Variable: MOVE_CELLS
+	 *
+	 * Specifies the event name for moveCells.
+	 */
+	MOVE_CELLS: 'moveCells',
+
+	/**
+	 * Variable: CELLS_MOVED
+	 *
+	 * Specifies the event name for cellsMoved.
+	 */
+	CELLS_MOVED: 'cellsMoved',
+
+	/**
+	 * Variable: RESIZE_CELLS
+	 *
+	 * Specifies the event name for resizeCells.
+	 */
+	RESIZE_CELLS: 'resizeCells',
+
+	/**
+	 * Variable: CELLS_RESIZED
+	 *
+	 * Specifies the event name for cellsResized.
+	 */
+	CELLS_RESIZED: 'cellsResized',
+
+	/**
+	 * Variable: TOGGLE_CELLS
+	 *
+	 * Specifies the event name for toggleCells.
+	 */
+	TOGGLE_CELLS: 'toggleCells',
+
+	/**
+	 * Variable: CELLS_TOGGLED
+	 *
+	 * Specifies the event name for cellsToggled.
+	 */
+	CELLS_TOGGLED: 'cellsToggled',
+
+	/**
+	 * Variable: ORDER_CELLS
+	 *
+	 * Specifies the event name for orderCells.
+	 */
+	ORDER_CELLS: 'orderCells',
+
+	/**
+	 * Variable: CELLS_ORDERED
+	 *
+	 * Specifies the event name for cellsOrdered.
+	 */
+	CELLS_ORDERED: 'cellsOrdered',
+
+	/**
+	 * Variable: REMOVE_CELLS
+	 *
+	 * Specifies the event name for removeCells.
+	 */
+	REMOVE_CELLS: 'removeCells',
+
+	/**
+	 * Variable: CELLS_REMOVED
+	 *
+	 * Specifies the event name for cellsRemoved.
+	 */
+	CELLS_REMOVED: 'cellsRemoved',
+
+	/**
+	 * Variable: GROUP_CELLS
+	 *
+	 * Specifies the event name for groupCells.
+	 */
+	GROUP_CELLS: 'groupCells',
+
+	/**
+	 * Variable: UNGROUP_CELLS
+	 *
+	 * Specifies the event name for ungroupCells.
+	 */
+	UNGROUP_CELLS: 'ungroupCells',
+
+	/**
+	 * Variable: REMOVE_CELLS_FROM_PARENT
+	 *
+	 * Specifies the event name for removeCellsFromParent.
+	 */
+	REMOVE_CELLS_FROM_PARENT: 'removeCellsFromParent',
+
+	/**
+	 * Variable: FOLD_CELLS
+	 *
+	 * Specifies the event name for foldCells.
+	 */
+	FOLD_CELLS: 'foldCells',
+
+	/**
+	 * Variable: CELLS_FOLDED
+	 *
+	 * Specifies the event name for cellsFolded.
+	 */
+	CELLS_FOLDED: 'cellsFolded',
+
+	/**
+	 * Variable: ALIGN_CELLS
+	 *
+	 * Specifies the event name for alignCells.
+	 */
+	ALIGN_CELLS: 'alignCells',
+
+	/**
+	 * Variable: LABEL_CHANGED
+	 *
+	 * Specifies the event name for labelChanged.
+	 */
+	LABEL_CHANGED: 'labelChanged',
+
+	/**
+	 * Variable: CONNECT_CELL
+	 *
+	 * Specifies the event name for connectCell.
+	 */
+	CONNECT_CELL: 'connectCell',
+
+	/**
+	 * Variable: CELL_CONNECTED
+	 *
+	 * Specifies the event name for cellConnected.
+	 */
+	CELL_CONNECTED: 'cellConnected',
+
+	/**
+	 * Variable: SPLIT_EDGE
+	 *
+	 * Specifies the event name for splitEdge.
+	 */
+	SPLIT_EDGE: 'splitEdge',
+
+	/**
+	 * Variable: FLIP_EDGE
+	 *
+	 * Specifies the event name for flipEdge.
+	 */
+	FLIP_EDGE: 'flipEdge',
+
+	/**
+	 * Variable: START_EDITING
+	 *
+	 * Specifies the event name for startEditing.
+	 */
+	START_EDITING: 'startEditing',
+
+	/**
+	 * Variable: EDITING_STARTED
+	 *
+	 * Specifies the event name for editingStarted.
+	 */
+	EDITING_STARTED: 'editingStarted',
+
+	/**
+	 * Variable: EDITING_STOPPED
+	 *
+	 * Specifies the event name for editingStopped.
+	 */
+	EDITING_STOPPED: 'editingStopped',
+
+	/**
+	 * Variable: ADD_OVERLAY
+	 *
+	 * Specifies the event name for addOverlay.
+	 */
+	ADD_OVERLAY: 'addOverlay',
+
+	/**
+	 * Variable: REMOVE_OVERLAY
+	 *
+	 * Specifies the event name for removeOverlay.
+	 */
+	REMOVE_OVERLAY: 'removeOverlay',
+
+	/**
+	 * Variable: UPDATE_CELL_SIZE
+	 *
+	 * Specifies the event name for updateCellSize.
+	 */
+	UPDATE_CELL_SIZE: 'updateCellSize',
+
+	/**
+	 * Variable: ESCAPE
+	 *
+	 * Specifies the event name for escape.
+	 */
+	ESCAPE: 'escape',
+
+	/**
+	 * Variable: DOUBLE_CLICK
+	 *
+	 * Specifies the event name for doubleClick.
+	 */
+	DOUBLE_CLICK: 'doubleClick',
+
+	/**
+	 * Variable: START
+	 *
+	 * Specifies the event name for start.
+	 */
+	START: 'start',
+
+	/**
+	 * Variable: RESET
+	 *
+	 * Specifies the event name for reset.
+	 */
+	RESET: 'reset'
+
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxXmlRequest
+ * 
+ * XML HTTP request wrapper. See also: <mxUtils.get>, <mxUtils.post> and
+ * <mxUtils.load>. This class provides a cross-browser abstraction for Ajax
+ * requests.
+ * 
+ * Encoding:
+ * 
+ * For encoding parameter values, the built-in encodeURIComponent JavaScript
+ * method must be used. For automatic encoding of post data in <mxEditor> the
+ * <mxEditor.escapePostData> switch can be set to true (default). The encoding
+ * will be carried out using the conte type of the page. That is, the page
+ * containting the editor should contain a meta tag in the header, eg.
+ * <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ * 
+ * Example:
+ * 
+ * (code)
+ * var onload = function(req)
+ * {
+ *   mxUtils.alert(req.getDocumentElement());
+ * }
+ * 
+ * var onerror = function(req)
+ * {
+ *   mxUtils.alert('Error');
+ * }
+ * new mxXmlRequest(url, 'key=value').send(onload, onerror);
+ * (end)
+ * 
+ * Sends an asynchronous POST request to the specified URL.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var req = new mxXmlRequest(url, 'key=value', 'POST', false);
+ * req.send();
+ * mxUtils.alert(req.getDocumentElement());
+ * (end)
+ * 
+ * Sends a synchronous POST request to the specified URL.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var encoder = new mxCodec();
+ * var result = encoder.encode(graph.getModel());
+ * var xml = encodeURIComponent(mxUtils.getXml(result));
+ * new mxXmlRequest(url, 'xml='+xml).send();
+ * (end)
+ * 
+ * Sends an encoded graph model to the specified URL using xml as the
+ * parameter name. The parameter can then be retrieved in C# as follows:
+ * 
+ * (code)
+ * string xml = HttpUtility.UrlDecode(context.Request.Params["xml"]);
+ * (end)
+ * 
+ * Or in Java as follows:
+ * 
+ * (code)
+ * String xml = URLDecoder.decode(request.getParameter("xml"), "UTF-8").replace("\n", "&#xa;");
+ * (end)
+ *
+ * Note that the linefeeds should only be replaced if the XML is
+ * processed in Java, for example when creating an image.
+ * 
+ * Constructor: mxXmlRequest
+ * 
+ * Constructs an XML HTTP request.
+ * 
+ * Parameters:
+ * 
+ * url - Target URL of the request.
+ * params - Form encoded parameters to send with a POST request.
+ * method - String that specifies the request method. Possible values are
+ * POST and GET. Default is POST.
+ * async - Boolean specifying if an asynchronous request should be used.
+ * Default is true.
+ * username - String specifying the username to be used for the request.
+ * password - String specifying the password to be used for the request.
+ */
+function mxXmlRequest(url, params, method, async, username, password)
+{
+	this.url = url;
+	this.params = params;
+	this.method = method || 'POST';
+	this.async = (async != null) ? async : true;
+	this.username = username;
+	this.password = password;
+};
+
+/**
+ * Variable: url
+ * 
+ * Holds the target URL of the request.
+ */
+mxXmlRequest.prototype.url = null;
+
+/**
+ * Variable: params
+ * 
+ * Holds the form encoded data for the POST request.
+ */
+mxXmlRequest.prototype.params = null;
+
+/**
+ * Variable: method
+ * 
+ * Specifies the request method. Possible values are POST and GET. Default
+ * is POST.
+ */
+mxXmlRequest.prototype.method = null;
+
+/**
+ * Variable: async
+ * 
+ * Boolean indicating if the request is asynchronous.
+ */
+mxXmlRequest.prototype.async = null;
+
+/**
+ * Variable: binary
+ * 
+ * Boolean indicating if the request is binary. This option is ignored in IE.
+ * In all other browsers the requested mime type is set to
+ * text/plain; charset=x-user-defined. Default is false.
+ */
+mxXmlRequest.prototype.binary = false;
+
+/**
+ * Variable: withCredentials
+ * 
+ * Specifies if withCredentials should be used in HTML5-compliant browsers. Default is
+ * false.
+ */
+mxXmlRequest.prototype.withCredentials = false;
+
+/**
+ * Variable: username
+ * 
+ * Specifies the username to be used for authentication.
+ */
+mxXmlRequest.prototype.username = null;
+
+/**
+ * Variable: password
+ * 
+ * Specifies the password to be used for authentication.
+ */
+mxXmlRequest.prototype.password = null;
+
+/**
+ * Variable: request
+ * 
+ * Holds the inner, browser-specific request object.
+ */
+mxXmlRequest.prototype.request = null;
+
+/**
+ * Variable: decodeSimulateValues
+ * 
+ * Specifies if request values should be decoded as URIs before setting the
+ * textarea value in <simulate>. Defaults to false for backwards compatibility,
+ * to avoid another decode on the server this should be set to true.
+ */
+mxXmlRequest.prototype.decodeSimulateValues = false;
+
+/**
+ * Function: isBinary
+ * 
+ * Returns <binary>.
+ */
+mxXmlRequest.prototype.isBinary = function()
+{
+	return this.binary;
+};
+
+/**
+ * Function: setBinary
+ * 
+ * Sets <binary>.
+ */
+mxXmlRequest.prototype.setBinary = function(value)
+{
+	this.binary = value;
+};
+
+/**
+ * Function: getText
+ * 
+ * Returns the response as a string.
+ */
+mxXmlRequest.prototype.getText = function()
+{
+	return this.request.responseText;
+};
+
+/**
+ * Function: isReady
+ * 
+ * Returns true if the response is ready.
+ */
+mxXmlRequest.prototype.isReady = function()
+{
+	return this.request.readyState == 4;
+};
+
+/**
+ * Function: getDocumentElement
+ * 
+ * Returns the document element of the response XML document.
+ */
+mxXmlRequest.prototype.getDocumentElement = function()
+{
+	var doc = this.getXml();
+	
+	if (doc != null)
+	{
+		return doc.documentElement;
+	}
+	
+	return null;
+};
+
+/**
+ * Function: getXml
+ * 
+ * Returns the response as an XML document. Use <getDocumentElement> to get
+ * the document element of the XML document.
+ */
+mxXmlRequest.prototype.getXml = function()
+{
+	var xml = this.request.responseXML;
+	
+	// Handles missing response headers in IE, the first condition handles
+	// the case where responseXML is there, but using its nodes leads to
+	// type errors in the mxCellCodec when putting the nodes into a new
+	// document. This happens in IE9 standards mode and with XML user
+	// objects only, as they are used directly as values in cells.
+	if (document.documentMode >= 9 || xml == null || xml.documentElement == null)
+	{
+		xml = mxUtils.parseXml(this.request.responseText);
+	}
+	
+	return xml;
+};
+
+/**
+ * Function: getText
+ * 
+ * Returns the response as a string.
+ */
+mxXmlRequest.prototype.getText = function()
+{
+	return this.request.responseText;
+};
+
+/**
+ * Function: getStatus
+ * 
+ * Returns the status as a number, eg. 404 for "Not found" or 200 for "OK".
+ * Note: The NS_ERROR_NOT_AVAILABLE for invalid responses cannot be cought.
+ */
+mxXmlRequest.prototype.getStatus = function()
+{
+	return this.request.status;
+};
+
+/**
+ * Function: create
+ * 
+ * Creates and returns the inner <request> object.
+ */
+mxXmlRequest.prototype.create = function()
+{
+	if (window.XMLHttpRequest)
+	{
+		return function()
+		{
+			var req = new XMLHttpRequest();
+			
+			// TODO: Check for overrideMimeType required here?
+			if (this.isBinary() && req.overrideMimeType)
+			{
+				req.overrideMimeType('text/plain; charset=x-user-defined');
+			}
+
+			return req;
+		};
+	}
+	else if (typeof(ActiveXObject) != 'undefined')
+	{
+		return function()
+		{
+			// TODO: Implement binary option
+			return new ActiveXObject('Microsoft.XMLHTTP');
+		};
+	}
+}();
+
+/**
+ * Function: send
+ * 
+ * Send the <request> to the target URL using the specified functions to
+ * process the response asychronously.
+ * 
+ * Parameters:
+ * 
+ * onload - Function to be invoked if a successful response was received.
+ * onerror - Function to be called on any error.
+ * timeout - Optional timeout in ms before calling ontimeout.
+ * ontimeout - Optional function to execute on timeout.
+ */
+mxXmlRequest.prototype.send = function(onload, onerror, timeout, ontimeout)
+{
+	this.request = this.create();
+	
+	if (this.request != null)
+	{
+		if (onload != null)
+		{
+			this.request.onreadystatechange = mxUtils.bind(this, function()
+			{
+				if (this.isReady())
+				{
+					onload(this);
+					this.onreadystatechaange = null;
+				}
+			});
+		}
+
+		this.request.open(this.method, this.url, this.async,
+			this.username, this.password);
+		this.setRequestHeaders(this.request, this.params);
+		
+		if (window.XMLHttpRequest && this.withCredentials)
+		{
+			this.request.withCredentials = 'true';
+		}
+		
+		if (!mxClient.IS_QUIRKS && (document.documentMode == null || document.documentMode > 9) &&
+			window.XMLHttpRequest && timeout != null && ontimeout != null)
+		{
+			this.request.timeout = timeout;
+			this.request.ontimeout = ontimeout;
+		}
+				
+		this.request.send(this.params);
+	}
+};
+
+/**
+ * Function: setRequestHeaders
+ * 
+ * Sets the headers for the given request and parameters. This sets the
+ * content-type to application/x-www-form-urlencoded if any params exist.
+ * 
+ * Example:
+ * 
+ * (code)
+ * request.setRequestHeaders = function(request, params)
+ * {
+ *   if (params != null)
+ *   {
+ *     request.setRequestHeader('Content-Type',
+ *             'multipart/form-data');
+ *     request.setRequestHeader('Content-Length',
+ *             params.length);
+ *   }
+ * };
+ * (end)
+ * 
+ * Use the code above before calling <send> if you require a
+ * multipart/form-data request.   
+ */
+mxXmlRequest.prototype.setRequestHeaders = function(request, params)
+{
+	if (params != null)
+	{
+		request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+	}
+};
+
+/**
+ * Function: simulate
+ * 
+ * Creates and posts a request to the given target URL using a dynamically
+ * created form inside the given document.
+ * 
+ * Parameters:
+ * 
+ * docs - Document that contains the form element.
+ * target - Target to send the form result to.
+ */
+mxXmlRequest.prototype.simulate = function(doc, target)
+{
+	doc = doc || document;
+	var old = null;
+
+	if (doc == document)
+	{
+		old = window.onbeforeunload;		
+		window.onbeforeunload = null;
+	}
+			
+	var form = doc.createElement('form');
+	form.setAttribute('method', this.method);
+	form.setAttribute('action', this.url);
+
+	if (target != null)
+	{
+		form.setAttribute('target', target);
+	}
+
+	form.style.display = 'none';
+	form.style.visibility = 'hidden';
+	
+	var pars = (this.params.indexOf('&') > 0) ?
+		this.params.split('&') :
+		this.params.split();
+
+	// Adds the parameters as textareas to the form
+	for (var i=0; i<pars.length; i++)
+	{
+		var pos = pars[i].indexOf('=');
+		
+		if (pos > 0)
+		{
+			var name = pars[i].substring(0, pos);
+			var value = pars[i].substring(pos+1);
+			
+			if (this.decodeSimulateValues)
+			{
+				value = decodeURIComponent(value);
+			}
+			
+			var textarea = doc.createElement('textarea');
+			textarea.setAttribute('wrap', 'off');
+			textarea.setAttribute('name', name);
+			mxUtils.write(textarea, value);
+			form.appendChild(textarea);
+		}
+	}
+	
+	doc.body.appendChild(form);
+	form.submit();
+	
+	if (form.parentNode != null)
+	{
+		form.parentNode.removeChild(form);
+	}
+
+	if (old != null)
+	{		
+		window.onbeforeunload = old;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxClipboard =
+{
+	/**
+	 * Class: mxClipboard
+	 * 
+	 * Singleton that implements a clipboard for graph cells.
+	 *
+	 * Example:
+	 * 
+	 * (code)
+	 * mxClipboard.copy(graph);
+	 * mxClipboard.paste(graph2);
+	 * (end)
+	 *
+	 * This copies the selection cells from the graph to the clipboard and
+	 * pastes them into graph2.
+	 * 
+	 * For fine-grained control of the clipboard data the <mxGraph.canExportCell>
+	 * and <mxGraph.canImportCell> functions can be overridden.
+	 * 
+	 * To restore previous parents for pasted cells, the implementation for
+	 * <copy> and <paste> can be changed as follows.
+	 * 
+	 * (code)
+	 * mxClipboard.copy = function(graph, cells)
+	 * {
+	 *   cells = cells || graph.getSelectionCells();
+	 *   var result = graph.getExportableCells(cells);
+	 *   
+	 *   mxClipboard.parents = new Object();
+	 *   
+	 *   for (var i = 0; i < result.length; i++)
+	 *   {
+	 *     mxClipboard.parents[i] = graph.model.getParent(cells[i]);
+	 *   }
+	 *   
+	 *   mxClipboard.insertCount = 1;
+	 *   mxClipboard.setCells(graph.cloneCells(result));
+	 *   
+	 *   return result;
+	 * };
+	 * 
+	 * mxClipboard.paste = function(graph)
+	 * {
+	 *   if (!mxClipboard.isEmpty())
+	 *   {
+	 *     var cells = graph.getImportableCells(mxClipboard.getCells());
+	 *     var delta = mxClipboard.insertCount * mxClipboard.STEPSIZE;
+	 *     var parent = graph.getDefaultParent();
+	 *     
+	 *     graph.model.beginUpdate();
+	 *     try
+	 *     {
+	 *       for (var i = 0; i < cells.length; i++)
+	 *       {
+	 *         var tmp = (mxClipboard.parents != null && graph.model.contains(mxClipboard.parents[i])) ?
+	 *              mxClipboard.parents[i] : parent;
+	 *         cells[i] = graph.importCells([cells[i]], delta, delta, tmp)[0];
+	 *       }
+	 *     }
+	 *     finally
+	 *     {
+	 *       graph.model.endUpdate();
+	 *     }
+	 *     
+	 *     // Increments the counter and selects the inserted cells
+	 *     mxClipboard.insertCount++;
+	 *     graph.setSelectionCells(cells);
+	 *   }
+	 * };
+	 * (end)
+	 * 
+	 * Variable: STEPSIZE
+	 * 
+	 * Defines the step size to offset the cells after each paste operation.
+	 * Default is 10.
+	 */
+	STEPSIZE: 10,
+
+	/**
+	 * Variable: insertCount
+	 * 
+	 * Counts the number of times the clipboard data has been inserted.
+	 */
+	insertCount: 1,
+
+	/**
+	 * Variable: cells
+	 * 
+	 * Holds the array of <mxCells> currently in the clipboard.
+	 */
+	cells: null,
+
+	/**
+	 * Function: setCells
+	 * 
+	 * Sets the cells in the clipboard. Fires a <mxEvent.CHANGE> event.
+	 */
+	setCells: function(cells)
+	{
+		mxClipboard.cells = cells;
+	},
+
+	/**
+	 * Function: getCells
+	 * 
+	 * Returns  the cells in the clipboard.
+	 */
+	getCells: function()
+	{
+		return mxClipboard.cells;
+	},
+	
+	/**
+	 * Function: isEmpty
+	 * 
+	 * Returns true if the clipboard currently has not data stored.
+	 */
+	isEmpty: function()
+	{
+		return mxClipboard.getCells() == null;
+	},
+	
+	/**
+	 * Function: cut
+	 * 
+	 * Cuts the given array of <mxCells> from the specified graph.
+	 * If cells is null then the selection cells of the graph will
+	 * be used. Returns the cells that have been cut from the graph.
+	 *
+	 * Parameters:
+	 * 
+	 * graph - <mxGraph> that contains the cells to be cut.
+	 * cells - Optional array of <mxCells> to be cut.
+	 */
+	cut: function(graph, cells)
+	{
+		cells = mxClipboard.copy(graph, cells);
+		mxClipboard.insertCount = 0;
+		mxClipboard.removeCells(graph, cells);
+		
+		return cells;
+	},
+
+	/**
+	 * Function: removeCells
+	 * 
+	 * Hook to remove the given cells from the given graph after
+	 * a cut operation.
+	 *
+	 * Parameters:
+	 * 
+	 * graph - <mxGraph> that contains the cells to be cut.
+	 * cells - Array of <mxCells> to be cut.
+	 */
+	removeCells: function(graph, cells)
+	{
+		graph.removeCells(cells);
+	},
+
+	/**
+	 * Function: copy
+	 * 
+	 * Copies the given array of <mxCells> from the specified
+	 * graph to <cells>. Returns the original array of cells that has
+	 * been cloned. Descendants of cells in the array are ignored.
+	 * 
+	 * Parameters:
+	 * 
+	 * graph - <mxGraph> that contains the cells to be copied.
+	 * cells - Optional array of <mxCells> to be copied.
+	 */
+	copy: function(graph, cells)
+	{
+		cells = cells || graph.getSelectionCells();
+		var result = graph.getExportableCells(graph.model.getTopmostCells(cells));
+		mxClipboard.insertCount = 1;
+		mxClipboard.setCells(graph.cloneCells(result));
+
+		return result;
+	},
+
+	/**
+	 * Function: paste
+	 * 
+	 * Pastes the <cells> into the specified graph restoring
+	 * the relation to <parents>, if possible. If the parents
+	 * are no longer in the graph or invisible then the
+	 * cells are added to the graph's default or into the
+	 * swimlane under the cell's new location if one exists.
+	 * The cells are added to the graph using <mxGraph.importCells>
+	 * and returned.
+	 * 
+	 * Parameters:
+	 * 
+	 * graph - <mxGraph> to paste the <cells> into.
+	 */
+	paste: function(graph)
+	{
+		var cells = null;
+		
+		if (!mxClipboard.isEmpty())
+		{
+			cells = graph.getImportableCells(mxClipboard.getCells());
+			var delta = mxClipboard.insertCount * mxClipboard.STEPSIZE;
+			var parent = graph.getDefaultParent();
+			cells = graph.importCells(cells, delta, delta, parent);
+			
+			// Increments the counter and selects the inserted cells
+			mxClipboard.insertCount++;
+			graph.setSelectionCells(cells);
+		}
+		
+		return cells;
+	}
+
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxWindow
+ * 
+ * Basic window inside a document.
+ * 
+ * Examples:
+ * 
+ * Creating a simple window.
+ *
+ * (code)
+ * var tb = document.createElement('div');
+ * var wnd = new mxWindow('Title', tb, 100, 100, 200, 200, true, true);
+ * wnd.setVisible(true); 
+ * (end)
+ *
+ * Creating a window that contains an iframe. 
+ * 
+ * (code)
+ * var frame = document.createElement('iframe');
+ * frame.setAttribute('width', '192px');
+ * frame.setAttribute('height', '172px');
+ * frame.setAttribute('src', 'http://www.example.com/');
+ * frame.style.backgroundColor = 'white';
+ * 
+ * var w = document.body.clientWidth;
+ * var h = (document.body.clientHeight || document.documentElement.clientHeight);
+ * var wnd = new mxWindow('Title', frame, (w-200)/2, (h-200)/3, 200, 200);
+ * wnd.setVisible(true);
+ * (end)
+ * 
+ * To limit the movement of a window, eg. to keep it from being moved beyond
+ * the top, left corner the following method can be overridden (recommended):
+ * 
+ * (code)
+ * wnd.setLocation = function(x, y)
+ * {
+ *   x = Math.max(0, x);
+ *   y = Math.max(0, y);
+ *   mxWindow.prototype.setLocation.apply(this, arguments);
+ * };
+ * (end)
+ * 
+ * Or the following event handler can be used:
+ * 
+ * (code)
+ * wnd.addListener(mxEvent.MOVE, function(e)
+ * {
+ *   wnd.setLocation(Math.max(0, wnd.getX()), Math.max(0, wnd.getY()));
+ * });
+ * (end)
+ * 
+ * To keep a window inside the current window:
+ * 
+ * (code)
+ * mxEvent.addListener(window, 'resize', mxUtils.bind(this, function()
+ * {
+ *   var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
+ *   var ih = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
+ *   
+ *   var x = this.window.getX();
+ *   var y = this.window.getY();
+ *   
+ *   if (x + this.window.table.clientWidth > iw)
+ *   {
+ *     x = Math.max(0, iw - this.window.table.clientWidth);
+ *   }
+ *   
+ *   if (y + this.window.table.clientHeight > ih)
+ *   {
+ *     y = Math.max(0, ih - this.window.table.clientHeight);
+ *   }
+ *   
+ *   if (this.window.getX() != x || this.window.getY() != y)
+ *   {
+ *     this.window.setLocation(x, y);
+ *   }
+ * }));
+ * (end)
+ *
+ * Event: mxEvent.MOVE_START
+ *
+ * Fires before the window is moved. The <code>event</code> property contains
+ * the corresponding mouse event.
+ *
+ * Event: mxEvent.MOVE
+ *
+ * Fires while the window is being moved. The <code>event</code> property
+ * contains the corresponding mouse event.
+ *
+ * Event: mxEvent.MOVE_END
+ *
+ * Fires after the window is moved. The <code>event</code> property contains
+ * the corresponding mouse event.
+ *
+ * Event: mxEvent.RESIZE_START
+ *
+ * Fires before the window is resized. The <code>event</code> property contains
+ * the corresponding mouse event.
+ *
+ * Event: mxEvent.RESIZE
+ *
+ * Fires while the window is being resized. The <code>event</code> property
+ * contains the corresponding mouse event.
+ *
+ * Event: mxEvent.RESIZE_END
+ *
+ * Fires after the window is resized. The <code>event</code> property contains
+ * the corresponding mouse event.
+ *
+ * Event: mxEvent.MAXIMIZE
+ * 
+ * Fires after the window is maximized. The <code>event</code> property
+ * contains the corresponding mouse event.
+ * 
+ * Event: mxEvent.MINIMIZE
+ * 
+ * Fires after the window is minimized. The <code>event</code> property
+ * contains the corresponding mouse event.
+ * 
+ * Event: mxEvent.NORMALIZE
+ * 
+ * Fires after the window is normalized, that is, it returned from
+ * maximized or minimized state. The <code>event</code> property contains the
+ * corresponding mouse event.
+ *  
+ * Event: mxEvent.ACTIVATE
+ * 
+ * Fires after a window is activated. The <code>previousWindow</code> property
+ * contains the previous window. The event sender is the active window.
+ * 
+ * Event: mxEvent.SHOW
+ * 
+ * Fires after the window is shown. This event has no properties.
+ * 
+ * Event: mxEvent.HIDE
+ * 
+ * Fires after the window is hidden. This event has no properties.
+ * 
+ * Event: mxEvent.CLOSE
+ * 
+ * Fires before the window is closed. The <code>event</code> property contains
+ * the corresponding mouse event.
+ * 
+ * Event: mxEvent.DESTROY
+ * 
+ * Fires before the window is destroyed. This event has no properties.
+ * 
+ * Constructor: mxWindow
+ * 
+ * Constructs a new window with the given dimension and title to display
+ * the specified content. The window elements use the given style as a
+ * prefix for the classnames of the respective window elements, namely,
+ * the window title and window pane. The respective postfixes are appended
+ * to the given stylename as follows:
+ * 
+ *   style - Base style for the window.
+ *   style+Title - Style for the window title.
+ *   style+Pane - Style for the window pane.
+ * 
+ * The default value for style is mxWindow, resulting in the following
+ * classnames for the window elements: mxWindow, mxWindowTitle and
+ * mxWindowPane.
+ * 
+ * If replaceNode is given then the window replaces the given DOM node in
+ * the document.
+ * 
+ * Parameters:
+ * 
+ * title - String that represents the title of the new window.
+ * content - DOM node that is used as the window content.
+ * x - X-coordinate of the window location.
+ * y - Y-coordinate of the window location.
+ * width - Width of the window.
+ * height - Optional height of the window. Default is to match the height
+ * of the content at the specified width.
+ * minimizable - Optional boolean indicating if the window is minimizable.
+ * Default is true.
+ * movable - Optional boolean indicating if the window is movable. Default
+ * is true.
+ * replaceNode - Optional DOM node that the window should replace.
+ * style - Optional base classname for the window elements. Default is
+ * mxWindow.
+ */
+function mxWindow(title, content, x, y, width, height, minimizable, movable, replaceNode, style)
+{
+	if (content != null)
+	{
+		minimizable = (minimizable != null) ? minimizable : true;
+		this.content = content;
+		this.init(x, y, width, height, style);
+		
+		this.installMaximizeHandler();
+		this.installMinimizeHandler();
+		this.installCloseHandler();
+		this.setMinimizable(minimizable);
+		this.setTitle(title);
+		
+		if (movable == null || movable)
+		{
+			this.installMoveHandler();
+		}
+
+		if (replaceNode != null && replaceNode.parentNode != null)
+		{
+			replaceNode.parentNode.replaceChild(this.div, replaceNode);
+		}
+		else
+		{
+			document.body.appendChild(this.div);
+		}
+	}
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxWindow.prototype = new mxEventSource();
+mxWindow.prototype.constructor = mxWindow;
+
+/**
+ * Variable: closeImage
+ * 
+ * URL of the image to be used for the close icon in the titlebar.
+ */
+mxWindow.prototype.closeImage = mxClient.imageBasePath + '/close.gif';
+
+/**
+ * Variable: minimizeImage
+ * 
+ * URL of the image to be used for the minimize icon in the titlebar.
+ */
+mxWindow.prototype.minimizeImage = mxClient.imageBasePath + '/minimize.gif';
+	
+/**
+ * Variable: normalizeImage
+ * 
+ * URL of the image to be used for the normalize icon in the titlebar.
+ */
+mxWindow.prototype.normalizeImage = mxClient.imageBasePath + '/normalize.gif';
+	
+/**
+ * Variable: maximizeImage
+ * 
+ * URL of the image to be used for the maximize icon in the titlebar.
+ */
+mxWindow.prototype.maximizeImage = mxClient.imageBasePath + '/maximize.gif';
+
+/**
+ * Variable: normalizeImage
+ * 
+ * URL of the image to be used for the resize icon.
+ */
+mxWindow.prototype.resizeImage = mxClient.imageBasePath + '/resize.gif';
+
+/**
+ * Variable: visible
+ * 
+ * Boolean flag that represents the visible state of the window.
+ */
+mxWindow.prototype.visible = false;
+
+/**
+ * Variable: minimumSize
+ * 
+ * <mxRectangle> that specifies the minimum width and height of the window.
+ * Default is (50, 40).
+ */
+mxWindow.prototype.minimumSize = new mxRectangle(0, 0, 50, 40);
+
+/**
+ * Variable: destroyOnClose
+ * 
+ * Specifies if the window should be destroyed when it is closed. If this
+ * is false then the window is hidden using <setVisible>. Default is true.
+ */
+mxWindow.prototype.destroyOnClose = true;
+
+/**
+ * Variable: contentHeightCorrection
+ * 
+ * Defines the correction factor for computing the height of the contentWrapper.
+ * Default is 6 for IE 7/8 standards mode and 2 for all other browsers and modes.
+ */
+mxWindow.prototype.contentHeightCorrection = (document.documentMode == 8 || document.documentMode == 7) ? 6 : 2;
+
+/**
+ * Variable: title
+ * 
+ * Reference to the DOM node (TD) that contains the title.
+ */
+mxWindow.prototype.title = null;
+
+/**
+ * Variable: content
+ * 
+ * Reference to the DOM node that represents the window content.
+ */
+mxWindow.prototype.content = null;
+
+/**
+ * Function: init
+ * 
+ * Initializes the DOM tree that represents the window.
+ */
+mxWindow.prototype.init = function(x, y, width, height, style)
+{
+	style = (style != null) ? style : 'mxWindow';
+	
+	this.div = document.createElement('div');
+	this.div.className = style;
+
+	this.div.style.left = x + 'px';
+	this.div.style.top = y + 'px';
+	this.table = document.createElement('table');
+	this.table.className = style;
+
+	// Disables built-in pan and zoom in IE10 and later
+	if (mxClient.IS_POINTER)
+	{
+		this.div.style.touchAction = 'none';
+	}
+	
+	// Workaround for table size problems in FF
+	if (width != null)
+	{
+		if (!mxClient.IS_QUIRKS)
+		{
+			this.div.style.width = width + 'px'; 
+		}
+		
+		this.table.style.width = width + 'px';
+	} 
+	
+	if (height != null)
+	{
+		if (!mxClient.IS_QUIRKS)
+		{
+			this.div.style.height = height + 'px';
+		}
+		
+		this.table.style.height = height + 'px';
+	}		
+	
+	// Creates title row
+	var tbody = document.createElement('tbody');
+	var tr = document.createElement('tr');
+	
+	this.title = document.createElement('td');
+	this.title.className = style + 'Title';
+	
+	this.buttons = document.createElement('div');
+	this.buttons.style.position = 'absolute';
+	this.buttons.style.display = 'inline-block';
+	this.buttons.style.right = '4px';
+	this.buttons.style.top = '5px';
+	this.title.appendChild(this.buttons);
+	
+	tr.appendChild(this.title);
+	tbody.appendChild(tr);
+	
+	// Creates content row and table cell
+	tr = document.createElement('tr');
+	this.td = document.createElement('td');
+	this.td.className = style + 'Pane';
+	
+	if (document.documentMode == 7)
+	{
+		this.td.style.height = '100%';
+	}
+
+	this.contentWrapper = document.createElement('div');
+	this.contentWrapper.className = style + 'Pane';
+	this.contentWrapper.style.width = '100%';
+	this.contentWrapper.appendChild(this.content);
+
+	// Workaround for div around div restricts height
+	// of inner div if outerdiv has hidden overflow
+	if (mxClient.IS_QUIRKS || this.content.nodeName.toUpperCase() != 'DIV')
+	{
+		this.contentWrapper.style.height = '100%';
+	}
+
+	// Puts all content into the DOM
+	this.td.appendChild(this.contentWrapper);
+	tr.appendChild(this.td);
+	tbody.appendChild(tr);
+	this.table.appendChild(tbody);
+	this.div.appendChild(this.table);
+	
+	// Puts the window on top of other windows when clicked
+	var activator = mxUtils.bind(this, function(evt)
+	{
+		this.activate();
+	});
+	
+	mxEvent.addGestureListeners(this.title, activator);
+	mxEvent.addGestureListeners(this.table, activator);
+
+	this.hide();
+};
+
+/**
+ * Function: setTitle
+ * 
+ * Sets the window title to the given string. HTML markup inside the title
+ * will be escaped.
+ */
+mxWindow.prototype.setTitle = function(title)
+{
+	// Removes all text content nodes (normally just one)
+	var child = this.title.firstChild;
+	
+	while (child != null)
+	{
+		var next = child.nextSibling;
+		
+		if (child.nodeType == mxConstants.NODETYPE_TEXT)
+		{
+			child.parentNode.removeChild(child);
+		}
+		
+		child = next;
+	}
+	
+	mxUtils.write(this.title, title || '');
+	this.title.appendChild(this.buttons);
+};
+
+/**
+ * Function: setScrollable
+ * 
+ * Sets if the window contents should be scrollable.
+ */
+mxWindow.prototype.setScrollable = function(scrollable)
+{
+	// Workaround for hang in Presto 2.5.22 (Opera 10.5)
+	if (navigator.userAgent.indexOf('Presto/2.5') < 0)
+	{
+		if (scrollable)
+		{
+			this.contentWrapper.style.overflow = 'auto';
+		}
+		else
+		{
+			this.contentWrapper.style.overflow = 'hidden';
+		}
+	}
+};
+
+/**
+ * Function: activate
+ * 
+ * Puts the window on top of all other windows.
+ */
+mxWindow.prototype.activate = function()
+{
+	if (mxWindow.activeWindow != this)
+	{
+		var style = mxUtils.getCurrentStyle(this.getElement());
+		var index = (style != null) ? style.zIndex : 3;
+
+		if (mxWindow.activeWindow)
+		{
+			var elt = mxWindow.activeWindow.getElement();
+			
+			if (elt != null && elt.style != null)
+			{
+				elt.style.zIndex = index;
+			}
+		}
+		
+		var previousWindow = mxWindow.activeWindow;
+		this.getElement().style.zIndex = parseInt(index) + 1;
+		mxWindow.activeWindow = this;
+		
+		this.fireEvent(new mxEventObject(mxEvent.ACTIVATE, 'previousWindow', previousWindow));
+	}
+};
+
+/**
+ * Function: getElement
+ * 
+ * Returuns the outermost DOM node that makes up the window.
+ */
+mxWindow.prototype.getElement = function()
+{
+	return this.div;
+};
+
+/**
+ * Function: fit
+ * 
+ * Makes sure the window is inside the client area of the window.
+ */
+mxWindow.prototype.fit = function()
+{
+	mxUtils.fit(this.div);
+};
+
+/**
+ * Function: isResizable
+ * 
+ * Returns true if the window is resizable.
+ */
+mxWindow.prototype.isResizable = function()
+{
+	if (this.resize != null)
+	{
+		return this.resize.style.display != 'none';
+	}
+	
+	return false;
+};
+
+/**
+ * Function: setResizable
+ * 
+ * Sets if the window should be resizable. To avoid interference with some
+ * built-in features of IE10 and later, the use of the following code is
+ * recommended if there are resizable <mxWindow>s in the page:
+ * 
+ * (code)
+ * if (mxClient.IS_POINTER)
+ * {
+ *   document.body.style.msTouchAction = 'none';
+ * }
+ * (end)
+ */
+mxWindow.prototype.setResizable = function(resizable)
+{
+	if (resizable)
+	{
+		if (this.resize == null)
+		{
+			this.resize = document.createElement('img');
+			this.resize.style.position = 'absolute';
+			this.resize.style.bottom = '2px';
+			this.resize.style.right = '2px';
+
+			this.resize.setAttribute('src', mxClient.imageBasePath + '/resize.gif');
+			this.resize.style.cursor = 'nw-resize';
+			
+			var startX = null;
+			var startY = null;
+			var width = null;
+			var height = null;
+			
+			var start = mxUtils.bind(this, function(evt)
+			{
+				// LATER: pointerdown starting on border of resize does start
+				// the drag operation but does not fire consecutive events via
+				// one of the listeners below (does pan instead).
+				// Workaround: document.body.style.msTouchAction = 'none'
+				this.activate();
+				startX = mxEvent.getClientX(evt);
+				startY = mxEvent.getClientY(evt);
+				width = this.div.offsetWidth;
+				height = this.div.offsetHeight;
+				
+				mxEvent.addGestureListeners(document, null, dragHandler, dropHandler);
+				this.fireEvent(new mxEventObject(mxEvent.RESIZE_START, 'event', evt));
+				mxEvent.consume(evt);
+			});
+
+			// Adds a temporary pair of listeners to intercept
+			// the gesture event in the document
+			var dragHandler = mxUtils.bind(this, function(evt)
+			{
+				if (startX != null && startY != null)
+				{
+					var dx = mxEvent.getClientX(evt) - startX;
+					var dy = mxEvent.getClientY(evt) - startY;
+	
+					this.setSize(width + dx, height + dy);
+	
+					this.fireEvent(new mxEventObject(mxEvent.RESIZE, 'event', evt));
+					mxEvent.consume(evt);
+				}
+			});
+			
+			var dropHandler = mxUtils.bind(this, function(evt)
+			{
+				if (startX != null && startY != null)
+				{
+					startX = null;
+					startY = null;
+					mxEvent.removeGestureListeners(document, null, dragHandler, dropHandler);
+					this.fireEvent(new mxEventObject(mxEvent.RESIZE_END, 'event', evt));
+					mxEvent.consume(evt);
+				}
+			});
+			
+			mxEvent.addGestureListeners(this.resize, start, dragHandler, dropHandler);
+			this.div.appendChild(this.resize);
+		}
+		else 
+		{
+			this.resize.style.display = 'inline';
+		}
+	}
+	else if (this.resize != null)
+	{
+		this.resize.style.display = 'none';
+	}
+};
+	
+/**
+ * Function: setSize
+ * 
+ * Sets the size of the window.
+ */
+mxWindow.prototype.setSize = function(width, height)
+{
+	width = Math.max(this.minimumSize.width, width);
+	height = Math.max(this.minimumSize.height, height);
+
+	// Workaround for table size problems in FF
+	if (!mxClient.IS_QUIRKS)
+	{
+		this.div.style.width =  width + 'px';
+		this.div.style.height = height + 'px';
+	}
+	
+	this.table.style.width =  width + 'px';
+	this.table.style.height = height + 'px';
+
+	if (!mxClient.IS_QUIRKS)
+	{
+		this.contentWrapper.style.height = (this.div.offsetHeight -
+			this.title.offsetHeight - this.contentHeightCorrection) + 'px';
+	}
+};
+	
+/**
+ * Function: setMinimizable
+ * 
+ * Sets if the window is minimizable.
+ */
+mxWindow.prototype.setMinimizable = function(minimizable)
+{
+	this.minimize.style.display = (minimizable) ? '' : 'none';
+};
+
+/**
+ * Function: getMinimumSize
+ * 
+ * Returns an <mxRectangle> that specifies the size for the minimized window.
+ * A width or height of 0 means keep the existing width or height. This
+ * implementation returns the height of the window title and keeps the width.
+ */
+mxWindow.prototype.getMinimumSize = function()
+{
+	return new mxRectangle(0, 0, 0, this.title.offsetHeight);
+};
+
+/**
+ * Function: installMinimizeHandler
+ * 
+ * Installs the event listeners required for minimizing the window.
+ */
+mxWindow.prototype.installMinimizeHandler = function()
+{
+	this.minimize = document.createElement('img');
+	
+	this.minimize.setAttribute('src', this.minimizeImage);
+	this.minimize.setAttribute('title', 'Minimize');
+	this.minimize.style.cursor = 'pointer';
+	this.minimize.style.marginLeft = '2px';
+	this.minimize.style.display = 'none';
+	
+	this.buttons.appendChild(this.minimize);
+	
+	var minimized = false;
+	var maxDisplay = null;
+	var height = null;
+
+	var funct = mxUtils.bind(this, function(evt)
+	{
+		this.activate();
+		
+		if (!minimized)
+		{
+			minimized = true;
+			
+			this.minimize.setAttribute('src', this.normalizeImage);
+			this.minimize.setAttribute('title', 'Normalize');
+			this.contentWrapper.style.display = 'none';
+			maxDisplay = this.maximize.style.display;
+			
+			this.maximize.style.display = 'none';
+			height = this.table.style.height;
+			
+			var minSize = this.getMinimumSize();
+			
+			if (minSize.height > 0)
+			{
+				if (!mxClient.IS_QUIRKS)
+				{
+					this.div.style.height = minSize.height + 'px';
+				}
+				
+				this.table.style.height = minSize.height + 'px';
+			}
+			
+			if (minSize.width > 0)
+			{
+				if (!mxClient.IS_QUIRKS)
+				{
+					this.div.style.width = minSize.width + 'px';
+				}
+				
+				this.table.style.width = minSize.width + 'px';
+			}
+			
+			if (this.resize != null)
+			{
+				this.resize.style.visibility = 'hidden';
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.MINIMIZE, 'event', evt));
+		}
+		else
+		{
+			minimized = false;
+			
+			this.minimize.setAttribute('src', this.minimizeImage);
+			this.minimize.setAttribute('title', 'Minimize');
+			this.contentWrapper.style.display = ''; // default
+			this.maximize.style.display = maxDisplay;
+			
+			if (!mxClient.IS_QUIRKS)
+			{
+				this.div.style.height = height;
+			}
+			
+			this.table.style.height = height;
+
+			if (this.resize != null)
+			{
+				this.resize.style.visibility = '';
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.NORMALIZE, 'event', evt));
+		}
+		
+		mxEvent.consume(evt);
+	});
+	
+	mxEvent.addGestureListeners(this.minimize, funct);
+};
+	
+/**
+ * Function: setMaximizable
+ * 
+ * Sets if the window is maximizable.
+ */
+mxWindow.prototype.setMaximizable = function(maximizable)
+{
+	this.maximize.style.display = (maximizable) ? '' : 'none';
+};
+
+/**
+ * Function: installMaximizeHandler
+ * 
+ * Installs the event listeners required for maximizing the window.
+ */
+mxWindow.prototype.installMaximizeHandler = function()
+{
+	this.maximize = document.createElement('img');
+	
+	this.maximize.setAttribute('src', this.maximizeImage);
+	this.maximize.setAttribute('title', 'Maximize');
+	this.maximize.style.cursor = 'default';
+	this.maximize.style.marginLeft = '2px';
+	this.maximize.style.cursor = 'pointer';
+	this.maximize.style.display = 'none';
+	
+	this.buttons.appendChild(this.maximize);
+	
+	var maximized = false;
+	var x = null;
+	var y = null;
+	var height = null;
+	var width = null;
+	var minDisplay = null;
+
+	var funct = mxUtils.bind(this, function(evt)
+	{
+		this.activate();
+		
+		if (this.maximize.style.display != 'none')
+		{
+			if (!maximized)
+			{
+				maximized = true;
+				
+				this.maximize.setAttribute('src', this.normalizeImage);
+				this.maximize.setAttribute('title', 'Normalize');
+				this.contentWrapper.style.display = '';
+				minDisplay = this.minimize.style.display;
+				this.minimize.style.display = 'none';
+				
+				// Saves window state
+				x = parseInt(this.div.style.left);
+				y = parseInt(this.div.style.top);
+				height = this.table.style.height;
+				width = this.table.style.width;
+
+				this.div.style.left = '0px';
+				this.div.style.top = '0px';
+				var docHeight = Math.max(document.body.clientHeight || 0, document.documentElement.clientHeight || 0);
+
+				if (!mxClient.IS_QUIRKS)
+				{
+					this.div.style.width = (document.body.clientWidth - 2) + 'px';
+					this.div.style.height = (docHeight - 2) + 'px';
+				}
+
+				this.table.style.width = (document.body.clientWidth - 2) + 'px';
+				this.table.style.height = (docHeight - 2) + 'px';
+				
+				if (this.resize != null)
+				{
+					this.resize.style.visibility = 'hidden';
+				}
+
+				if (!mxClient.IS_QUIRKS)
+				{
+					var style = mxUtils.getCurrentStyle(this.contentWrapper);
+		
+					if (style.overflow == 'auto' || this.resize != null)
+					{
+						this.contentWrapper.style.height = (this.div.offsetHeight -
+							this.title.offsetHeight - this.contentHeightCorrection) + 'px';
+					}
+				}
+
+				this.fireEvent(new mxEventObject(mxEvent.MAXIMIZE, 'event', evt));
+			}
+			else
+			{
+				maximized = false;
+				
+				this.maximize.setAttribute('src', this.maximizeImage);
+				this.maximize.setAttribute('title', 'Maximize');
+				this.contentWrapper.style.display = '';
+				this.minimize.style.display = minDisplay;
+
+				// Restores window state
+				this.div.style.left = x+'px';
+				this.div.style.top = y+'px';
+				
+				if (!mxClient.IS_QUIRKS)
+				{
+					this.div.style.height = height;
+					this.div.style.width = width;
+
+					var style = mxUtils.getCurrentStyle(this.contentWrapper);
+		
+					if (style.overflow == 'auto' || this.resize != null)
+					{
+						this.contentWrapper.style.height = (this.div.offsetHeight -
+							this.title.offsetHeight - this.contentHeightCorrection) + 'px';
+					}
+				}
+				
+				this.table.style.height = height;
+				this.table.style.width = width;
+
+				if (this.resize != null)
+				{
+					this.resize.style.visibility = '';
+				}
+				
+				this.fireEvent(new mxEventObject(mxEvent.NORMALIZE, 'event', evt));
+			}
+			
+			mxEvent.consume(evt);
+		}
+	});
+	
+	mxEvent.addGestureListeners(this.maximize, funct);
+	mxEvent.addListener(this.title, 'dblclick', funct);
+};
+	
+/**
+ * Function: installMoveHandler
+ * 
+ * Installs the event listeners required for moving the window.
+ */
+mxWindow.prototype.installMoveHandler = function()
+{
+	this.title.style.cursor = 'move';
+	
+	mxEvent.addGestureListeners(this.title,
+		mxUtils.bind(this, function(evt)
+		{
+			var startX = mxEvent.getClientX(evt);
+			var startY = mxEvent.getClientY(evt);
+			var x = this.getX();
+			var y = this.getY();
+						
+			// Adds a temporary pair of listeners to intercept
+			// the gesture event in the document
+			var dragHandler = mxUtils.bind(this, function(evt)
+			{
+				var dx = mxEvent.getClientX(evt) - startX;
+				var dy = mxEvent.getClientY(evt) - startY;
+				this.setLocation(x + dx, y + dy);
+				this.fireEvent(new mxEventObject(mxEvent.MOVE, 'event', evt));
+				mxEvent.consume(evt);
+			});
+			
+			var dropHandler = mxUtils.bind(this, function(evt)
+			{
+				mxEvent.removeGestureListeners(document, null, dragHandler, dropHandler);
+				this.fireEvent(new mxEventObject(mxEvent.MOVE_END, 'event', evt));
+				mxEvent.consume(evt);
+			});
+			
+			mxEvent.addGestureListeners(document, null, dragHandler, dropHandler);
+			this.fireEvent(new mxEventObject(mxEvent.MOVE_START, 'event', evt));
+			mxEvent.consume(evt);
+		}));
+	
+	// Disables built-in pan and zoom in IE10 and later
+	if (mxClient.IS_POINTER)
+	{
+		this.title.style.touchAction = 'none';
+	}
+};
+
+/**
+ * Function: setLocation
+ * 
+ * Sets the upper, left corner of the window.
+ */
+ mxWindow.prototype.setLocation = function(x, y)
+ {
+	this.div.style.left = x + 'px';
+	this.div.style.top = y + 'px';
+ };
+
+/**
+ * Function: getX
+ *
+ * Returns the current position on the x-axis.
+ */
+mxWindow.prototype.getX = function()
+{
+	return parseInt(this.div.style.left);
+};
+
+/**
+ * Function: getY
+ *
+ * Returns the current position on the y-axis.
+ */
+mxWindow.prototype.getY = function()
+{
+	return parseInt(this.div.style.top);
+};
+
+/**
+ * Function: installCloseHandler
+ *
+ * Adds the <closeImage> as a new image node in <closeImg> and installs the
+ * <close> event.
+ */
+mxWindow.prototype.installCloseHandler = function()
+{
+	this.closeImg = document.createElement('img');
+	
+	this.closeImg.setAttribute('src', this.closeImage);
+	this.closeImg.setAttribute('title', 'Close');
+	this.closeImg.style.marginLeft = '2px';
+	this.closeImg.style.cursor = 'pointer';
+	this.closeImg.style.display = 'none';
+	
+	this.buttons.appendChild(this.closeImg);
+
+	mxEvent.addGestureListeners(this.closeImg,
+		mxUtils.bind(this, function(evt)
+		{
+			this.fireEvent(new mxEventObject(mxEvent.CLOSE, 'event', evt));
+			
+			if (this.destroyOnClose)
+			{
+				this.destroy();
+			}
+			else
+			{
+				this.setVisible(false);
+			}
+			
+			mxEvent.consume(evt);
+		}));
+};
+
+/**
+ * Function: setImage
+ * 
+ * Sets the image associated with the window.
+ * 
+ * Parameters:
+ * 
+ * image - URL of the image to be used.
+ */
+mxWindow.prototype.setImage = function(image)
+{
+	this.image = document.createElement('img');
+	this.image.setAttribute('src', image);
+	this.image.setAttribute('align', 'left');
+	this.image.style.marginRight = '4px';
+	this.image.style.marginLeft = '0px';
+	this.image.style.marginTop = '-2px';
+	
+	this.title.insertBefore(this.image, this.title.firstChild);
+};
+
+/**
+ * Function: setClosable
+ * 
+ * Sets the image associated with the window.
+ * 
+ * Parameters:
+ * 
+ * closable - Boolean specifying if the window should be closable.
+ */
+mxWindow.prototype.setClosable = function(closable)
+{
+	this.closeImg.style.display = (closable) ? '' : 'none';
+};
+
+/**
+ * Function: isVisible
+ * 
+ * Returns true if the window is visible.
+ */
+mxWindow.prototype.isVisible = function()
+{
+	if (this.div != null)
+	{
+		return this.div.style.display != 'none';
+	}
+	
+	return false;
+};
+
+/**
+ * Function: setVisible
+ *
+ * Shows or hides the window depending on the given flag.
+ * 
+ * Parameters:
+ * 
+ * visible - Boolean indicating if the window should be made visible.
+ */
+mxWindow.prototype.setVisible = function(visible)
+{
+	if (this.div != null && this.isVisible() != visible)
+	{
+		if (visible)
+		{
+			this.show();
+		}
+		else
+		{
+			this.hide();
+		}
+	}
+};
+
+/**
+ * Function: show
+ *
+ * Shows the window.
+ */
+mxWindow.prototype.show = function()
+{
+	this.div.style.display = '';
+	this.activate();
+	
+	var style = mxUtils.getCurrentStyle(this.contentWrapper);
+	
+	if (!mxClient.IS_QUIRKS && (style.overflow == 'auto' || this.resize != null))
+	{
+		this.contentWrapper.style.height = (this.div.offsetHeight -
+				this.title.offsetHeight - this.contentHeightCorrection) + 'px';
+	}
+	
+	this.fireEvent(new mxEventObject(mxEvent.SHOW));
+};
+
+/**
+ * Function: hide
+ *
+ * Hides the window.
+ */
+mxWindow.prototype.hide = function()
+{
+	this.div.style.display = 'none';
+	this.fireEvent(new mxEventObject(mxEvent.HIDE));
+};
+
+/**
+ * Function: destroy
+ *
+ * Destroys the window and removes all associated resources. Fires a
+ * <destroy> event prior to destroying the window.
+ */
+mxWindow.prototype.destroy = function()
+{
+	this.fireEvent(new mxEventObject(mxEvent.DESTROY));
+	
+	if (this.div != null)
+	{
+		mxEvent.release(this.div);
+		this.div.parentNode.removeChild(this.div);
+		this.div = null;
+	}
+	
+	this.title = null;
+	this.content = null;
+	this.contentWrapper = null;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxForm
+ * 
+ * A simple class for creating HTML forms.
+ * 
+ * Constructor: mxForm
+ * 
+ * Creates a HTML table using the specified classname.
+ */
+function mxForm(className)
+{
+	this.table = document.createElement('table');
+	this.table.className = className;
+	this.body = document.createElement('tbody');
+	
+	this.table.appendChild(this.body);
+};
+
+/**
+ * Variable: table
+ * 
+ * Holds the DOM node that represents the table.
+ */
+mxForm.prototype.table = null;
+
+/**
+ * Variable: body
+ * 
+ * Holds the DOM node that represents the tbody (table body). New rows
+ * can be added to this object using DOM API.
+ */
+mxForm.prototype.body = false;
+
+/**
+ * Function: getTable
+ * 
+ * Returns the table that contains this form.
+ */
+mxForm.prototype.getTable = function()
+{
+	return this.table;
+};
+
+/**
+ * Function: addButtons
+ * 
+ * Helper method to add an OK and Cancel button using the respective
+ * functions.
+ */
+mxForm.prototype.addButtons = function(okFunct, cancelFunct)
+{
+	var tr = document.createElement('tr');
+	var td = document.createElement('td');
+	tr.appendChild(td);
+	td = document.createElement('td');
+
+	// Adds the ok button
+	var button = document.createElement('button');
+	mxUtils.write(button, mxResources.get('ok') || 'OK');
+	td.appendChild(button);
+
+	mxEvent.addListener(button, 'click', function()
+	{
+		okFunct();
+	});
+	
+	// Adds the cancel button
+	button = document.createElement('button');
+	mxUtils.write(button, mxResources.get('cancel') || 'Cancel');
+	td.appendChild(button);
+	
+	mxEvent.addListener(button, 'click', function()
+	{
+		cancelFunct();
+	});
+	
+	tr.appendChild(td);
+	this.body.appendChild(tr);
+};
+
+/**
+ * Function: addText
+ * 
+ * Adds an input for the given name, type and value and returns it.
+ */
+mxForm.prototype.addText = function(name, value, type)
+{
+	var input = document.createElement('input');
+	
+	input.setAttribute('type', type || 'text');
+	input.value = value;
+	
+	return this.addField(name, input);
+};
+
+/**
+ * Function: addCheckbox
+ * 
+ * Adds a checkbox for the given name and value and returns the textfield.
+ */
+mxForm.prototype.addCheckbox = function(name, value)
+{
+	var input = document.createElement('input');
+	
+	input.setAttribute('type', 'checkbox');
+	this.addField(name, input);
+
+	// IE can only change the checked value if the input is inside the DOM
+	if (value)
+	{
+		input.checked = true;
+	}
+
+	return input;
+};
+
+/**
+ * Function: addTextarea
+ * 
+ * Adds a textarea for the given name and value and returns the textarea.
+ */
+mxForm.prototype.addTextarea = function(name, value, rows)
+{
+	var input = document.createElement('textarea');
+	
+	if (mxClient.IS_NS)
+	{
+		rows--;
+	}
+	
+	input.setAttribute('rows', rows || 2);
+	input.value = value;
+	
+	return this.addField(name, input);
+};
+
+/**
+ * Function: addCombo
+ * 
+ * Adds a combo for the given name and returns the combo.
+ */
+mxForm.prototype.addCombo = function(name, isMultiSelect, size)
+{
+	var select = document.createElement('select');
+	
+	if (size != null)
+	{
+		select.setAttribute('size', size);
+	}
+	
+	if (isMultiSelect)
+	{
+		select.setAttribute('multiple', 'true');
+	}
+	
+	return this.addField(name, select);
+};
+
+/**
+ * Function: addOption
+ * 
+ * Adds an option for the given label to the specified combo.
+ */
+mxForm.prototype.addOption = function(combo, label, value, isSelected)
+{
+	var option = document.createElement('option');
+	
+	mxUtils.writeln(option, label);
+	option.setAttribute('value', value);
+	
+	if (isSelected)
+	{
+		option.setAttribute('selected', isSelected);
+	}
+	
+	combo.appendChild(option);
+};
+
+/**
+ * Function: addField
+ * 
+ * Adds a new row with the name and the input field in two columns and
+ * returns the given input.
+ */
+mxForm.prototype.addField = function(name, input)
+{
+	var tr = document.createElement('tr');
+	var td = document.createElement('td');
+	mxUtils.write(td, name);
+	tr.appendChild(td);
+	
+	td = document.createElement('td');
+	td.appendChild(input);
+	tr.appendChild(td);
+	this.body.appendChild(tr);
+	
+	return input;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxImage
+ *
+ * Encapsulates the URL, width and height of an image.
+ * 
+ * Constructor: mxImage
+ * 
+ * Constructs a new image.
+ */
+function mxImage(src, width, height)
+{
+	this.src = src;
+	this.width = width;
+	this.height = height;
+};
+
+/**
+ * Variable: src
+ *
+ * String that specifies the URL of the image.
+ */
+mxImage.prototype.src = null;
+
+/**
+ * Variable: width
+ *
+ * Integer that specifies the width of the image.
+ */
+mxImage.prototype.width = null;
+
+/**
+ * Variable: height
+ *
+ * Integer that specifies the height of the image.
+ */
+mxImage.prototype.height = null;
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDivResizer
+ * 
+ * Maintains the size of a div element in Internet Explorer. This is a
+ * workaround for the right and bottom style being ignored in IE.
+ * 
+ * If you need a div to cover the scrollwidth and -height of a document,
+ * then you can use this class as follows:
+ * 
+ * (code)
+ * var resizer = new mxDivResizer(background);
+ * resizer.getDocumentHeight = function()
+ * {
+ *   return document.body.scrollHeight;
+ * }
+ * resizer.getDocumentWidth = function()
+ * {
+ *   return document.body.scrollWidth;
+ * }
+ * resizer.resize();
+ * (end)
+ * 
+ * Constructor: mxDivResizer
+ * 
+ * Constructs an object that maintains the size of a div
+ * element when the window is being resized. This is only
+ * required for Internet Explorer as it ignores the respective
+ * stylesheet information for DIV elements.
+ * 
+ * Parameters:
+ * 
+ * div - Reference to the DOM node whose size should be maintained.
+ * container - Optional Container that contains the div. Default is the
+ * window.
+ */
+function mxDivResizer(div, container)
+{
+	if (div.nodeName.toLowerCase() == 'div')
+	{
+		if (container == null)
+		{
+			container = window;
+		}
+
+		this.div = div;
+		var style = mxUtils.getCurrentStyle(div);
+		
+		if (style != null)
+		{
+			this.resizeWidth = style.width == 'auto';
+			this.resizeHeight = style.height == 'auto';
+		}
+		
+		mxEvent.addListener(container, 'resize',
+			mxUtils.bind(this, function(evt)
+			{
+				if (!this.handlingResize)
+				{
+					this.handlingResize = true;
+					this.resize();
+					this.handlingResize = false;
+				}
+			})
+		);
+		
+		this.resize();
+	}
+};
+
+/**
+ * Function: resizeWidth
+ * 
+ * Boolean specifying if the width should be updated.
+ */
+mxDivResizer.prototype.resizeWidth = true;
+
+/**
+ * Function: resizeHeight
+ * 
+ * Boolean specifying if the height should be updated.
+ */
+mxDivResizer.prototype.resizeHeight = true;
+
+/**
+ * Function: handlingResize
+ * 
+ * Boolean specifying if the width should be updated.
+ */
+mxDivResizer.prototype.handlingResize = false;
+
+/**
+ * Function: resize
+ * 
+ * Updates the style of the DIV after the window has been resized.
+ */
+mxDivResizer.prototype.resize = function()
+{
+	var w = this.getDocumentWidth();
+	var h = this.getDocumentHeight();
+
+	var l = parseInt(this.div.style.left);
+	var r = parseInt(this.div.style.right);
+	var t = parseInt(this.div.style.top);
+	var b = parseInt(this.div.style.bottom);
+	
+	if (this.resizeWidth &&
+		!isNaN(l) &&
+		!isNaN(r) &&
+		l >= 0 &&
+		r >= 0 &&
+		w - r - l > 0)
+	{
+		this.div.style.width = (w - r - l)+'px';
+	}
+	
+	if (this.resizeHeight &&
+		!isNaN(t) &&
+		!isNaN(b) &&
+		t >= 0 &&
+		b >= 0 &&
+		h - t - b > 0)
+	{
+		this.div.style.height = (h - t - b)+'px';
+	}
+};
+
+/**
+ * Function: getDocumentWidth
+ * 
+ * Hook for subclassers to return the width of the document (without
+ * scrollbars).
+ */
+mxDivResizer.prototype.getDocumentWidth = function()
+{
+	return document.body.clientWidth;
+};
+
+/**
+ * Function: getDocumentHeight
+ * 
+ * Hook for subclassers to return the height of the document (without
+ * scrollbars).
+ */
+mxDivResizer.prototype.getDocumentHeight = function()
+{
+	return document.body.clientHeight;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDragSource
+ * 
+ * Wrapper to create a drag source from a DOM element so that the element can
+ * be dragged over a graph and dropped into the graph as a new cell.
+ * 
+ * Problem is that in the dropHandler the current preview location is not
+ * available, so the preview and the dropHandler must match.
+ * 
+ * Constructor: mxDragSource
+ * 
+ * Constructs a new drag source for the given element.
+ */
+function mxDragSource(element, dropHandler)
+{
+	this.element = element;
+	this.dropHandler = dropHandler;
+	
+	// Handles a drag gesture on the element
+	mxEvent.addGestureListeners(element, mxUtils.bind(this, function(evt)
+	{
+		this.mouseDown(evt);
+	}));
+	
+	// Prevents native drag and drop
+	mxEvent.addListener(element, 'dragstart', function(evt)
+	{
+		mxEvent.consume(evt);
+	});
+	
+	this.eventConsumer = function(sender, evt)
+	{
+		var evtName = evt.getProperty('eventName');
+		var me = evt.getProperty('event');
+		
+		if (evtName != mxEvent.MOUSE_DOWN)
+		{
+			me.consume();
+		}
+	};
+};
+
+/**
+ * Variable: element
+ *
+ * Reference to the DOM node which was made draggable.
+ */
+mxDragSource.prototype.element = null;
+
+/**
+ * Variable: dropHandler
+ *
+ * Holds the DOM node that is used to represent the drag preview. If this is
+ * null then the source element will be cloned and used for the drag preview.
+ */
+mxDragSource.prototype.dropHandler = null;
+
+/**
+ * Variable: dragOffset
+ *
+ * <mxPoint> that specifies the offset of the <dragElement>. Default is null.
+ */
+mxDragSource.prototype.dragOffset = null;
+
+/**
+ * Variable: dragElement
+ *
+ * Holds the DOM node that is used to represent the drag preview. If this is
+ * null then the source element will be cloned and used for the drag preview.
+ */
+mxDragSource.prototype.dragElement = null;
+
+/**
+ * Variable: previewElement
+ *
+ * Optional <mxRectangle> that specifies the unscaled size of the preview.
+ */
+mxDragSource.prototype.previewElement = null;
+
+/**
+ * Variable: enabled
+ *
+ * Specifies if this drag source is enabled. Default is true.
+ */
+mxDragSource.prototype.enabled = true;
+
+/**
+ * Variable: currentGraph
+ *
+ * Reference to the <mxGraph> that is the current drop target.
+ */
+mxDragSource.prototype.currentGraph = null;
+
+/**
+ * Variable: currentDropTarget
+ *
+ * Holds the current drop target under the mouse.
+ */
+mxDragSource.prototype.currentDropTarget = null;
+
+/**
+ * Variable: currentPoint
+ *
+ * Holds the current drop location.
+ */
+mxDragSource.prototype.currentPoint = null;
+
+/**
+ * Variable: currentGuide
+ *
+ * Holds an <mxGuide> for the <currentGraph> if <dragPreview> is not null.
+ */
+mxDragSource.prototype.currentGuide = null;
+
+/**
+ * Variable: currentGuide
+ *
+ * Holds an <mxGuide> for the <currentGraph> if <dragPreview> is not null.
+ */
+mxDragSource.prototype.currentHighlight = null;
+
+/**
+ * Variable: autoscroll
+ *
+ * Specifies if the graph should scroll automatically. Default is true.
+ */
+mxDragSource.prototype.autoscroll = true;
+
+/**
+ * Variable: guidesEnabled
+ *
+ * Specifies if <mxGuide> should be enabled. Default is true.
+ */
+mxDragSource.prototype.guidesEnabled = true;
+
+/**
+ * Variable: gridEnabled
+ *
+ * Specifies if the grid should be allowed. Default is true.
+ */
+mxDragSource.prototype.gridEnabled = true;
+
+/**
+ * Variable: highlightDropTargets
+ *
+ * Specifies if drop targets should be highlighted. Default is true.
+ */
+mxDragSource.prototype.highlightDropTargets = true;
+
+/**
+ * Variable: dragElementZIndex
+ * 
+ * ZIndex for the drag element. Default is 100.
+ */
+mxDragSource.prototype.dragElementZIndex = 100;
+
+/**
+ * Variable: dragElementOpacity
+ * 
+ * Opacity of the drag element in %. Default is 70.
+ */
+mxDragSource.prototype.dragElementOpacity = 70;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns <enabled>.
+ */
+mxDragSource.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Sets <enabled>.
+ */
+mxDragSource.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: isGuidesEnabled
+ * 
+ * Returns <guidesEnabled>.
+ */
+mxDragSource.prototype.isGuidesEnabled = function()
+{
+	return this.guidesEnabled;
+};
+
+/**
+ * Function: setGuidesEnabled
+ * 
+ * Sets <guidesEnabled>.
+ */
+mxDragSource.prototype.setGuidesEnabled = function(value)
+{
+	this.guidesEnabled = value;
+};
+
+/**
+ * Function: isGridEnabled
+ * 
+ * Returns <gridEnabled>.
+ */
+mxDragSource.prototype.isGridEnabled = function()
+{
+	return this.gridEnabled;
+};
+
+/**
+ * Function: setGridEnabled
+ * 
+ * Sets <gridEnabled>.
+ */
+mxDragSource.prototype.setGridEnabled = function(value)
+{
+	this.gridEnabled = value;
+};
+
+/**
+ * Function: getGraphForEvent
+ * 
+ * Returns the graph for the given mouse event. This implementation returns
+ * null.
+ */
+mxDragSource.prototype.getGraphForEvent = function(evt)
+{
+	return null;
+};
+
+/**
+ * Function: getDropTarget
+ * 
+ * Returns the drop target for the given graph and coordinates. This
+ * implementation uses <mxGraph.getCellAt>.
+ */
+mxDragSource.prototype.getDropTarget = function(graph, x, y, evt)
+{
+	return graph.getCellAt(x, y);
+};
+
+/**
+ * Function: createDragElement
+ * 
+ * Creates and returns a clone of the <dragElementPrototype> or the <element>
+ * if the former is not defined.
+ */
+mxDragSource.prototype.createDragElement = function(evt)
+{
+	return this.element.cloneNode(true);
+};
+
+/**
+ * Function: createPreviewElement
+ * 
+ * Creates and returns an element which can be used as a preview in the given
+ * graph.
+ */
+mxDragSource.prototype.createPreviewElement = function(graph)
+{
+	return null;
+};
+
+/**
+ * Function: isActive
+ * 
+ * Returns true if this drag source is active.
+ */
+mxDragSource.prototype.isActive = function()
+{
+	return this.mouseMoveHandler != null;
+};
+
+/**
+ * Function: reset
+ * 
+ * Stops and removes everything and restores the state of the object.
+ */
+mxDragSource.prototype.reset = function()
+{
+	if (this.currentGraph != null)
+	{
+		this.dragExit(this.currentGraph);
+		this.currentGraph = null;
+	}
+	
+	this.removeDragElement();
+	this.removeListeners();
+	this.stopDrag();
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Returns the drop target for the given graph and coordinates. This
+ * implementation uses <mxGraph.getCellAt>.
+ * 
+ * To ignore popup menu events for a drag source, this function can be
+ * overridden as follows.
+ * 
+ * (code)
+ * var mouseDown = dragSource.mouseDown;
+ * 
+ * dragSource.mouseDown = function(evt)
+ * {
+ *   if (!mxEvent.isPopupTrigger(evt))
+ *   {
+ *     mouseDown.apply(this, arguments);
+ *   }
+ * };
+ * (end)
+ */
+mxDragSource.prototype.mouseDown = function(evt)
+{
+	if (this.enabled && !mxEvent.isConsumed(evt) && this.mouseMoveHandler == null)
+	{
+		this.startDrag(evt);
+		this.mouseMoveHandler = mxUtils.bind(this, this.mouseMove);
+		this.mouseUpHandler = mxUtils.bind(this, this.mouseUp);		
+		mxEvent.addGestureListeners(document, null, this.mouseMoveHandler, this.mouseUpHandler);
+		
+		if (mxClient.IS_TOUCH && !mxEvent.isMouseEvent(evt))
+		{
+			this.eventSource = mxEvent.getSource(evt);
+			mxEvent.addGestureListeners(this.eventSource, null, this.mouseMoveHandler, this.mouseUpHandler);
+		}
+	}
+};
+
+/**
+ * Function: startDrag
+ * 
+ * Creates the <dragElement> using <createDragElement>.
+ */
+mxDragSource.prototype.startDrag = function(evt)
+{
+	this.dragElement = this.createDragElement(evt);
+	this.dragElement.style.position = 'absolute';
+	this.dragElement.style.zIndex = this.dragElementZIndex;
+	mxUtils.setOpacity(this.dragElement, this.dragElementOpacity);
+};
+
+/**
+ * Function: stopDrag
+ * 
+ * Invokes <removeDragElement>.
+ */
+mxDragSource.prototype.stopDrag = function()
+{
+	// LATER: This used to have a mouse event. If that is still needed we need to add another
+	// final call to the DnD protocol to add a cleanup step in the case of escape press, which
+	// is not associated with a mouse event and which currently calles this method.
+	this.removeDragElement();
+};
+
+/**
+ * Function: removeDragElement
+ * 
+ * Removes and destroys the <dragElement>.
+ */
+mxDragSource.prototype.removeDragElement = function()
+{
+	if (this.dragElement != null)
+	{
+		if (this.dragElement.parentNode != null)
+		{
+			this.dragElement.parentNode.removeChild(this.dragElement);
+		}
+		
+		this.dragElement = null;
+	}
+};
+
+/**
+ * Function: graphContainsEvent
+ * 
+ * Returns true if the given graph contains the given event.
+ */
+mxDragSource.prototype.graphContainsEvent = function(graph, evt)
+{
+	var x = mxEvent.getClientX(evt);
+	var y = mxEvent.getClientY(evt);
+	var offset = mxUtils.getOffset(graph.container);
+	var origin = mxUtils.getScrollOrigin();
+
+	// Checks if event is inside the bounds of the graph container
+	return x >= offset.x - origin.x && y >= offset.y - origin.y &&
+		x <= offset.x - origin.x + graph.container.offsetWidth &&
+		y <= offset.y - origin.y + graph.container.offsetHeight;
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Gets the graph for the given event using <getGraphForEvent>, updates the
+ * <currentGraph>, calling <dragEnter> and <dragExit> on the new and old graph,
+ * respectively, and invokes <dragOver> if <currentGraph> is not null.
+ */
+mxDragSource.prototype.mouseMove = function(evt)
+{
+	var graph = this.getGraphForEvent(evt);
+	
+	// Checks if event is inside the bounds of the graph container
+	if (graph != null && !this.graphContainsEvent(graph, evt))
+	{
+		graph = null;
+	}
+
+	if (graph != this.currentGraph)
+	{
+		if (this.currentGraph != null)
+		{
+			this.dragExit(this.currentGraph, evt);
+		}
+		
+		this.currentGraph = graph;
+		
+		if (this.currentGraph != null)
+		{
+			this.dragEnter(this.currentGraph, evt);
+		}
+	}
+	
+	if (this.currentGraph != null)
+	{
+		this.dragOver(this.currentGraph, evt);
+	}
+
+	if (this.dragElement != null && (this.previewElement == null || this.previewElement.style.visibility != 'visible'))
+	{
+		var x = mxEvent.getClientX(evt);
+		var y = mxEvent.getClientY(evt);
+		
+		if (this.dragElement.parentNode == null)
+		{
+			document.body.appendChild(this.dragElement);
+		}
+
+		this.dragElement.style.visibility = 'visible';
+		
+		if (this.dragOffset != null)
+		{
+			x += this.dragOffset.x;
+			y += this.dragOffset.y;
+		}
+		
+		var offset = mxUtils.getDocumentScrollOrigin(document);
+		
+		this.dragElement.style.left = (x + offset.x) + 'px';
+		this.dragElement.style.top = (y + offset.y) + 'px';
+	}
+	else if (this.dragElement != null)
+	{
+		this.dragElement.style.visibility = 'hidden';
+	}
+	
+	mxEvent.consume(evt);
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Processes the mouse up event and invokes <drop>, <dragExit> and <stopDrag>
+ * as required.
+ */
+mxDragSource.prototype.mouseUp = function(evt)
+{
+	if (this.currentGraph != null)
+	{
+		if (this.currentPoint != null && (this.previewElement == null ||
+			this.previewElement.style.visibility != 'hidden'))
+		{
+			var scale = this.currentGraph.view.scale;
+			var tr = this.currentGraph.view.translate;
+			var x = this.currentPoint.x / scale - tr.x;
+			var y = this.currentPoint.y / scale - tr.y;
+			
+			this.drop(this.currentGraph, evt, this.currentDropTarget, x, y);
+		}
+		
+		this.dragExit(this.currentGraph);
+		this.currentGraph = null;
+	}
+
+	this.stopDrag();
+	this.removeListeners();
+	
+	mxEvent.consume(evt);
+};
+
+/**
+ * Function: removeListeners
+ * 
+ * Actives the given graph as a drop target.
+ */
+mxDragSource.prototype.removeListeners = function()
+{
+	if (this.eventSource != null)
+	{
+		mxEvent.removeGestureListeners(this.eventSource, null, this.mouseMoveHandler, this.mouseUpHandler);
+		this.eventSource = null;
+	}
+	
+	mxEvent.removeGestureListeners(document, null, this.mouseMoveHandler, this.mouseUpHandler);
+	this.mouseMoveHandler = null;
+	this.mouseUpHandler = null;
+};
+
+/**
+ * Function: dragEnter
+ * 
+ * Actives the given graph as a drop target.
+ */
+mxDragSource.prototype.dragEnter = function(graph, evt)
+{
+	graph.isMouseDown = true;
+	graph.isMouseTrigger = mxEvent.isMouseEvent(evt);
+	this.previewElement = this.createPreviewElement(graph);
+	
+	// Guide is only needed if preview element is used
+	if (this.isGuidesEnabled() && this.previewElement != null)
+	{
+		this.currentGuide = new mxGuide(graph, graph.graphHandler.getGuideStates());
+	}
+	
+	if (this.highlightDropTargets)
+	{
+		this.currentHighlight = new mxCellHighlight(graph, mxConstants.DROP_TARGET_COLOR);
+	}
+	
+	// Consumes all events in the current graph before they are fired
+	graph.addListener(mxEvent.FIRE_MOUSE_EVENT, this.eventConsumer);
+};
+
+/**
+ * Function: dragExit
+ * 
+ * Deactivates the given graph as a drop target.
+ */
+mxDragSource.prototype.dragExit = function(graph, evt)
+{
+	this.currentDropTarget = null;
+	this.currentPoint = null;
+	graph.isMouseDown = false;
+	
+	// Consumes all events in the current graph before they are fired
+	graph.removeListener(this.eventConsumer);
+	
+	if (this.previewElement != null)
+	{
+		if (this.previewElement.parentNode != null)
+		{
+			this.previewElement.parentNode.removeChild(this.previewElement);
+		}
+		
+		this.previewElement = null;
+	}
+	
+	if (this.currentGuide != null)
+	{
+		this.currentGuide.destroy();
+		this.currentGuide = null;
+	}
+	
+	if (this.currentHighlight != null)
+	{
+		this.currentHighlight.destroy();
+		this.currentHighlight = null;
+	}
+};
+
+/**
+ * Function: dragOver
+ * 
+ * Implements autoscroll, updates the <currentPoint>, highlights any drop
+ * targets and updates the preview.
+ */
+mxDragSource.prototype.dragOver = function(graph, evt)
+{
+	var offset = mxUtils.getOffset(graph.container);
+	var origin = mxUtils.getScrollOrigin(graph.container);
+	var x = mxEvent.getClientX(evt) - offset.x + origin.x - graph.panDx;
+	var y = mxEvent.getClientY(evt) - offset.y + origin.y - graph.panDy;
+
+	if (graph.autoScroll && (this.autoscroll == null || this.autoscroll))
+	{
+		graph.scrollPointToVisible(x, y, graph.autoExtend);
+	}
+
+	// Highlights the drop target under the mouse
+	if (this.currentHighlight != null && graph.isDropEnabled())
+	{
+		this.currentDropTarget = this.getDropTarget(graph, x, y, evt);
+		var state = graph.getView().getState(this.currentDropTarget);
+		this.currentHighlight.highlight(state);
+	}
+
+	// Updates the location of the preview
+	if (this.previewElement != null)
+	{
+		if (this.previewElement.parentNode == null)
+		{
+			graph.container.appendChild(this.previewElement);
+			
+			this.previewElement.style.zIndex = '3';
+			this.previewElement.style.position = 'absolute';
+		}
+		
+		var gridEnabled = this.isGridEnabled() && graph.isGridEnabledEvent(evt);
+		var hideGuide = true;
+
+		// Grid and guides
+		if (this.currentGuide != null && this.currentGuide.isEnabledForEvent(evt))
+		{
+			// LATER: HTML preview appears smaller than SVG preview
+			var w = parseInt(this.previewElement.style.width);
+			var h = parseInt(this.previewElement.style.height);
+			var bounds = new mxRectangle(0, 0, w, h);
+			var delta = new mxPoint(x, y);
+			delta = this.currentGuide.move(bounds, delta, gridEnabled);
+			hideGuide = false;
+			x = delta.x;
+			y = delta.y;
+		}
+		else if (gridEnabled)
+		{
+			var scale = graph.view.scale;
+			var tr = graph.view.translate;
+			var off = graph.gridSize / 2;
+			x = (graph.snap(x / scale - tr.x - off) + tr.x) * scale;
+			y = (graph.snap(y / scale - tr.y - off) + tr.y) * scale;
+		}
+		
+		if (this.currentGuide != null && hideGuide)
+		{
+			this.currentGuide.hide();
+		}
+		
+		if (this.previewOffset != null)
+		{
+			x += this.previewOffset.x;
+			y += this.previewOffset.y;
+		}
+
+		this.previewElement.style.left = Math.round(x) + 'px';
+		this.previewElement.style.top = Math.round(y) + 'px';
+		this.previewElement.style.visibility = 'visible';
+	}
+	
+	this.currentPoint = new mxPoint(x, y);
+};
+
+/**
+ * Function: drop
+ * 
+ * Returns the drop target for the given graph and coordinates. This
+ * implementation uses <mxGraph.getCellAt>.
+ */
+mxDragSource.prototype.drop = function(graph, evt, dropTarget, x, y)
+{
+	this.dropHandler(graph, evt, dropTarget, x, y);
+	
+	// Had to move this to after the insert because it will
+	// affect the scrollbars of the window in IE to try and
+	// make the complete container visible.
+	// LATER: Should be made optional.
+	if (graph.container.style.visibility != 'hidden')
+	{
+		graph.container.focus();
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxToolbar
+ * 
+ * Creates a toolbar inside a given DOM node. The toolbar may contain icons,
+ * buttons and combo boxes.
+ * 
+ * Event: mxEvent.SELECT
+ * 
+ * Fires when an item was selected in the toolbar. The <code>function</code>
+ * property contains the function that was selected in <selectMode>.
+ * 
+ * Constructor: mxToolbar
+ * 
+ * Constructs a toolbar in the specified container.
+ *
+ * Parameters:
+ *
+ * container - DOM node that contains the toolbar.
+ */
+function mxToolbar(container)
+{
+	this.container = container;
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxToolbar.prototype = new mxEventSource();
+mxToolbar.prototype.constructor = mxToolbar;
+
+/**
+ * Variable: container
+ * 
+ * Reference to the DOM nodes that contains the toolbar.
+ */
+mxToolbar.prototype.container = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxToolbar.prototype.enabled = true;
+
+/**
+ * Variable: noReset
+ * 
+ * Specifies if <resetMode> requires a forced flag of true for resetting
+ * the current mode in the toolbar. Default is false. This is set to true
+ * if the toolbar item is double clicked to avoid a reset after a single
+ * use of the item.
+ */
+mxToolbar.prototype.noReset = false;
+
+/**
+ * Variable: updateDefaultMode
+ * 
+ * Boolean indicating if the default mode should be the last selected
+ * switch mode or the first inserted switch mode. Default is true, that
+ * is the last selected switch mode is the default mode. The default mode
+ * is the mode to be selected after a reset of the toolbar. If this is
+ * false, then the default mode is the first inserted mode item regardless
+ * of what was last selected. Otherwise, the selected item after a reset is
+ * the previously selected item.
+ */
+mxToolbar.prototype.updateDefaultMode = true;
+
+/**
+ * Function: addItem
+ * 
+ * Adds the given function as an image with the specified title and icon
+ * and returns the new image node.
+ * 
+ * Parameters:
+ * 
+ * title - Optional string that is used as the tooltip.
+ * icon - Optional URL of the image to be used. If no URL is given, then a
+ * button is created.
+ * funct - Function to execute on a mouse click.
+ * pressedIcon - Optional URL of the pressed image. Default is a gray
+ * background.
+ * style - Optional style classname. Default is mxToolbarItem.
+ * factoryMethod - Optional factory method for popup menu, eg.
+ * function(menu, evt, cell) { menu.addItem('Hello, World!'); }
+ */
+mxToolbar.prototype.addItem = function(title, icon, funct, pressedIcon, style, factoryMethod)
+{
+	var img = document.createElement((icon != null) ? 'img' : 'button');
+	var initialClassName = style || ((factoryMethod != null) ?
+			'mxToolbarMode' : 'mxToolbarItem');
+	img.className = initialClassName;
+	img.setAttribute('src', icon);
+	
+	if (title != null)
+	{
+		if (icon != null)
+		{
+			img.setAttribute('title', title);
+		}
+		else
+		{
+			mxUtils.write(img, title);
+		}
+	}
+	
+	this.container.appendChild(img);
+
+	// Invokes the function on a click on the toolbar item
+	if (funct != null)
+	{
+		mxEvent.addListener(img, 'click', funct);
+		
+		if (mxClient.IS_TOUCH)
+		{
+			mxEvent.addListener(img, 'touchend', funct);
+		}
+	}
+
+	var mouseHandler = mxUtils.bind(this, function(evt)
+	{
+		if (pressedIcon != null)
+		{
+			img.setAttribute('src', icon);
+		}
+		else
+		{
+			img.style.backgroundColor = '';
+		}
+	});
+
+	// Highlights the toolbar item with a gray background
+	// while it is being clicked with the mouse
+	mxEvent.addGestureListeners(img, mxUtils.bind(this, function(evt)
+	{
+		if (pressedIcon != null)
+		{
+			img.setAttribute('src', pressedIcon);
+		}
+		else
+		{
+			img.style.backgroundColor = 'gray';
+		}
+		
+		// Popup Menu
+		if (factoryMethod != null)
+		{
+			if (this.menu == null)
+			{
+				this.menu = new mxPopupMenu();
+				this.menu.init();
+			}
+			
+			var last = this.currentImg;
+			
+			if (this.menu.isMenuShowing())
+			{
+				this.menu.hideMenu();
+			}
+			
+			if (last != img)
+			{
+				// Redirects factory method to local factory method
+				this.currentImg = img;
+				this.menu.factoryMethod = factoryMethod;
+				
+				var point = new mxPoint(
+					img.offsetLeft,
+					img.offsetTop + img.offsetHeight);
+				this.menu.popup(point.x, point.y, null, evt);
+
+				// Sets and overrides to restore classname
+				if (this.menu.isMenuShowing())
+				{
+					img.className = initialClassName + 'Selected';
+					
+					this.menu.hideMenu = function()
+					{
+						mxPopupMenu.prototype.hideMenu.apply(this);
+						img.className = initialClassName;
+						this.currentImg = null;
+					};
+				}
+			}
+		}
+	}), null, mouseHandler);
+
+	mxEvent.addListener(img, 'mouseout', mouseHandler);
+	
+	return img;
+};
+
+/**
+ * Function: addCombo
+ * 
+ * Adds and returns a new SELECT element using the given style. The element
+ * is placed inside a DIV with the mxToolbarComboContainer style classname.
+ * 
+ * Parameters:
+ * 
+ * style - Optional style classname. Default is mxToolbarCombo.
+ */
+mxToolbar.prototype.addCombo = function(style)
+{
+	var div = document.createElement('div');
+	div.style.display = 'inline';
+	div.className = 'mxToolbarComboContainer';
+	
+	var select = document.createElement('select');
+	select.className = style || 'mxToolbarCombo';
+	div.appendChild(select);
+	
+	this.container.appendChild(div);
+	
+	return select;
+};
+
+/**
+ * Function: addCombo
+ * 
+ * Adds and returns a new SELECT element using the given title as the
+ * default element. The selection is reset to this element after each
+ * change.
+ * 
+ * Parameters:
+ * 
+ * title - String that specifies the title of the default element.
+ * style - Optional style classname. Default is mxToolbarCombo.
+ */
+mxToolbar.prototype.addActionCombo = function(title, style)
+{
+	var select = document.createElement('select');
+	select.className = style || 'mxToolbarCombo';
+	this.addOption(select, title, null);
+	
+	mxEvent.addListener(select, 'change', function(evt)
+	{
+		var value = select.options[select.selectedIndex];
+		select.selectedIndex = 0;
+		
+		if (value.funct != null)
+		{
+			value.funct(evt);
+		}
+	});
+	
+	this.container.appendChild(select);
+	
+	return select;
+};
+
+/**
+ * Function: addOption
+ * 
+ * Adds and returns a new OPTION element inside the given SELECT element.
+ * If the given value is a function then it is stored in the option's funct
+ * field.
+ * 
+ * Parameters:
+ * 
+ * combo - SELECT element that will contain the new entry.
+ * title - String that specifies the title of the option.
+ * value - Specifies the value associated with this option.
+ */
+mxToolbar.prototype.addOption = function(combo, title, value)
+{
+	var option = document.createElement('option');
+	mxUtils.writeln(option, title);
+	
+	if (typeof(value) == 'function')
+	{
+		option.funct = value;
+	}
+	else
+	{
+		option.setAttribute('value', value);
+	}
+	
+	combo.appendChild(option);
+	
+	return option;
+};
+
+/**
+ * Function: addSwitchMode
+ * 
+ * Adds a new selectable item to the toolbar. Only one switch mode item may
+ * be selected at a time. The currently selected item is the default item
+ * after a reset of the toolbar.
+ */
+mxToolbar.prototype.addSwitchMode = function(title, icon, funct, pressedIcon, style)
+{
+	var img = document.createElement('img');
+	img.initialClassName = style || 'mxToolbarMode';
+	img.className = img.initialClassName;
+	img.setAttribute('src', icon);
+	img.altIcon = pressedIcon;
+	
+	if (title != null)
+	{
+		img.setAttribute('title', title);
+	}
+	
+	mxEvent.addListener(img, 'click', mxUtils.bind(this, function(evt)
+	{
+		var tmp = this.selectedMode.altIcon;
+		
+		if (tmp != null)
+		{
+			this.selectedMode.altIcon = this.selectedMode.getAttribute('src');
+			this.selectedMode.setAttribute('src', tmp);
+		}
+		else
+		{
+			this.selectedMode.className = this.selectedMode.initialClassName;
+		}
+		
+		if (this.updateDefaultMode)
+		{
+			this.defaultMode = img;
+		}
+		
+		this.selectedMode = img;
+		
+		var tmp = img.altIcon;
+		
+		if (tmp != null)
+		{
+			img.altIcon = img.getAttribute('src');
+			img.setAttribute('src', tmp);
+		}
+		else
+		{
+			img.className = img.initialClassName+'Selected';
+		}
+		
+		this.fireEvent(new mxEventObject(mxEvent.SELECT));
+		funct();
+	}));
+	
+	this.container.appendChild(img);
+	
+	if (this.defaultMode == null)
+	{
+		this.defaultMode = img;
+		
+		// Function should fire only once so
+		// do not pass it with the select event
+		this.selectMode(img);
+		funct();
+	}
+	
+	return img;
+};
+
+/**
+ * Function: addMode
+ * 
+ * Adds a new item to the toolbar. The selection is typically reset after
+ * the item has been consumed, for example by adding a new vertex to the
+ * graph. The reset is not carried out if the item is double clicked.
+ * 
+ * The function argument uses the following signature: funct(evt, cell) where
+ * evt is the native mouse event and cell is the cell under the mouse.
+ */
+mxToolbar.prototype.addMode = function(title, icon, funct, pressedIcon, style, toggle)
+{
+	toggle = (toggle != null) ? toggle : true;
+	var img = document.createElement((icon != null) ? 'img' : 'button');
+	
+	img.initialClassName = style || 'mxToolbarMode';
+	img.className = img.initialClassName;
+	img.setAttribute('src', icon);
+	img.altIcon = pressedIcon;
+
+	if (title != null)
+	{
+		img.setAttribute('title', title);
+	}
+	
+	if (this.enabled && toggle)
+	{
+		mxEvent.addListener(img, 'click', mxUtils.bind(this, function(evt)
+		{
+			this.selectMode(img, funct);
+			this.noReset = false;
+		}));
+		
+		mxEvent.addListener(img, 'dblclick', mxUtils.bind(this, function(evt)
+		{
+			this.selectMode(img, funct);
+			this.noReset = true;
+		}));
+		
+		if (this.defaultMode == null)
+		{
+			this.defaultMode = img;
+			this.defaultFunction = funct;
+			this.selectMode(img, funct);
+		}
+	}
+
+	this.container.appendChild(img);					
+
+	return img;
+};
+
+/**
+ * Function: selectMode
+ * 
+ * Resets the state of the previously selected mode and displays the given
+ * DOM node as selected. This function fires a select event with the given
+ * function as a parameter.
+ */
+mxToolbar.prototype.selectMode = function(domNode, funct)
+{
+	if (this.selectedMode != domNode)
+	{
+		if (this.selectedMode != null)
+		{
+			var tmp = this.selectedMode.altIcon;
+			
+			if (tmp != null)
+			{
+				this.selectedMode.altIcon = this.selectedMode.getAttribute('src');
+				this.selectedMode.setAttribute('src', tmp);
+			}
+			else
+			{
+				this.selectedMode.className = this.selectedMode.initialClassName;
+			}
+		}
+		
+		this.selectedMode = domNode;
+		var tmp = this.selectedMode.altIcon;
+		
+		if (tmp != null)
+		{
+			this.selectedMode.altIcon = this.selectedMode.getAttribute('src');
+			this.selectedMode.setAttribute('src', tmp);
+		}
+		else
+		{
+			this.selectedMode.className = this.selectedMode.initialClassName+'Selected';
+		}
+		
+		this.fireEvent(new mxEventObject(mxEvent.SELECT, "function", funct));
+	}
+};
+
+/**
+ * Function: resetMode
+ * 
+ * Selects the default mode and resets the state of the previously selected
+ * mode.
+ */
+mxToolbar.prototype.resetMode = function(forced)
+{
+	if ((forced || !this.noReset) && this.selectedMode != this.defaultMode)
+	{
+		// The last selected switch mode will be activated
+		// so the function was already executed and is
+		// no longer required here
+		this.selectMode(this.defaultMode, this.defaultFunction);
+	}
+};
+
+/**
+ * Function: addSeparator
+ * 
+ * Adds the specifies image as a separator.
+ * 
+ * Parameters:
+ * 
+ * icon - URL of the separator icon.
+ */
+mxToolbar.prototype.addSeparator = function(icon)
+{
+	return this.addItem(null, icon, null);
+};
+
+/**
+ * Function: addBreak
+ * 
+ * Adds a break to the container.
+ */
+mxToolbar.prototype.addBreak = function()
+{
+	mxUtils.br(this.container);
+};
+
+/**
+ * Function: addLine
+ * 
+ * Adds a horizontal line to the container.
+ */
+mxToolbar.prototype.addLine = function()
+{
+	var hr = document.createElement('hr');
+	
+	hr.style.marginRight = '6px';
+	hr.setAttribute('size', '1');
+	
+	this.container.appendChild(hr);
+};
+
+/**
+ * Function: destroy
+ * 
+ * Removes the toolbar and all its associated resources.
+ */
+mxToolbar.prototype.destroy = function ()
+{
+	mxEvent.release(this.container);
+	this.container = null;
+	this.defaultMode = null;
+	this.defaultFunction = null;
+	this.selectedMode = null;
+	
+	if (this.menu != null)
+	{
+		this.menu.destroy();
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxUndoableEdit
+ * 
+ * Implements a composite undoable edit. Here is an example for a custom change
+ * which gets executed via the model:
+ * 
+ * (code)
+ * function CustomChange(model, name)
+ * {
+ *   this.model = model;
+ *   this.name = name;
+ *   this.previous = name;
+ * };
+ * 
+ * CustomChange.prototype.execute = function()
+ * {
+ *   var tmp = this.model.name;
+ *   this.model.name = this.previous;
+ *   this.previous = tmp;
+ * };
+ * 
+ * var name = prompt('Enter name');
+ * graph.model.execute(new CustomChange(graph.model, name));
+ * (end)
+ * 
+ * Event: mxEvent.EXECUTED
+ * 
+ * Fires between START_EDIT and END_EDIT after an atomic change was executed.
+ * The <code>change</code> property contains the change that was executed.
+ * 
+ * Event: mxEvent.START_EDIT
+ * 
+ * Fires before a set of changes will be executed in <undo> or <redo>.
+ * This event contains no properties.
+ * 
+ * Event: mxEvent.END_EDIT
+ *
+ * Fires after a set of changeswas executed in <undo> or <redo>.
+ * This event contains no properties.
+ * 
+ * Constructor: mxUndoableEdit
+ * 
+ * Constructs a new undoable edit for the given source.
+ */
+function mxUndoableEdit(source, significant)
+{
+	this.source = source;
+	this.changes = [];
+	this.significant = (significant != null) ? significant : true;
+};
+
+/**
+ * Variable: source
+ * 
+ * Specifies the source of the edit.
+ */
+mxUndoableEdit.prototype.source = null;
+
+/**
+ * Variable: changes
+ * 
+ * Array that contains the changes that make up this edit. The changes are
+ * expected to either have an undo and redo function, or an execute
+ * function. Default is an empty array.
+ */
+mxUndoableEdit.prototype.changes = null;
+
+/**
+ * Variable: significant
+ * 
+ * Specifies if the undoable change is significant.
+ * Default is true.
+ */
+mxUndoableEdit.prototype.significant = null;
+
+/**
+ * Variable: undone
+ * 
+ * Specifies if this edit has been undone. Default is false.
+ */
+mxUndoableEdit.prototype.undone = false;
+
+/**
+ * Variable: redone
+ * 
+ * Specifies if this edit has been redone. Default is false.
+ */
+mxUndoableEdit.prototype.redone = false;
+
+/**
+ * Function: isEmpty
+ * 
+ * Returns true if the this edit contains no changes.
+ */
+mxUndoableEdit.prototype.isEmpty = function()
+{
+	return this.changes.length == 0;
+};
+
+/**
+ * Function: isSignificant
+ * 
+ * Returns <significant>.
+ */
+mxUndoableEdit.prototype.isSignificant = function()
+{
+	return this.significant;
+};
+
+/**
+ * Function: add
+ * 
+ * Adds the specified change to this edit. The change is an object that is
+ * expected to either have an undo and redo, or an execute function.
+ */
+mxUndoableEdit.prototype.add = function(change)
+{
+	this.changes.push(change);
+};
+
+/**
+ * Function: notify
+ * 
+ * Hook to notify any listeners of the changes after an <undo> or <redo>
+ * has been carried out. This implementation is empty.
+ */
+mxUndoableEdit.prototype.notify = function() { };
+
+/**
+ * Function: die
+ * 
+ * Hook to free resources after the edit has been removed from the command
+ * history. This implementation is empty.
+ */
+mxUndoableEdit.prototype.die = function() { };
+
+/**
+ * Function: undo
+ * 
+ * Undoes all changes in this edit.
+ */
+mxUndoableEdit.prototype.undo = function()
+{
+	if (!this.undone)
+	{
+		this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));
+		var count = this.changes.length;
+		
+		for (var i = count - 1; i >= 0; i--)
+		{
+			var change = this.changes[i];
+			
+			if (change.execute != null)
+			{
+				change.execute();
+			}
+			else if (change.undo != null)
+			{
+				change.undo();
+			}
+			
+			// New global executed event
+			this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED, 'change', change));
+		}
+		
+		this.undone = true;
+		this.redone = false;
+		this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT));
+	}
+	
+	this.notify();
+};
+
+/**
+ * Function: redo
+ * 
+ * Redoes all changes in this edit.
+ */
+mxUndoableEdit.prototype.redo = function()
+{
+	if (!this.redone)
+	{
+		this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));
+		var count = this.changes.length;
+		
+		for (var i = 0; i < count; i++)
+		{
+			var change = this.changes[i];
+			
+			if (change.execute != null)
+			{
+				change.execute();
+			}
+			else if (change.redo != null)
+			{
+				change.redo();
+			}
+			
+			// New global executed event
+			this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED, 'change', change));
+		}
+		
+		this.undone = false;
+		this.redone = true;
+		this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT));
+	}
+	
+	this.notify();
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxUndoManager
+ *
+ * Implements a command history. When changing the graph model, an
+ * <mxUndoableChange> object is created at the start of the transaction (when
+ * model.beginUpdate is called). All atomic changes are then added to this
+ * object until the last model.endUpdate call, at which point the
+ * <mxUndoableEdit> is dispatched in an event, and added to the history inside
+ * <mxUndoManager>. This is done by an event listener in
+ * <mxEditor.installUndoHandler>.
+ * 
+ * Each atomic change of the model is represented by an object (eg.
+ * <mxRootChange>, <mxChildChange>, <mxTerminalChange> etc) which contains the
+ * complete undo information. The <mxUndoManager> also listens to the
+ * <mxGraphView> and stores it's changes to the current root as insignificant
+ * undoable changes, so that drilling (step into, step up) is undone.
+ * 
+ * This means when you execute an atomic change on the model, then change the
+ * current root on the view and click undo, the change of the root will be
+ * undone together with the change of the model so that the display represents
+ * the state at which the model was changed. However, these changes are not
+ * transmitted for sharing as they do not represent a state change.
+ *
+ * Example:
+ * 
+ * When adding an undo manager to a graph, make sure to add it
+ * to the model and the view as well to maintain a consistent
+ * display across multiple undo/redo steps.
+ *
+ * (code)
+ * var undoManager = new mxUndoManager();
+ * var listener = function(sender, evt)
+ * {
+ *   undoManager.undoableEditHappened(evt.getProperty('edit'));
+ * };
+ * graph.getModel().addListener(mxEvent.UNDO, listener);
+ * graph.getView().addListener(mxEvent.UNDO, listener);
+ * (end)
+ * 
+ * The code creates a function that informs the undoManager
+ * of an undoable edit and binds it to the undo event of
+ * <mxGraphModel> and <mxGraphView> using
+ * <mxEventSource.addListener>.
+ * 
+ * Event: mxEvent.CLEAR
+ * 
+ * Fires after <clear> was invoked. This event has no properties.
+ * 
+ * Event: mxEvent.UNDO
+ * 
+ * Fires afer a significant edit was undone in <undo>. The <code>edit</code>
+ * property contains the <mxUndoableEdit> that was undone.
+ * 
+ * Event: mxEvent.REDO
+ * 
+ * Fires afer a significant edit was redone in <redo>. The <code>edit</code>
+ * property contains the <mxUndoableEdit> that was redone.
+ * 
+ * Event: mxEvent.ADD
+ * 
+ * Fires after an undoable edit was added to the history. The <code>edit</code>
+ * property contains the <mxUndoableEdit> that was added.
+ * 
+ * Constructor: mxUndoManager
+ *
+ * Constructs a new undo manager with the given history size. If no history
+ * size is given, then a default size of 100 steps is used.
+ */
+function mxUndoManager(size)
+{
+	this.size = (size != null) ? size : 100;
+	this.clear();
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxUndoManager.prototype = new mxEventSource();
+mxUndoManager.prototype.constructor = mxUndoManager;
+
+/**
+ * Variable: size
+ * 
+ * Maximum command history size. 0 means unlimited history. Default is
+ * 100.
+ */
+mxUndoManager.prototype.size = null;
+
+/**
+ * Variable: history
+ * 
+ * Array that contains the steps of the command history.
+ */
+mxUndoManager.prototype.history = null;
+
+/**
+ * Variable: indexOfNextAdd
+ * 
+ * Index of the element to be added next.
+ */
+mxUndoManager.prototype.indexOfNextAdd = 0;
+
+/**
+ * Function: isEmpty
+ * 
+ * Returns true if the history is empty.
+ */
+mxUndoManager.prototype.isEmpty = function()
+{
+	return this.history.length == 0;
+};
+
+/**
+ * Function: clear
+ * 
+ * Clears the command history.
+ */
+mxUndoManager.prototype.clear = function()
+{
+	this.history = [];
+	this.indexOfNextAdd = 0;
+	this.fireEvent(new mxEventObject(mxEvent.CLEAR));
+};
+
+/**
+ * Function: canUndo
+ * 
+ * Returns true if an undo is possible.
+ */
+mxUndoManager.prototype.canUndo = function()
+{
+	return this.indexOfNextAdd > 0;
+};
+
+/**
+ * Function: undo
+ * 
+ * Undoes the last change.
+ */
+mxUndoManager.prototype.undo = function()
+{
+    while (this.indexOfNextAdd > 0)
+    {
+        var edit = this.history[--this.indexOfNextAdd];
+        edit.undo();
+
+		if (edit.isSignificant())
+        {
+        	this.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', edit));
+            break;
+        }
+    }
+};
+
+/**
+ * Function: canRedo
+ * 
+ * Returns true if a redo is possible.
+ */
+mxUndoManager.prototype.canRedo = function()
+{
+	return this.indexOfNextAdd < this.history.length;
+};
+
+/**
+ * Function: redo
+ * 
+ * Redoes the last change.
+ */
+mxUndoManager.prototype.redo = function()
+{
+    var n = this.history.length;
+    
+    while (this.indexOfNextAdd < n)
+    {
+        var edit =  this.history[this.indexOfNextAdd++];
+        edit.redo();
+        
+        if (edit.isSignificant())
+        {
+        	this.fireEvent(new mxEventObject(mxEvent.REDO, 'edit', edit));
+            break;
+        }
+    }
+};
+
+/**
+ * Function: undoableEditHappened
+ * 
+ * Method to be called to add new undoable edits to the <history>.
+ */
+mxUndoManager.prototype.undoableEditHappened = function(undoableEdit)
+{
+	this.trim();
+	
+	if (this.size > 0 &&
+		this.size == this.history.length)
+	{
+		this.history.shift();
+	}
+	
+	this.history.push(undoableEdit);
+	this.indexOfNextAdd = this.history.length;
+	this.fireEvent(new mxEventObject(mxEvent.ADD, 'edit', undoableEdit));
+};
+
+/**
+ * Function: trim
+ * 
+ * Removes all pending steps after <indexOfNextAdd> from the history,
+ * invoking die on each edit. This is called from <undoableEditHappened>.
+ */
+mxUndoManager.prototype.trim = function()
+{
+	if (this.history.length > this.indexOfNextAdd)
+	{
+		var edits = this.history.splice(this.indexOfNextAdd,
+			this.history.length - this.indexOfNextAdd);
+			
+		for (var i = 0; i < edits.length; i++)
+		{
+			edits[i].die();
+		}
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ *
+ * Class: mxUrlConverter
+ * 
+ * Converts relative and absolute URLs to absolute URLs with protocol and domain.
+ */
+var mxUrlConverter = function()
+{
+	// Empty constructor
+};
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if the converter is enabled. Default is true.
+ */
+mxUrlConverter.prototype.enabled = true;
+
+/**
+ * Variable: baseUrl
+ * 
+ * Specifies the base URL to be used as a prefix for relative URLs.
+ */
+mxUrlConverter.prototype.baseUrl = null;
+
+/**
+ * Variable: baseDomain
+ * 
+ * Specifies the base domain to be used as a prefix for absolute URLs.
+ */
+mxUrlConverter.prototype.baseDomain = null;
+
+/**
+ * Function: updateBaseUrl
+ * 
+ * Private helper function to update the base URL.
+ */
+mxUrlConverter.prototype.updateBaseUrl = function()
+{
+	this.baseDomain = location.protocol + '//' + location.host;
+	this.baseUrl = this.baseDomain + location.pathname;
+	var tmp = this.baseUrl.lastIndexOf('/');
+	
+	// Strips filename etc
+	if (tmp > 0)
+	{
+		this.baseUrl = this.baseUrl.substring(0, tmp + 1);
+	}
+};
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns <enabled>.
+ */
+mxUrlConverter.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Sets <enabled>.
+ */
+mxUrlConverter.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: getBaseUrl
+ * 
+ * Returns <baseUrl>.
+ */
+mxUrlConverter.prototype.getBaseUrl = function()
+{
+	return this.baseUrl;
+};
+
+/**
+ * Function: setBaseUrl
+ * 
+ * Sets <baseUrl>.
+ */
+mxUrlConverter.prototype.setBaseUrl = function(value)
+{
+	this.baseUrl = value;
+};
+
+/**
+ * Function: getBaseDomain
+ * 
+ * Returns <baseDomain>.
+ */
+mxUrlConverter.prototype.getBaseDomain = function()
+{
+	return this.baseDomain;
+},
+
+/**
+ * Function: setBaseDomain
+ * 
+ * Sets <baseDomain>.
+ */
+mxUrlConverter.prototype.setBaseDomain = function(value)
+{
+	this.baseDomain = value;
+},
+
+/**
+ * Function: isRelativeUrl
+ * 
+ * Returns true if the given URL is relative.
+ */
+mxUrlConverter.prototype.isRelativeUrl = function(url)
+{
+	return url.substring(0, 2) != '//' && url.substring(0, 7) != 'http://' && url.substring(0, 8) != 'https://' && url.substring(0, 10) != 'data:image';
+};
+
+/**
+ * Function: convert
+ * 
+ * Converts the given URL to an absolute URL with protol and domain.
+ * Relative URLs are first converted to absolute URLs.
+ */
+mxUrlConverter.prototype.convert = function(url)
+{
+	if (this.isEnabled() && this.isRelativeUrl(url))
+	{
+		if (this.getBaseUrl() == null)
+		{
+			this.updateBaseUrl();
+		}
+		
+		if (url.charAt(0) == '/')
+		{
+			url = this.getBaseDomain() + url;
+		}
+		else
+		{
+			url = this.getBaseUrl() + url;
+		}
+	}
+	
+	return url;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxPanningManager
+ *
+ * Implements a handler for panning.
+ */
+function mxPanningManager(graph)
+{
+	this.thread = null;
+	this.active = false;
+	this.tdx = 0;
+	this.tdy = 0;
+	this.t0x = 0;
+	this.t0y = 0;
+	this.dx = 0;
+	this.dy = 0;
+	this.scrollbars = false;
+	this.scrollLeft = 0;
+	this.scrollTop = 0;
+	
+	this.mouseListener =
+	{
+	    mouseDown: function(sender, me) { },
+	    mouseMove: function(sender, me) { },
+	    mouseUp: mxUtils.bind(this, function(sender, me)
+	    {
+	    	if (this.active)
+	    	{
+	    		this.stop();
+	    	}
+	    })
+	};
+	
+	graph.addMouseListener(this.mouseListener);
+	
+	// Stops scrolling on every mouseup anywhere in the document
+	mxEvent.addListener(document, 'mouseup', mxUtils.bind(this, function()
+	{
+    	if (this.active)
+    	{
+    		this.stop();
+    	}
+	}));
+	
+	var createThread = mxUtils.bind(this, function()
+	{
+    	this.scrollbars = mxUtils.hasScrollbars(graph.container);
+    	this.scrollLeft = graph.container.scrollLeft;
+    	this.scrollTop = graph.container.scrollTop;
+
+    	return window.setInterval(mxUtils.bind(this, function()
+		{
+			this.tdx -= this.dx;
+			this.tdy -= this.dy;
+
+			if (this.scrollbars)
+			{
+				var left = -graph.container.scrollLeft - Math.ceil(this.dx);
+				var top = -graph.container.scrollTop - Math.ceil(this.dy);
+				graph.panGraph(left, top);
+				graph.panDx = this.scrollLeft - graph.container.scrollLeft;
+				graph.panDy = this.scrollTop - graph.container.scrollTop;
+				graph.fireEvent(new mxEventObject(mxEvent.PAN));
+				// TODO: Implement graph.autoExtend
+			}
+			else
+			{
+				graph.panGraph(this.getDx(), this.getDy());
+			}
+		}), this.delay);
+	});
+	
+	this.isActive = function()
+	{
+		return active;
+	};
+	
+	this.getDx = function()
+	{
+		return Math.round(this.tdx);
+	};
+	
+	this.getDy = function()
+	{
+		return Math.round(this.tdy);
+	};
+	
+	this.start = function()
+	{
+		this.t0x = graph.view.translate.x;
+		this.t0y = graph.view.translate.y;
+		this.active = true;
+	};
+	
+	this.panTo = function(x, y, w, h)
+	{
+		if (!this.active)
+		{
+			this.start();
+		}
+		
+    	this.scrollLeft = graph.container.scrollLeft;
+    	this.scrollTop = graph.container.scrollTop;
+		
+		w = (w != null) ? w : 0;
+		h = (h != null) ? h : 0;
+		
+		var c = graph.container;
+		this.dx = x + w - c.scrollLeft - c.clientWidth;
+		
+		if (this.dx < 0 && Math.abs(this.dx) < this.border)
+		{
+			this.dx = this.border + this.dx;
+		}
+		else if (this.handleMouseOut)
+		{
+			this.dx = Math.max(this.dx, 0);
+		}
+		else
+		{
+			this.dx = 0;
+		}
+		
+		if (this.dx == 0)
+		{
+			this.dx = x - c.scrollLeft;
+			
+			if (this.dx > 0 && this.dx < this.border)
+			{
+				this.dx = this.dx - this.border;
+			}
+			else if (this.handleMouseOut)
+			{
+				this.dx = Math.min(0, this.dx);
+			}
+			else
+			{
+				this.dx = 0;
+			}
+		}
+		
+		this.dy = y + h - c.scrollTop - c.clientHeight;
+
+		if (this.dy < 0 && Math.abs(this.dy) < this.border)
+		{
+			this.dy = this.border + this.dy;
+		}
+		else if (this.handleMouseOut)
+		{
+			this.dy = Math.max(this.dy, 0);
+		}
+		else
+		{
+			this.dy = 0;
+		}
+		
+		if (this.dy == 0)
+		{
+			this.dy = y - c.scrollTop;
+			
+			if (this.dy > 0 && this.dy < this.border)
+			{
+				this.dy = this.dy - this.border;
+			}
+			else if (this.handleMouseOut)
+			{
+				this.dy = Math.min(0, this.dy);
+			} 
+			else
+			{
+				this.dy = 0;
+			}
+		}
+		
+		if (this.dx != 0 || this.dy != 0)
+		{
+			this.dx *= this.damper;
+			this.dy *= this.damper;
+			
+			if (this.thread == null)
+			{
+				this.thread = createThread();
+			}
+		}
+		else if (this.thread != null)
+		{
+			window.clearInterval(this.thread);
+			this.thread = null;
+		}
+	};
+	
+	this.stop = function()
+	{
+		if (this.active)
+		{
+			this.active = false;
+		
+			if (this.thread != null)
+	    	{
+				window.clearInterval(this.thread);
+				this.thread = null;
+	    	}
+			
+			this.tdx = 0;
+			this.tdy = 0;
+			
+			if (!this.scrollbars)
+			{
+				var px = graph.panDx;
+				var py = graph.panDy;
+		    	
+		    	if (px != 0 || py != 0)
+		    	{
+		    		graph.panGraph(0, 0);
+			    	graph.view.setTranslate(this.t0x + px / graph.view.scale, this.t0y + py / graph.view.scale);
+		    	}
+			}
+			else
+			{
+				graph.panDx = 0;
+				graph.panDy = 0;
+				graph.fireEvent(new mxEventObject(mxEvent.PAN));
+			}
+		}
+	};
+	
+	this.destroy = function()
+	{
+		graph.removeMouseListener(this.mouseListener);
+	};
+};
+
+/**
+ * Variable: damper
+ * 
+ * Damper value for the panning. Default is 1/6.
+ */
+mxPanningManager.prototype.damper = 1/6;
+
+/**
+ * Variable: delay
+ * 
+ * Delay in milliseconds for the panning. Default is 10.
+ */
+mxPanningManager.prototype.delay = 10;
+
+/**
+ * Variable: handleMouseOut
+ * 
+ * Specifies if mouse events outside of the component should be handled. Default is true. 
+ */
+mxPanningManager.prototype.handleMouseOut = true;
+
+/**
+ * Variable: border
+ * 
+ * Border to handle automatic panning inside the component. Default is 0 (disabled).
+ */
+mxPanningManager.prototype.border = 0;
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxPopupMenu
+ * 
+ * Basic popup menu. To add a vertical scrollbar to a given submenu, the
+ * following code can be used.
+ * 
+ * (code)
+ * var mxPopupMenuShowMenu = mxPopupMenu.prototype.showMenu;
+ * mxPopupMenu.prototype.showMenu = function()
+ * {
+ *   mxPopupMenuShowMenu.apply(this, arguments);
+ *   
+ *   this.div.style.overflowY = 'auto';
+ *   this.div.style.overflowX = 'hidden';
+ *   this.div.style.maxHeight = '160px';
+ * };
+ * (end)
+ * 
+ * Constructor: mxPopupMenu
+ * 
+ * Constructs a popupmenu.
+ * 
+ * Event: mxEvent.SHOW
+ *
+ * Fires after the menu has been shown in <popup>.
+ */
+function mxPopupMenu(factoryMethod)
+{
+	this.factoryMethod = factoryMethod;
+	
+	if (factoryMethod != null)
+	{
+		this.init();
+	}
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxPopupMenu.prototype = new mxEventSource();
+mxPopupMenu.prototype.constructor = mxPopupMenu;
+
+/**
+ * Variable: submenuImage
+ * 
+ * URL of the image to be used for the submenu icon.
+ */
+mxPopupMenu.prototype.submenuImage = mxClient.imageBasePath + '/submenu.gif';
+
+/**
+ * Variable: zIndex
+ * 
+ * Specifies the zIndex for the popupmenu and its shadow. Default is 1006.
+ */
+mxPopupMenu.prototype.zIndex = 10006;
+
+/**
+ * Variable: factoryMethod
+ * 
+ * Function that is used to create the popup menu. The function takes the
+ * current panning handler, the <mxCell> under the mouse and the mouse
+ * event that triggered the call as arguments.
+ */
+mxPopupMenu.prototype.factoryMethod = null;
+
+/**
+ * Variable: useLeftButtonForPopup
+ * 
+ * Specifies if popupmenus should be activated by clicking the left mouse
+ * button. Default is false.
+ */
+mxPopupMenu.prototype.useLeftButtonForPopup = false;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxPopupMenu.prototype.enabled = true;
+
+/**
+ * Variable: itemCount
+ * 
+ * Contains the number of times <addItem> has been called for a new menu.
+ */
+mxPopupMenu.prototype.itemCount = 0;
+
+/**
+ * Variable: autoExpand
+ * 
+ * Specifies if submenus should be expanded on mouseover. Default is false.
+ */
+mxPopupMenu.prototype.autoExpand = false;
+
+/**
+ * Variable: smartSeparators
+ * 
+ * Specifies if separators should only be added if a menu item follows them.
+ * Default is false.
+ */
+mxPopupMenu.prototype.smartSeparators = false;
+
+/**
+ * Variable: labels
+ * 
+ * Specifies if any labels should be visible. Default is true.
+ */
+mxPopupMenu.prototype.labels = true;
+
+/**
+ * Function: init
+ * 
+ * Initializes the shapes required for this vertex handler.
+ */
+mxPopupMenu.prototype.init = function()
+{
+	// Adds the inner table
+	this.table = document.createElement('table');
+	this.table.className = 'mxPopupMenu';
+	
+	this.tbody = document.createElement('tbody');
+	this.table.appendChild(this.tbody);
+
+	// Adds the outer div
+	this.div = document.createElement('div');
+	this.div.className = 'mxPopupMenu';
+	this.div.style.display = 'inline';
+	this.div.style.zIndex = this.zIndex;
+	this.div.appendChild(this.table);
+
+	// Disables the context menu on the outer div
+	mxEvent.disableContextMenu(this.div);
+};
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxPopupMenu.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+	
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation
+ * updates <enabled>.
+ */
+mxPopupMenu.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isPopupTrigger
+ * 
+ * Returns true if the given event is a popupmenu trigger for the optional
+ * given cell.
+ * 
+ * Parameters:
+ * 
+ * me - <mxMouseEvent> that represents the mouse event.
+ */
+mxPopupMenu.prototype.isPopupTrigger = function(me)
+{
+	return me.isPopupTrigger() || (this.useLeftButtonForPopup && mxEvent.isLeftMouseButton(me.getEvent()));
+};
+
+/**
+ * Function: addItem
+ * 
+ * Adds the given item to the given parent item. If no parent item is specified
+ * then the item is added to the top-level menu. The return value may be used
+ * as the parent argument, ie. as a submenu item. The return value is the table
+ * row that represents the item.
+ * 
+ * Paramters:
+ * 
+ * title - String that represents the title of the menu item.
+ * image - Optional URL for the image icon.
+ * funct - Function associated that takes a mouseup or touchend event.
+ * parent - Optional item returned by <addItem>.
+ * iconCls - Optional string that represents the CSS class for the image icon.
+ * IconsCls is ignored if image is given.
+ * enabled - Optional boolean indicating if the item is enabled. Default is true.
+ * active - Optional boolean indicating if the menu should implement any event handling.
+ * Default is true.
+ */
+mxPopupMenu.prototype.addItem = function(title, image, funct, parent, iconCls, enabled, active)
+{
+	parent = parent || this;
+	this.itemCount++;
+	
+	// Smart separators only added if element contains items
+	if (parent.willAddSeparator)
+	{
+		if (parent.containsItems)
+		{
+			this.addSeparator(parent, true);
+		}
+
+		parent.willAddSeparator = false;
+	}
+
+	parent.containsItems = true;
+	var tr = document.createElement('tr');
+	tr.className = 'mxPopupMenuItem';
+	var col1 = document.createElement('td');
+	col1.className = 'mxPopupMenuIcon';
+
+	// Adds the given image into the first column
+	if (image != null)
+	{
+		var img = document.createElement('img');
+		img.src = image;
+		col1.appendChild(img);
+	}
+	else if (iconCls != null)
+	{
+		var div = document.createElement('div');
+		div.className = iconCls;
+		col1.appendChild(div);
+	}
+	
+	tr.appendChild(col1);
+	
+	if (this.labels)
+	{
+		var col2 = document.createElement('td');
+		col2.className = 'mxPopupMenuItem' +
+			((enabled != null && !enabled) ? ' mxDisabled' : '');
+		
+		mxUtils.write(col2, title);
+		col2.align = 'left';
+		tr.appendChild(col2);
+	
+		var col3 = document.createElement('td');
+		col3.className = 'mxPopupMenuItem' +
+			((enabled != null && !enabled) ? ' mxDisabled' : '');
+		col3.style.paddingRight = '6px';
+		col3.style.textAlign = 'right';
+		
+		tr.appendChild(col3);
+		
+		if (parent.div == null)
+		{
+			this.createSubmenu(parent);
+		}
+	}
+	
+	parent.tbody.appendChild(tr);
+
+	if (active != false && enabled != false)
+	{
+		var currentSelection = null;
+		
+		mxEvent.addGestureListeners(tr,
+			mxUtils.bind(this, function(evt)
+			{
+				this.eventReceiver = tr;
+				
+				if (parent.activeRow != tr && parent.activeRow != parent)
+				{
+					if (parent.activeRow != null && parent.activeRow.div.parentNode != null)
+					{
+						this.hideSubmenu(parent);
+					}
+					
+					if (tr.div != null)
+					{
+						this.showSubmenu(parent, tr);
+						parent.activeRow = tr;
+					}
+				}
+				
+				// Workaround for lost current selection in page because of focus in IE
+				if (mxClient.IS_QUIRKS || document.documentMode == 8)
+				{
+					currentSelection = document.selection.createRange();
+				}
+				
+				mxEvent.consume(evt);
+			}),
+			mxUtils.bind(this, function(evt)
+			{
+				if (parent.activeRow != tr && parent.activeRow != parent)
+				{
+					if (parent.activeRow != null && parent.activeRow.div.parentNode != null)
+					{
+						this.hideSubmenu(parent);
+					}
+					
+					if (this.autoExpand && tr.div != null)
+					{
+						this.showSubmenu(parent, tr);
+						parent.activeRow = tr;
+					}
+				}
+		
+				// Sets hover style because TR in IE doesn't have hover
+				tr.className = 'mxPopupMenuItemHover';
+			}),
+			mxUtils.bind(this, function(evt)
+			{
+				// EventReceiver avoids clicks on a submenu item
+				// which has just been shown in the mousedown
+				if (this.eventReceiver == tr)
+				{
+					if (parent.activeRow != tr)
+					{
+						this.hideMenu();
+					}
+					
+					// Workaround for lost current selection in page because of focus in IE
+					if (currentSelection != null)
+					{
+						// Workaround for "unspecified error" in IE8 standards
+						try
+						{
+							currentSelection.select();
+						}
+						catch (e)
+						{
+							// ignore
+						}
+
+						currentSelection = null;
+					}
+					
+					if (funct != null)
+					{
+						funct(evt);
+					}
+				}
+				
+				this.eventReceiver = null;
+				mxEvent.consume(evt);
+			})
+		);
+	
+		// Resets hover style because TR in IE doesn't have hover
+		mxEvent.addListener(tr, 'mouseout',
+			mxUtils.bind(this, function(evt)
+			{
+				tr.className = 'mxPopupMenuItem';
+			})
+		);
+	}
+	
+	return tr;
+};
+
+/**
+ * Adds a checkmark to the given menuitem.
+ */
+mxPopupMenu.prototype.addCheckmark = function(item, img)
+{
+	var td = item.firstChild.nextSibling;
+	td.style.backgroundImage = 'url(\'' + img + '\')';
+	td.style.backgroundRepeat = 'no-repeat';
+	td.style.backgroundPosition = '2px 50%';
+};
+
+/**
+ * Function: createSubmenu
+ * 
+ * Creates the nodes required to add submenu items inside the given parent
+ * item. This is called in <addItem> if a parent item is used for the first
+ * time. This adds various DOM nodes and a <submenuImage> to the parent.
+ * 
+ * Parameters:
+ * 
+ * parent - An item returned by <addItem>.
+ */
+mxPopupMenu.prototype.createSubmenu = function(parent)
+{
+	parent.table = document.createElement('table');
+	parent.table.className = 'mxPopupMenu';
+
+	parent.tbody = document.createElement('tbody');
+	parent.table.appendChild(parent.tbody);
+
+	parent.div = document.createElement('div');
+	parent.div.className = 'mxPopupMenu';
+
+	parent.div.style.position = 'absolute';
+	parent.div.style.display = 'inline';
+	parent.div.style.zIndex = this.zIndex;
+	
+	parent.div.appendChild(parent.table);
+	
+	var img = document.createElement('img');
+	img.setAttribute('src', this.submenuImage);
+	
+	// Last column of the submenu item in the parent menu
+	td = parent.firstChild.nextSibling.nextSibling;
+	td.appendChild(img);
+};
+
+/**
+ * Function: showSubmenu
+ * 
+ * Shows the submenu inside the given parent row.
+ */
+mxPopupMenu.prototype.showSubmenu = function(parent, row)
+{
+	if (row.div != null)
+	{
+		row.div.style.left = (parent.div.offsetLeft +
+			row.offsetLeft+row.offsetWidth - 1) + 'px';
+		row.div.style.top = (parent.div.offsetTop+row.offsetTop) + 'px';
+		document.body.appendChild(row.div);
+		
+		// Moves the submenu to the left side if there is no space
+		var left = parseInt(row.div.offsetLeft);
+		var width = parseInt(row.div.offsetWidth);
+		var offset = mxUtils.getDocumentScrollOrigin(document);
+		
+		var b = document.body;
+		var d = document.documentElement;
+		
+		var right = offset.x + (b.clientWidth || d.clientWidth);
+		
+		if (left + width > right)
+		{
+			row.div.style.left = Math.max(0, (parent.div.offsetLeft - width + ((mxClient.IS_IE) ? 6 : -6))) + 'px';
+		}
+		
+		mxUtils.fit(row.div);
+	}
+};
+
+/**
+ * Function: addSeparator
+ * 
+ * Adds a horizontal separator in the given parent item or the top-level menu
+ * if no parent is specified.
+ * 
+ * Parameters:
+ * 
+ * parent - Optional item returned by <addItem>.
+ * force - Optional boolean to ignore <smartSeparators>. Default is false.
+ */
+mxPopupMenu.prototype.addSeparator = function(parent, force)
+{
+	parent = parent || this;
+	
+	if (this.smartSeparators && !force)
+	{
+		parent.willAddSeparator = true;
+	}
+	else if (parent.tbody != null)
+	{
+		parent.willAddSeparator = false;
+		var tr = document.createElement('tr');
+		
+		var col1 = document.createElement('td');
+		col1.className = 'mxPopupMenuIcon';
+		col1.style.padding = '0 0 0 0px';
+		
+		tr.appendChild(col1);
+		
+		var col2 = document.createElement('td');
+		col2.style.padding = '0 0 0 0px';
+		col2.setAttribute('colSpan', '2');
+	
+		var hr = document.createElement('hr');
+		hr.setAttribute('size', '1');
+		col2.appendChild(hr);
+		
+		tr.appendChild(col2);
+		
+		parent.tbody.appendChild(tr);
+	}
+};
+
+/**
+ * Function: popup
+ * 
+ * Shows the popup menu for the given event and cell.
+ * 
+ * Example:
+ * 
+ * (code)
+ * graph.panningHandler.popup = function(x, y, cell, evt)
+ * {
+ *   mxUtils.alert('Hello, World!');
+ * }
+ * (end)
+ */
+mxPopupMenu.prototype.popup = function(x, y, cell, evt)
+{
+	if (this.div != null && this.tbody != null && this.factoryMethod != null)
+	{
+		this.div.style.left = x + 'px';
+		this.div.style.top = y + 'px';
+		
+		// Removes all child nodes from the existing menu
+		while (this.tbody.firstChild != null)
+		{
+			mxEvent.release(this.tbody.firstChild);
+			this.tbody.removeChild(this.tbody.firstChild);
+		}
+		
+		this.itemCount = 0;
+		this.factoryMethod(this, cell, evt);
+		
+		if (this.itemCount > 0)
+		{
+			this.showMenu();
+			this.fireEvent(new mxEventObject(mxEvent.SHOW));
+		}
+	}
+};
+
+/**
+ * Function: isMenuShowing
+ * 
+ * Returns true if the menu is showing.
+ */
+mxPopupMenu.prototype.isMenuShowing = function()
+{
+	return this.div != null && this.div.parentNode == document.body;
+};
+
+/**
+ * Function: showMenu
+ * 
+ * Shows the menu.
+ */
+mxPopupMenu.prototype.showMenu = function()
+{
+	// Disables filter-based shadow in IE9 standards mode
+	if (document.documentMode >= 9)
+	{
+		this.div.style.filter = 'none';
+	}
+	
+	// Fits the div inside the viewport
+	document.body.appendChild(this.div);
+	mxUtils.fit(this.div);
+};
+
+/**
+ * Function: hideMenu
+ * 
+ * Removes the menu and all submenus.
+ */
+mxPopupMenu.prototype.hideMenu = function()
+{
+	if (this.div != null)
+	{
+		if (this.div.parentNode != null)
+		{
+			this.div.parentNode.removeChild(this.div);
+		}
+		
+		this.hideSubmenu(this);
+		this.containsItems = false;
+		this.fireEvent(new mxEventObject(mxEvent.HIDE));
+	}
+};
+
+/**
+ * Function: hideSubmenu
+ * 
+ * Removes all submenus inside the given parent.
+ * 
+ * Parameters:
+ * 
+ * parent - An item returned by <addItem>.
+ */
+mxPopupMenu.prototype.hideSubmenu = function(parent)
+{
+	if (parent.activeRow != null)
+	{
+		this.hideSubmenu(parent.activeRow);
+		
+		if (parent.activeRow.div.parentNode != null)
+		{
+			parent.activeRow.div.parentNode.removeChild(parent.activeRow.div);
+		}
+		
+		parent.activeRow = null;
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxPopupMenu.prototype.destroy = function()
+{
+	if (this.div != null)
+	{
+		mxEvent.release(this.div);
+		
+		if (this.div.parentNode != null)
+		{
+			this.div.parentNode.removeChild(this.div);
+		}
+		
+		this.div = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxAutoSaveManager
+ * 
+ * Manager for automatically saving diagrams. The <save> hook must be
+ * implemented.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var mgr = new mxAutoSaveManager(editor.graph);
+ * mgr.save = function()
+ * {
+ *   mxLog.show();
+ *   mxLog.debug('save');
+ * };
+ * (end)
+ * 
+ * Constructor: mxAutoSaveManager
+ *
+ * Constructs a new automatic layout for the given graph.
+ *
+ * Arguments:
+ * 
+ * graph - Reference to the enclosing graph. 
+ */
+function mxAutoSaveManager(graph)
+{
+	// Notifies the manager of a change
+	this.changeHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.isEnabled())
+		{
+			this.graphModelChanged(evt.getProperty('edit').changes);
+		}
+	});
+
+	this.setGraph(graph);
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxAutoSaveManager.prototype = new mxEventSource();
+mxAutoSaveManager.prototype.constructor = mxAutoSaveManager;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxAutoSaveManager.prototype.graph = null;
+
+/**
+ * Variable: autoSaveDelay
+ * 
+ * Minimum amount of seconds between two consecutive autosaves. Eg. a
+ * value of 1 (s) means the graph is not stored more than once per second.
+ * Default is 10.
+ */
+mxAutoSaveManager.prototype.autoSaveDelay = 10;
+
+/**
+ * Variable: autoSaveThrottle
+ * 
+ * Minimum amount of seconds between two consecutive autosaves triggered by
+ * more than <autoSaveThreshhold> changes within a timespan of less than
+ * <autoSaveDelay> seconds. Eg. a value of 1 (s) means the graph is not
+ * stored more than once per second even if there are more than
+ * <autoSaveThreshold> changes within that timespan. Default is 2.
+ */
+mxAutoSaveManager.prototype.autoSaveThrottle = 2;
+
+/**
+ * Variable: autoSaveThreshold
+ * 
+ * Minimum amount of ignored changes before an autosave. Eg. a value of 2
+ * means after 2 change of the graph model the autosave will trigger if the
+ * condition below is true. Default is 5.
+ */
+mxAutoSaveManager.prototype.autoSaveThreshold = 5;
+
+/**
+ * Variable: ignoredChanges
+ * 
+ * Counter for ignored changes in autosave.
+ */
+mxAutoSaveManager.prototype.ignoredChanges = 0;
+
+/**
+ * Variable: lastSnapshot
+ * 
+ * Used for autosaving. See <autosave>.
+ */
+mxAutoSaveManager.prototype.lastSnapshot = 0;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if event handling is enabled. Default is true.
+ */
+mxAutoSaveManager.prototype.enabled = true;
+
+/**
+ * Variable: changeHandler
+ * 
+ * Holds the function that handles graph model changes.
+ */
+mxAutoSaveManager.prototype.changeHandler = null;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxAutoSaveManager.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation
+ * updates <enabled>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean that specifies the new enabled state.
+ */
+mxAutoSaveManager.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: setGraph
+ * 
+ * Sets the graph that the layouts operate on.
+ */
+mxAutoSaveManager.prototype.setGraph = function(graph)
+{
+	if (this.graph != null)
+	{
+		this.graph.getModel().removeListener(this.changeHandler);
+	}
+	
+	this.graph = graph;
+	
+	if (this.graph != null)
+	{
+		this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler);
+	}
+};
+
+/**
+ * Function: save
+ * 
+ * Empty hook that is called if the graph should be saved.
+ */
+mxAutoSaveManager.prototype.save = function()
+{
+	// empty
+};
+
+/**
+ * Function: graphModelChanged
+ * 
+ * Invoked when the graph model has changed.
+ */
+mxAutoSaveManager.prototype.graphModelChanged = function(changes)
+{
+	var now = new Date().getTime();
+	var dt = (now - this.lastSnapshot) / 1000;
+	
+	if (dt > this.autoSaveDelay ||
+		(this.ignoredChanges >= this.autoSaveThreshold &&
+		 dt > this.autoSaveThrottle))
+	{
+		this.save();
+		this.reset();
+	}
+	else
+	{
+		// Increments the number of ignored changes
+		this.ignoredChanges++;
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets all counters.
+ */
+mxAutoSaveManager.prototype.reset = function()
+{
+	this.lastSnapshot = new Date().getTime();
+	this.ignoredChanges = 0;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Removes all handlers from the <graph> and deletes the reference to it.
+ */
+mxAutoSaveManager.prototype.destroy = function()
+{
+	this.setGraph(null);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ *
+ * Class: mxAnimation
+ * 
+ * Implements a basic animation in JavaScript.
+ * 
+ * Constructor: mxAnimation
+ * 
+ * Constructs an animation.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxAnimation(delay)
+{
+	this.delay = (delay != null) ? delay : 20;
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxAnimation.prototype = new mxEventSource();
+mxAnimation.prototype.constructor = mxAnimation;
+
+/**
+ * Variable: delay
+ * 
+ * Specifies the delay between the animation steps. Defaul is 30ms.
+ */
+mxAnimation.prototype.delay = null;
+
+/**
+ * Variable: thread
+ * 
+ * Reference to the thread while the animation is running.
+ */
+mxAnimation.prototype.thread = null;
+
+/**
+ * Function: isRunning
+ * 
+ * Returns true if the animation is running.
+ */
+mxAnimation.prototype.isRunning = function()
+{
+	return this.thread != null;
+};
+
+/**
+ * Function: startAnimation
+ *
+ * Starts the animation by repeatedly invoking updateAnimation.
+ */
+mxAnimation.prototype.startAnimation = function()
+{
+	if (this.thread == null)
+	{
+		this.thread = window.setInterval(mxUtils.bind(this, this.updateAnimation), this.delay);
+	}
+};
+
+/**
+ * Function: updateAnimation
+ *
+ * Hook for subclassers to implement the animation. Invoke stopAnimation
+ * when finished, startAnimation to resume. This is called whenever the
+ * timer fires and fires an mxEvent.EXECUTE event with no properties.
+ */
+mxAnimation.prototype.updateAnimation = function()
+{
+	this.fireEvent(new mxEventObject(mxEvent.EXECUTE));
+};
+
+/**
+ * Function: stopAnimation
+ *
+ * Stops the animation by deleting the timer and fires an <mxEvent.DONE>.
+ */
+mxAnimation.prototype.stopAnimation = function()
+{
+	if (this.thread != null)
+	{
+		window.clearInterval(this.thread);
+		this.thread = null;
+		this.fireEvent(new mxEventObject(mxEvent.DONE));
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ *
+ * Class: mxMorphing
+ * 
+ * Implements animation for morphing cells. Here is an example of
+ * using this class for animating the result of a layout algorithm:
+ * 
+ * (code)
+ * graph.getModel().beginUpdate();
+ * try
+ * {
+ *   var circleLayout = new mxCircleLayout(graph);
+ *   circleLayout.execute(graph.getDefaultParent());
+ * }
+ * finally
+ * {
+ *   var morph = new mxMorphing(graph);
+ *   morph.addListener(mxEvent.DONE, function()
+ *   {
+ *     graph.getModel().endUpdate();
+ *   });
+ *   
+ *   morph.startAnimation();
+ * }
+ * (end)
+ * 
+ * Constructor: mxMorphing
+ * 
+ * Constructs an animation.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * steps - Optional number of steps in the morphing animation. Default is 6.
+ * ease - Optional easing constant for the animation. Default is 1.5.
+ * delay - Optional delay between the animation steps. Passed to <mxAnimation>.
+ */
+function mxMorphing(graph, steps, ease, delay)
+{
+	mxAnimation.call(this, delay);
+	this.graph = graph;
+	this.steps = (steps != null) ? steps : 6;
+	this.ease = (ease != null) ? ease : 1.5;
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxMorphing.prototype = new mxAnimation();
+mxMorphing.prototype.constructor = mxMorphing;
+
+/**
+ * Variable: graph
+ * 
+ * Specifies the delay between the animation steps. Defaul is 30ms.
+ */
+mxMorphing.prototype.graph = null;
+
+/**
+ * Variable: steps
+ * 
+ * Specifies the maximum number of steps for the morphing.
+ */
+mxMorphing.prototype.steps = null;
+
+/**
+ * Variable: step
+ * 
+ * Contains the current step.
+ */
+mxMorphing.prototype.step = 0;
+
+/**
+ * Variable: ease
+ * 
+ * Ease-off for movement towards the given vector. Larger values are
+ * slower and smoother. Default is 4.
+ */
+mxMorphing.prototype.ease = null;
+
+/**
+ * Variable: cells
+ * 
+ * Optional array of cells to be animated. If this is not specified
+ * then all cells are checked and animated if they have been moved
+ * in the current transaction.
+ */
+mxMorphing.prototype.cells = null;
+
+/**
+ * Function: updateAnimation
+ *
+ * Animation step.
+ */
+mxMorphing.prototype.updateAnimation = function()
+{
+	var move = new mxCellStatePreview(this.graph);
+
+	if (this.cells != null)
+	{
+		// Animates the given cells individually without recursion
+		for (var i = 0; i < this.cells.length; i++)
+		{
+			this.animateCell(this.cells[i], move, false);
+		}
+	}
+	else
+	{
+		// Animates all changed cells by using recursion to find
+		// the changed cells but not for the animation itself
+		this.animateCell(this.graph.getModel().getRoot(), move, true);
+	}
+	
+	this.show(move);
+	
+	if (move.isEmpty() || this.step++ >= this.steps)
+	{
+		this.stopAnimation();
+	}
+};
+
+/**
+ * Function: show
+ *
+ * Shows the changes in the given <mxCellStatePreview>.
+ */
+mxMorphing.prototype.show = function(move)
+{
+	move.show();
+};
+
+/**
+ * Function: animateCell
+ *
+ * Animates the given cell state using <mxCellStatePreview.moveState>.
+ */
+mxMorphing.prototype.animateCell = function(cell, move, recurse)
+{
+	var state = this.graph.getView().getState(cell);
+	var delta = null;
+
+	if (state != null)
+	{
+		// Moves the animated state from where it will be after the model
+		// change by subtracting the given delta vector from that location
+		delta = this.getDelta(state);
+
+		if (this.graph.getModel().isVertex(cell) && (delta.x != 0 || delta.y != 0))
+		{
+			var translate = this.graph.view.getTranslate();
+			var scale = this.graph.view.getScale();
+			
+			delta.x += translate.x * scale;
+			delta.y += translate.y * scale;
+			
+			move.moveState(state, -delta.x / this.ease, -delta.y / this.ease);
+		}
+	}
+	
+	if (recurse && !this.stopRecursion(state, delta))
+	{
+		var childCount = this.graph.getModel().getChildCount(cell);
+
+		for (var i = 0; i < childCount; i++)
+		{
+			this.animateCell(this.graph.getModel().getChildAt(cell, i), move, recurse);
+		}
+	}
+};
+
+/**
+ * Function: stopRecursion
+ *
+ * Returns true if the animation should not recursively find more
+ * deltas for children if the given parent state has been animated.
+ */
+mxMorphing.prototype.stopRecursion = function(state, delta)
+{
+	return delta != null && (delta.x != 0 || delta.y != 0);
+};
+
+/**
+ * Function: getDelta
+ *
+ * Returns the vector between the current rendered state and the future
+ * location of the state after the display will be updated.
+ */
+mxMorphing.prototype.getDelta = function(state)
+{
+	var origin = this.getOriginForCell(state.cell);
+	var translate = this.graph.getView().getTranslate();
+	var scale = this.graph.getView().getScale();
+	var x = state.x / scale - translate.x;
+	var y = state.y / scale - translate.y;
+
+	return new mxPoint((origin.x - x) * scale, (origin.y - y) * scale);
+};
+
+/**
+ * Function: getOriginForCell
+ *
+ * Returns the top, left corner of the given cell. TODO: Improve performance
+ * by using caching inside this method as the result per cell never changes
+ * during the lifecycle of this object.
+ */
+mxMorphing.prototype.getOriginForCell = function(cell)
+{
+	var result = null;
+	
+	if (cell != null)
+	{
+		var parent = this.graph.getModel().getParent(cell);
+		var geo = this.graph.getCellGeometry(cell);
+		result = this.getOriginForCell(parent);
+		
+		// TODO: Handle offsets
+		if (geo != null)
+		{
+			if (geo.relative)
+			{
+				var pgeo = this.graph.getCellGeometry(parent);
+				
+				if (pgeo != null)
+				{
+					result.x += geo.x * pgeo.width;
+					result.y += geo.y * pgeo.height;
+				}
+			}
+			else
+			{
+				result.x += geo.x;
+				result.y += geo.y;
+			}
+		}
+	}
+	
+	if (result == null)
+	{
+		var t = this.graph.view.getTranslate();
+		result = new mxPoint(-t.x, -t.y);
+	}
+	
+	return result;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxImageBundle
+ *
+ * Maps from keys to base64 encoded images or file locations. All values must
+ * be URLs or use the format data:image/format followed by a comma and the base64
+ * encoded image data, eg. "data:image/gif,XYZ", where XYZ is the base64 encoded
+ * image data.
+ * 
+ * To add a new image bundle to an existing graph, the following code is used:
+ * 
+ * (code)
+ * var bundle = new mxImageBundle(alt);
+ * bundle.putImage('myImage', 'data:image/gif,R0lGODlhEAAQAMIGAAAAAICAAICAgP' +
+ *   '//AOzp2O3r2////////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgAHACwAAAAA' +
+ *   'EAAQAAADTXi63AowynnAMDfjPUDlnAAJhmeBFxAEloliKltWmiYCQvfVr6lBPB1ggxN1hi' +
+ *   'laSSASFQpIV5HJBDyHpqK2ejVRm2AAgZCdmCGO9CIBADs=', fallback);
+ * bundle.putImage('mySvgImage', 'data:image/svg+xml,' + encodeURIComponent(
+ *   '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">' +
+ *   '<linearGradient id="gradient"><stop offset="10%" stop-color="#F00"/>' +
+ *   '<stop offset="90%" stop-color="#fcc"/></linearGradient>' +
+ *   '<rect fill="url(#gradient)" width="100%" height="100%"/></svg>'), fallback);
+ * graph.addImageBundle(bundle);
+ * (end);
+ * 
+ * Alt is an optional boolean (default is false) that specifies if the value
+ * or the fallback should be returned in <getImage>.
+ * 
+ * The image can then be referenced in any cell style using image=myImage.
+ * If you are using mxOutline, you should use the same image bundles in the
+ * graph that renders the outline.
+ * 
+ * The keys for images are resolved in <mxGraph.postProcessCellStyle> and
+ * turned into a data URI if the returned value has a short data URI format
+ * as specified above.
+ * 
+ * A typical value for the fallback is a MTHML link as defined in RFC 2557.
+ * Note that this format requires a file to be dynamically created on the
+ * server-side, or the page that contains the graph to be modified to contain
+ * the resources, this can be done by adding a comment that contains the
+ * resource in the HEAD section of the page after the title tag.
+ * 
+ * This type of fallback mechanism should be used in IE6 and IE7. IE8 does
+ * support data URIs, but the maximum size is limited to 32 KB, which means
+ * all data URIs should be limited to 32 KB.
+ */
+function mxImageBundle(alt)
+{
+	this.images = [];
+	this.alt = (alt != null) ? alt : false;
+};
+
+/**
+ * Variable: images
+ * 
+ * Maps from keys to images.
+ */
+mxImageBundle.prototype.images = null;
+
+/**
+ * Variable: alt
+ * 
+ * Specifies if the fallback representation should be returned.
+ */
+mxImageBundle.prototype.images = null;
+
+/**
+ * Function: putImage
+ * 
+ * Adds the specified entry to the map. The entry is an object with a value and
+ * fallback property as specified in the arguments.
+ */
+mxImageBundle.prototype.putImage = function(key, value, fallback)
+{
+	this.images[key] = {value: value, fallback: fallback};
+};
+
+/**
+ * Function: getImage
+ * 
+ * Returns the value for the given key. This returns the value
+ * or fallback, depending on <alt>. The fallback is returned if
+ * <alt> is true, the value is returned otherwise.
+ */
+mxImageBundle.prototype.getImage = function(key)
+{
+	var result = null;
+	
+	if (key != null)
+	{
+		var img = this.images[key];
+		
+		if (img != null)
+		{
+			result = (this.alt) ? img.fallback : img.value;
+		}
+	}
+	
+	return result;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxImageExport
+ * 
+ * Creates a new image export instance to be used with an export canvas. Here
+ * is an example that uses this class to create an image via a backend using
+ * <mxXmlExportCanvas>.
+ * 
+ * (code)
+ * var xmlDoc = mxUtils.createXmlDocument();
+ * var root = xmlDoc.createElement('output');
+ * xmlDoc.appendChild(root);
+ * 
+ * var xmlCanvas = new mxXmlCanvas2D(root);
+ * var imgExport = new mxImageExport();
+ * imgExport.drawState(graph.getView().getState(graph.model.root), xmlCanvas);
+ * 
+ * var bounds = graph.getGraphBounds();
+ * var w = Math.ceil(bounds.x + bounds.width);
+ * var h = Math.ceil(bounds.y + bounds.height);
+ * 
+ * var xml = mxUtils.getXml(root);
+ * new mxXmlRequest('export', 'format=png&w=' + w +
+ * 		'&h=' + h + '&bg=#F9F7ED&xml=' + encodeURIComponent(xml))
+ * 		.simulate(document, '_blank');
+ * (end)
+ * 
+ * Constructor: mxImageExport
+ * 
+ * Constructs a new image export.
+ */
+function mxImageExport() { };
+
+/**
+ * Variable: includeOverlays
+ * 
+ * Specifies if overlays should be included in the export. Default is false.
+ */
+mxImageExport.prototype.includeOverlays = false;
+
+/**
+ * Function: drawState
+ * 
+ * Draws the given state and all its descendants to the given canvas.
+ */
+mxImageExport.prototype.drawState = function(state, canvas)
+{
+	if (state != null)
+	{
+		this.visitStatesRecursive(state, canvas, mxUtils.bind(this, function()
+		{
+			this.drawCellState.apply(this, arguments);
+		}));
+				
+		// Paints the overlays
+		if (this.includeOverlays)
+		{
+			this.visitStatesRecursive(state, canvas, mxUtils.bind(this, function()
+			{
+				this.drawOverlays.apply(this, arguments);
+			}));
+		}
+	}
+};
+
+/**
+ * Function: drawState
+ * 
+ * Draws the given state and all its descendants to the given canvas.
+ */
+mxImageExport.prototype.visitStatesRecursive = function(state, canvas, visitor)
+{
+	if (state != null)
+	{
+		visitor(state, canvas);
+		
+		var graph = state.view.graph;
+		var childCount = graph.model.getChildCount(state.cell);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			var childState = graph.view.getState(graph.model.getChildAt(state.cell, i));
+			this.visitStatesRecursive(childState, canvas, visitor);
+		}
+	}
+};
+
+/**
+ * Function: getLinkForCellState
+ * 
+ * Returns the link for the given cell state and canvas. This returns null.
+ */
+mxImageExport.prototype.getLinkForCellState = function(state, canvas)
+{
+	return null;
+};
+
+/**
+ * Function: drawCellState
+ * 
+ * Draws the given state to the given canvas.
+ */
+mxImageExport.prototype.drawCellState = function(state, canvas)
+{
+	// Experimental feature
+	var link = this.getLinkForCellState(state, canvas);
+	
+	if (link != null)
+	{
+		canvas.setLink(link);
+	}
+	
+	// Paints the shape and text
+	this.drawShape(state, canvas);
+	this.drawText(state, canvas);
+
+	if (link != null)
+	{
+		canvas.setLink(null);
+	}
+};
+
+/**
+ * Function: drawShape
+ * 
+ * Draws the shape of the given state.
+ */
+mxImageExport.prototype.drawShape = function(state, canvas)
+{
+	if (state.shape instanceof mxShape && state.shape.checkBounds())
+	{
+		canvas.save();
+		state.shape.paint(canvas);
+		canvas.restore();
+	}
+};
+
+/**
+ * Function: drawText
+ * 
+ * Draws the text of the given state.
+ */
+mxImageExport.prototype.drawText = function(state, canvas)
+{
+	if (state.text != null && state.text.checkBounds())
+	{
+		canvas.save();
+		state.text.paint(canvas);
+		canvas.restore();
+	}
+};
+
+/**
+ * Function: drawOverlays
+ * 
+ * Draws the overlays for the given state. This is called if <includeOverlays>
+ * is true.
+ */
+mxImageExport.prototype.drawOverlays = function(state, canvas)
+{
+	if (state.overlays != null)
+	{
+		state.overlays.visit(function(id, shape)
+		{
+			if (shape instanceof mxShape)
+			{
+				shape.paint(canvas);
+			}
+		});
+	}
+};
+
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxAbstractCanvas2D
+ *
+ * Base class for all canvases. A description of the public API is available in <mxXmlCanvas2D>.
+ * All color values of <mxConstants.NONE> will be converted to null in the state.
+ * 
+ * Constructor: mxAbstractCanvas2D
+ *
+ * Constructs a new abstract canvas.
+ */
+function mxAbstractCanvas2D()
+{
+	/**
+	 * Variable: converter
+	 * 
+	 * Holds the <mxUrlConverter> to convert image URLs.
+	 */
+	this.converter = this.createUrlConverter();
+	
+	this.reset();
+};
+
+/**
+ * Variable: state
+ * 
+ * Holds the current state.
+ */
+mxAbstractCanvas2D.prototype.state = null;
+
+/**
+ * Variable: states
+ * 
+ * Stack of states.
+ */
+mxAbstractCanvas2D.prototype.states = null;
+
+/**
+ * Variable: path
+ * 
+ * Holds the current path as an array.
+ */
+mxAbstractCanvas2D.prototype.path = null;
+
+/**
+ * Variable: rotateHtml
+ * 
+ * Switch for rotation of HTML. Default is false.
+ */
+mxAbstractCanvas2D.prototype.rotateHtml = true;
+
+/**
+ * Variable: lastX
+ * 
+ * Holds the last x coordinate.
+ */
+mxAbstractCanvas2D.prototype.lastX = 0;
+
+/**
+ * Variable: lastY
+ * 
+ * Holds the last y coordinate.
+ */
+mxAbstractCanvas2D.prototype.lastY = 0;
+
+/**
+ * Variable: moveOp
+ * 
+ * Contains the string used for moving in paths. Default is 'M'.
+ */
+mxAbstractCanvas2D.prototype.moveOp = 'M';
+
+/**
+ * Variable: lineOp
+ * 
+ * Contains the string used for moving in paths. Default is 'L'.
+ */
+mxAbstractCanvas2D.prototype.lineOp = 'L';
+
+/**
+ * Variable: quadOp
+ * 
+ * Contains the string used for quadratic paths. Default is 'Q'.
+ */
+mxAbstractCanvas2D.prototype.quadOp = 'Q';
+
+/**
+ * Variable: curveOp
+ * 
+ * Contains the string used for bezier curves. Default is 'C'.
+ */
+mxAbstractCanvas2D.prototype.curveOp = 'C';
+
+/**
+ * Variable: closeOp
+ * 
+ * Holds the operator for closing curves. Default is 'Z'.
+ */
+mxAbstractCanvas2D.prototype.closeOp = 'Z';
+
+/**
+ * Variable: pointerEvents
+ * 
+ * Boolean value that specifies if events should be handled. Default is false.
+ */
+mxAbstractCanvas2D.prototype.pointerEvents = false;
+
+/**
+ * Function: createUrlConverter
+ * 
+ * Create a new <mxUrlConverter> and returns it.
+ */
+mxAbstractCanvas2D.prototype.createUrlConverter = function()
+{
+	return new mxUrlConverter();
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this canvas.
+ */
+mxAbstractCanvas2D.prototype.reset = function()
+{
+	this.state = this.createState();
+	this.states = [];
+};
+
+/**
+ * Function: createState
+ * 
+ * Creates the state of the this canvas.
+ */
+mxAbstractCanvas2D.prototype.createState = function()
+{
+	return {
+		dx: 0,
+		dy: 0,
+		scale: 1,
+		alpha: 1,
+		fillAlpha: 1,
+		strokeAlpha: 1,
+		fillColor: null,
+		gradientFillAlpha: 1,
+		gradientColor: null,
+		gradientAlpha: 1,
+		gradientDirection: null,
+		strokeColor: null,
+		strokeWidth: 1,
+		dashed: false,
+		dashPattern: '3 3',
+		fixDash: false,
+		lineCap: 'flat',
+		lineJoin: 'miter',
+		miterLimit: 10,
+		fontColor: '#000000',
+		fontBackgroundColor: null,
+		fontBorderColor: null,
+		fontSize: mxConstants.DEFAULT_FONTSIZE,
+		fontFamily: mxConstants.DEFAULT_FONTFAMILY,
+		fontStyle: 0,
+		shadow: false,
+		shadowColor: mxConstants.SHADOWCOLOR,
+		shadowAlpha: mxConstants.SHADOW_OPACITY,
+		shadowDx: mxConstants.SHADOW_OFFSET_X,
+		shadowDy: mxConstants.SHADOW_OFFSET_Y,
+		rotation: 0,
+		rotationCx: 0,
+		rotationCy: 0
+	};
+};
+
+/**
+ * Function: format
+ * 
+ * Rounds all numbers to integers.
+ */
+mxAbstractCanvas2D.prototype.format = function(value)
+{
+	return Math.round(parseFloat(value));
+};
+
+/**
+ * Function: addOp
+ * 
+ * Adds the given operation to the path.
+ */
+mxAbstractCanvas2D.prototype.addOp = function()
+{
+	if (this.path != null)
+	{
+		this.path.push(arguments[0]);
+		
+		if (arguments.length > 2)
+		{
+			var s = this.state;
+
+			for (var i = 2; i < arguments.length; i += 2)
+			{
+				this.lastX = arguments[i - 1];
+				this.lastY = arguments[i];
+				
+				this.path.push(this.format((this.lastX + s.dx) * s.scale));
+				this.path.push(this.format((this.lastY + s.dy) * s.scale));
+			}
+		}
+	}
+};
+
+/**
+ * Function: rotatePoint
+ * 
+ * Rotates the given point and returns the result as an <mxPoint>.
+ */
+mxAbstractCanvas2D.prototype.rotatePoint = function(x, y, theta, cx, cy)
+{
+	var rad = theta * (Math.PI / 180);
+	
+	return mxUtils.getRotatedPoint(new mxPoint(x, y), Math.cos(rad),
+		Math.sin(rad), new mxPoint(cx, cy));
+};
+
+/**
+ * Function: save
+ * 
+ * Saves the current state.
+ */
+mxAbstractCanvas2D.prototype.save = function()
+{
+	this.states.push(this.state);
+	this.state = mxUtils.clone(this.state);
+};
+
+/**
+ * Function: restore
+ * 
+ * Restores the current state.
+ */
+mxAbstractCanvas2D.prototype.restore = function()
+{
+	if (this.states.length > 0)
+	{
+		this.state = this.states.pop();
+	}
+};
+
+/**
+ * Function: setLink
+ * 
+ * Sets the current link. Hook for subclassers.
+ */
+mxAbstractCanvas2D.prototype.setLink = function(link)
+{
+	// nop
+};
+
+/**
+ * Function: scale
+ * 
+ * Scales the current state.
+ */
+mxAbstractCanvas2D.prototype.scale = function(value)
+{
+	this.state.scale *= value;
+	this.state.strokeWidth *= value;
+};
+
+/**
+ * Function: translate
+ * 
+ * Translates the current state.
+ */
+mxAbstractCanvas2D.prototype.translate = function(dx, dy)
+{
+	this.state.dx += dx;
+	this.state.dy += dy;
+};
+
+/**
+ * Function: rotate
+ * 
+ * Rotates the current state.
+ */
+mxAbstractCanvas2D.prototype.rotate = function(theta, flipH, flipV, cx, cy)
+{
+	// nop
+};
+
+/**
+ * Function: setAlpha
+ * 
+ * Sets the current alpha.
+ */
+mxAbstractCanvas2D.prototype.setAlpha = function(value)
+{
+	this.state.alpha = value;
+};
+
+/**
+ * Function: setFillAlpha
+ * 
+ * Sets the current solid fill alpha.
+ */
+mxAbstractCanvas2D.prototype.setFillAlpha = function(value)
+{
+	this.state.fillAlpha = value;
+};
+
+/**
+ * Function: setStrokeAlpha
+ * 
+ * Sets the current stroke alpha.
+ */
+mxAbstractCanvas2D.prototype.setStrokeAlpha = function(value)
+{
+	this.state.strokeAlpha = value;
+};
+
+/**
+ * Function: setFillColor
+ * 
+ * Sets the current fill color.
+ */
+mxAbstractCanvas2D.prototype.setFillColor = function(value)
+{
+	if (value == mxConstants.NONE)
+	{
+		value = null;
+	}
+	
+	this.state.fillColor = value;
+	this.state.gradientColor = null;
+};
+
+/**
+ * Function: setGradient
+ * 
+ * Sets the current gradient.
+ */
+mxAbstractCanvas2D.prototype.setGradient = function(color1, color2, x, y, w, h, direction, alpha1, alpha2)
+{
+	var s = this.state;
+	s.fillColor = color1;
+	s.gradientFillAlpha = (alpha1 != null) ? alpha1 : 1;
+	s.gradientColor = color2;
+	s.gradientAlpha = (alpha2 != null) ? alpha2 : 1;
+	s.gradientDirection = direction;
+};
+
+/**
+ * Function: setStrokeColor
+ * 
+ * Sets the current stroke color.
+ */
+mxAbstractCanvas2D.prototype.setStrokeColor = function(value)
+{
+	if (value == mxConstants.NONE)
+	{
+		value = null;
+	}
+	
+	this.state.strokeColor = value;
+};
+
+/**
+ * Function: setStrokeWidth
+ * 
+ * Sets the current stroke width.
+ */
+mxAbstractCanvas2D.prototype.setStrokeWidth = function(value)
+{
+	this.state.strokeWidth = value;
+};
+
+/**
+ * Function: setDashed
+ * 
+ * Enables or disables dashed lines.
+ */
+mxAbstractCanvas2D.prototype.setDashed = function(value, fixDash)
+{
+	this.state.dashed = value;
+	this.state.fixDash = fixDash;
+};
+
+/**
+ * Function: setDashPattern
+ * 
+ * Sets the current dash pattern.
+ */
+mxAbstractCanvas2D.prototype.setDashPattern = function(value)
+{
+	this.state.dashPattern = value;
+};
+
+/**
+ * Function: setLineCap
+ * 
+ * Sets the current line cap.
+ */
+mxAbstractCanvas2D.prototype.setLineCap = function(value)
+{
+	this.state.lineCap = value;
+};
+
+/**
+ * Function: setLineJoin
+ * 
+ * Sets the current line join.
+ */
+mxAbstractCanvas2D.prototype.setLineJoin = function(value)
+{
+	this.state.lineJoin = value;
+};
+
+/**
+ * Function: setMiterLimit
+ * 
+ * Sets the current miter limit.
+ */
+mxAbstractCanvas2D.prototype.setMiterLimit = function(value)
+{
+	this.state.miterLimit = value;
+};
+
+/**
+ * Function: setFontColor
+ * 
+ * Sets the current font color.
+ */
+mxAbstractCanvas2D.prototype.setFontColor = function(value)
+{
+	if (value == mxConstants.NONE)
+	{
+		value = null;
+	}
+	
+	this.state.fontColor = value;
+};
+
+/**
+ * Function: setFontColor
+ * 
+ * Sets the current font color.
+ */
+mxAbstractCanvas2D.prototype.setFontBackgroundColor = function(value)
+{
+	if (value == mxConstants.NONE)
+	{
+		value = null;
+	}
+	
+	this.state.fontBackgroundColor = value;
+};
+
+/**
+ * Function: setFontColor
+ * 
+ * Sets the current font color.
+ */
+mxAbstractCanvas2D.prototype.setFontBorderColor = function(value)
+{
+	if (value == mxConstants.NONE)
+	{
+		value = null;
+	}
+	
+	this.state.fontBorderColor = value;
+};
+
+/**
+ * Function: setFontSize
+ * 
+ * Sets the current font size.
+ */
+mxAbstractCanvas2D.prototype.setFontSize = function(value)
+{
+	this.state.fontSize = parseFloat(value);
+};
+
+/**
+ * Function: setFontFamily
+ * 
+ * Sets the current font family.
+ */
+mxAbstractCanvas2D.prototype.setFontFamily = function(value)
+{
+	this.state.fontFamily = value;
+};
+
+/**
+ * Function: setFontStyle
+ * 
+ * Sets the current font style.
+ */
+mxAbstractCanvas2D.prototype.setFontStyle = function(value)
+{
+	if (value == null)
+	{
+		value = 0;
+	}
+	
+	this.state.fontStyle = value;
+};
+
+/**
+ * Function: setShadow
+ * 
+ * Enables or disables and configures the current shadow.
+ */
+mxAbstractCanvas2D.prototype.setShadow = function(enabled)
+{
+	this.state.shadow = enabled;
+};
+
+/**
+ * Function: setShadowColor
+ * 
+ * Enables or disables and configures the current shadow.
+ */
+mxAbstractCanvas2D.prototype.setShadowColor = function(value)
+{
+	if (value == mxConstants.NONE)
+	{
+		value = null;
+	}
+	
+	this.state.shadowColor = value;
+};
+
+/**
+ * Function: setShadowAlpha
+ * 
+ * Enables or disables and configures the current shadow.
+ */
+mxAbstractCanvas2D.prototype.setShadowAlpha = function(value)
+{
+	this.state.shadowAlpha = value;
+};
+
+/**
+ * Function: setShadowOffset
+ * 
+ * Enables or disables and configures the current shadow.
+ */
+mxAbstractCanvas2D.prototype.setShadowOffset = function(dx, dy)
+{
+	this.state.shadowDx = dx;
+	this.state.shadowDy = dy;
+};
+
+/**
+ * Function: begin
+ * 
+ * Starts a new path.
+ */
+mxAbstractCanvas2D.prototype.begin = function()
+{
+	this.lastX = 0;
+	this.lastY = 0;
+	this.path = [];
+};
+
+/**
+ * Function: moveTo
+ * 
+ *  Moves the current path the given coordinates.
+ */
+mxAbstractCanvas2D.prototype.moveTo = function(x, y)
+{
+	this.addOp(this.moveOp, x, y);
+};
+
+/**
+ * Function: lineTo
+ * 
+ * Draws a line to the given coordinates. Uses moveTo with the op argument.
+ */
+mxAbstractCanvas2D.prototype.lineTo = function(x, y)
+{
+	this.addOp(this.lineOp, x, y);
+};
+
+/**
+ * Function: quadTo
+ * 
+ * Adds a quadratic curve to the current path.
+ */
+mxAbstractCanvas2D.prototype.quadTo = function(x1, y1, x2, y2)
+{
+	this.addOp(this.quadOp, x1, y1, x2, y2);
+};
+
+/**
+ * Function: curveTo
+ * 
+ * Adds a bezier curve to the current path.
+ */
+mxAbstractCanvas2D.prototype.curveTo = function(x1, y1, x2, y2, x3, y3)
+{
+	this.addOp(this.curveOp, x1, y1, x2, y2, x3, y3);
+};
+
+/**
+ * Function: arcTo
+ * 
+ * Adds the given arc to the current path. This is a synthetic operation that
+ * is broken down into curves.
+ */
+mxAbstractCanvas2D.prototype.arcTo = function(rx, ry, angle, largeArcFlag, sweepFlag, x, y)
+{
+	var curves = mxUtils.arcToCurves(this.lastX, this.lastY, rx, ry, angle, largeArcFlag, sweepFlag, x, y);
+	
+	if (curves != null)
+	{
+		for (var i = 0; i < curves.length; i += 6) 
+		{
+			this.curveTo(curves[i], curves[i + 1], curves[i + 2],
+				curves[i + 3], curves[i + 4], curves[i + 5]);
+		}
+	}
+};
+
+/**
+ * Function: close
+ * 
+ * Closes the current path.
+ */
+mxAbstractCanvas2D.prototype.close = function(x1, y1, x2, y2, x3, y3)
+{
+	this.addOp(this.closeOp);
+};
+
+/**
+ * Function: end
+ * 
+ * Empty implementation for backwards compatibility. This will be removed.
+ */
+mxAbstractCanvas2D.prototype.end = function() { };
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxXmlCanvas2D
+ *
+ * Base class for all canvases. The following methods make up the public
+ * interface of the canvas 2D for all painting in mxGraph:
+ * 
+ * - <save>, <restore>
+ * - <scale>, <translate>, <rotate>
+ * - <setAlpha>, <setFillAlpha>, <setStrokeAlpha>, <setFillColor>, <setGradient>,
+ *   <setStrokeColor>, <setStrokeWidth>, <setDashed>, <setDashPattern>, <setLineCap>, 
+ *   <setLineJoin>, <setMiterLimit>
+ * - <setFontColor>, <setFontBackgroundColor>, <setFontBorderColor>, <setFontSize>,
+ *   <setFontFamily>, <setFontStyle>
+ * - <setShadow>, <setShadowColor>, <setShadowAlpha>, <setShadowOffset>
+ * - <rect>, <roundrect>, <ellipse>, <image>, <text>
+ * - <begin>, <moveTo>, <lineTo>, <quadTo>, <curveTo>
+ * - <stroke>, <fill>, <fillAndStroke>
+ * 
+ * <mxAbstractCanvas2D.arcTo> is an additional method for drawing paths. This is
+ * a synthetic method, meaning that it is turned into a sequence of curves by
+ * default. Subclassers may add native support for arcs.
+ * 
+ * Constructor: mxXmlCanvas2D
+ *
+ * Constructs a new abstract canvas.
+ */
+function mxXmlCanvas2D(root)
+{
+	mxAbstractCanvas2D.call(this);
+
+	/**
+	 * Variable: root
+	 * 
+	 * Reference to the container for the SVG content.
+	 */
+	this.root = root;
+
+	// Writes default settings;
+	this.writeDefaults();
+};
+
+/**
+ * Extends mxAbstractCanvas2D
+ */
+mxUtils.extend(mxXmlCanvas2D, mxAbstractCanvas2D);
+
+/**
+ * Variable: textEnabled
+ * 
+ * Specifies if text output should be enabled. Default is true.
+ */
+mxXmlCanvas2D.prototype.textEnabled = true;
+
+/**
+ * Variable: compressed
+ * 
+ * Specifies if the output should be compressed by removing redundant calls.
+ * Default is true.
+ */
+mxXmlCanvas2D.prototype.compressed = true;
+
+/**
+ * Function: writeDefaults
+ * 
+ * Writes the rendering defaults to <root>:
+ */
+mxXmlCanvas2D.prototype.writeDefaults = function()
+{
+	var elem;
+	
+	// Writes font defaults
+	elem = this.createElement('fontfamily');
+	elem.setAttribute('family', mxConstants.DEFAULT_FONTFAMILY);
+	this.root.appendChild(elem);
+	
+	elem = this.createElement('fontsize');
+	elem.setAttribute('size', mxConstants.DEFAULT_FONTSIZE);
+	this.root.appendChild(elem);
+	
+	// Writes shadow defaults
+	elem = this.createElement('shadowcolor');
+	elem.setAttribute('color', mxConstants.SHADOWCOLOR);
+	this.root.appendChild(elem);
+	
+	elem = this.createElement('shadowalpha');
+	elem.setAttribute('alpha', mxConstants.SHADOW_OPACITY);
+	this.root.appendChild(elem);
+	
+	elem = this.createElement('shadowoffset');
+	elem.setAttribute('dx', mxConstants.SHADOW_OFFSET_X);
+	elem.setAttribute('dy', mxConstants.SHADOW_OFFSET_Y);
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: format
+ * 
+ * Returns a formatted number with 2 decimal places.
+ */
+mxXmlCanvas2D.prototype.format = function(value)
+{
+	return parseFloat(parseFloat(value).toFixed(2));
+};
+
+/**
+ * Function: createElement
+ * 
+ * Creates the given element using the owner document of <root>.
+ */
+mxXmlCanvas2D.prototype.createElement = function(name)
+{
+	return this.root.ownerDocument.createElement(name);
+};
+
+/**
+ * Function: save
+ * 
+ * Saves the drawing state.
+ */
+mxXmlCanvas2D.prototype.save = function()
+{
+	if (this.compressed)
+	{
+		mxAbstractCanvas2D.prototype.save.apply(this, arguments);
+	}
+	
+	this.root.appendChild(this.createElement('save'));
+};
+
+/**
+ * Function: restore
+ * 
+ * Restores the drawing state.
+ */
+mxXmlCanvas2D.prototype.restore = function()
+{
+	if (this.compressed)
+	{
+		mxAbstractCanvas2D.prototype.restore.apply(this, arguments);
+	}
+	
+	this.root.appendChild(this.createElement('restore'));
+};
+
+/**
+ * Function: scale
+ * 
+ * Scales the output.
+ * 
+ * Parameters:
+ * 
+ * scale - Number that represents the scale where 1 is equal to 100%.
+ */
+mxXmlCanvas2D.prototype.scale = function(value)
+{
+        var elem = this.createElement('scale');
+        elem.setAttribute('scale', value);
+        this.root.appendChild(elem);
+};
+
+/**
+ * Function: translate
+ * 
+ * Translates the output.
+ * 
+ * Parameters:
+ * 
+ * dx - Number that specifies the horizontal translation.
+ * dy - Number that specifies the vertical translation.
+ */
+mxXmlCanvas2D.prototype.translate = function(dx, dy)
+{
+	var elem = this.createElement('translate');
+	elem.setAttribute('dx', this.format(dx));
+	elem.setAttribute('dy', this.format(dy));
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: rotate
+ * 
+ * Rotates and/or flips the output around a given center. (Note: Due to
+ * limitations in VML, the rotation cannot be concatenated.)
+ * 
+ * Parameters:
+ * 
+ * theta - Number that represents the angle of the rotation (in degrees).
+ * flipH - Boolean indicating if the output should be flipped horizontally.
+ * flipV - Boolean indicating if the output should be flipped vertically.
+ * cx - Number that represents the x-coordinate of the rotation center.
+ * cy - Number that represents the y-coordinate of the rotation center.
+ */
+mxXmlCanvas2D.prototype.rotate = function(theta, flipH, flipV, cx, cy)
+{
+	var elem = this.createElement('rotate');
+	
+	if (theta != 0 || flipH || flipV)
+	{
+		elem.setAttribute('theta', this.format(theta));
+		elem.setAttribute('flipH', (flipH) ? '1' : '0');
+		elem.setAttribute('flipV', (flipV) ? '1' : '0');
+		elem.setAttribute('cx', this.format(cx));
+		elem.setAttribute('cy', this.format(cy));
+		this.root.appendChild(elem);
+	}
+};
+
+/**
+ * Function: setAlpha
+ * 
+ * Sets the current alpha.
+ * 
+ * Parameters:
+ * 
+ * value - Number that represents the new alpha. Possible values are between
+ * 1 (opaque) and 0 (transparent).
+ */
+mxXmlCanvas2D.prototype.setAlpha = function(value)
+{
+	if (this.compressed)
+	{
+		if (this.state.alpha == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setAlpha.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('alpha');
+	elem.setAttribute('alpha', this.format(value));
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: setFillAlpha
+ * 
+ * Sets the current fill alpha.
+ * 
+ * Parameters:
+ * 
+ * value - Number that represents the new fill alpha. Possible values are between
+ * 1 (opaque) and 0 (transparent).
+ */
+mxXmlCanvas2D.prototype.setFillAlpha = function(value)
+{
+	if (this.compressed)
+	{
+		if (this.state.fillAlpha == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setFillAlpha.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('fillalpha');
+	elem.setAttribute('alpha', this.format(value));
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: setStrokeAlpha
+ * 
+ * Sets the current stroke alpha.
+ * 
+ * Parameters:
+ * 
+ * value - Number that represents the new stroke alpha. Possible values are between
+ * 1 (opaque) and 0 (transparent).
+ */
+mxXmlCanvas2D.prototype.setStrokeAlpha = function(value)
+{
+	if (this.compressed)
+	{
+		if (this.state.strokeAlpha == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setStrokeAlpha.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('strokealpha');
+	elem.setAttribute('alpha', this.format(value));
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: setFillColor
+ * 
+ * Sets the current fill color.
+ * 
+ * Parameters:
+ * 
+ * value - Hexadecimal representation of the color or 'none'.
+ */
+mxXmlCanvas2D.prototype.setFillColor = function(value)
+{
+	if (value == mxConstants.NONE)
+	{
+		value = null;
+	}
+	
+	if (this.compressed)
+	{
+		if (this.state.fillColor == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setFillColor.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('fillcolor');
+	elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: setGradient
+ * 
+ * Sets the gradient. Note that the coordinates may be ignored by some implementations.
+ * 
+ * Parameters:
+ * 
+ * color1 - Hexadecimal representation of the start color.
+ * color2 - Hexadecimal representation of the end color.
+ * x - X-coordinate of the gradient region.
+ * y - y-coordinate of the gradient region.
+ * w - Width of the gradient region.
+ * h - Height of the gradient region.
+ * direction - One of <mxConstants.DIRECTION_NORTH>, <mxConstants.DIRECTION_EAST>,
+ * <mxConstants.DIRECTION_SOUTH> or <mxConstants.DIRECTION_WEST>.
+ * alpha1 - Optional alpha of the start color. Default is 1. Possible values
+ * are between 1 (opaque) and 0 (transparent).
+ * alpha2 - Optional alpha of the end color. Default is 1. Possible values
+ * are between 1 (opaque) and 0 (transparent).
+ */
+mxXmlCanvas2D.prototype.setGradient = function(color1, color2, x, y, w, h, direction, alpha1, alpha2)
+{
+	if (color1 != null && color2 != null)
+	{
+		mxAbstractCanvas2D.prototype.setGradient.apply(this, arguments);
+		
+		var elem = this.createElement('gradient');
+		elem.setAttribute('c1', color1);
+		elem.setAttribute('c2', color2);
+		elem.setAttribute('x', this.format(x));
+		elem.setAttribute('y', this.format(y));
+		elem.setAttribute('w', this.format(w));
+		elem.setAttribute('h', this.format(h));
+		
+		// Default direction is south
+		if (direction != null)
+		{
+			elem.setAttribute('direction', direction);
+		}
+		
+		if (alpha1 != null)
+		{
+			elem.setAttribute('alpha1', alpha1);
+		}
+		
+		if (alpha2 != null)
+		{
+			elem.setAttribute('alpha2', alpha2);
+		}
+		
+		this.root.appendChild(elem);
+	}
+};
+
+/**
+ * Function: setStrokeColor
+ * 
+ * Sets the current stroke color.
+ * 
+ * Parameters:
+ * 
+ * value - Hexadecimal representation of the color or 'none'.
+ */
+mxXmlCanvas2D.prototype.setStrokeColor = function(value)
+{
+	if (value == mxConstants.NONE)
+	{
+		value = null;
+	}
+	
+	if (this.compressed)
+	{
+		if (this.state.strokeColor == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setStrokeColor.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('strokecolor');
+	elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: setStrokeWidth
+ * 
+ * Sets the current stroke width.
+ * 
+ * Parameters:
+ * 
+ * value - Numeric representation of the stroke width.
+ */
+mxXmlCanvas2D.prototype.setStrokeWidth = function(value)
+{
+	if (this.compressed)
+	{
+		if (this.state.strokeWidth == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setStrokeWidth.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('strokewidth');
+	elem.setAttribute('width', this.format(value));
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: setDashed
+ * 
+ * Enables or disables dashed lines.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean that specifies if dashed lines should be enabled.
+ * value - Boolean that specifies if the stroke width should be ignored
+ * for the dash pattern. Default is false.
+ */
+mxXmlCanvas2D.prototype.setDashed = function(value, fixDash)
+{
+	if (this.compressed)
+	{
+		if (this.state.dashed == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setDashed.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('dashed');
+	elem.setAttribute('dashed', (value) ? '1' : '0');
+	
+	if (fixDash != null)
+	{
+		elem.setAttribute('fixDash', (fixDash) ? '1' : '0');
+	}
+	
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: setDashPattern
+ * 
+ * Sets the current dash pattern. Default is '3 3'.
+ * 
+ * Parameters:
+ * 
+ * value - String that represents the dash pattern, which is a sequence of
+ * numbers defining the length of the dashes and the length of the spaces
+ * between the dashes. The lengths are relative to the line width - a length
+ * of 1 is equals to the line width.
+ */
+mxXmlCanvas2D.prototype.setDashPattern = function(value)
+{
+	if (this.compressed)
+	{
+		if (this.state.dashPattern == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setDashPattern.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('dashpattern');
+	elem.setAttribute('pattern', value);
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: setLineCap
+ * 
+ * Sets the line cap. Default is 'flat' which corresponds to 'butt' in SVG.
+ * 
+ * Parameters:
+ * 
+ * value - String that represents the line cap. Possible values are flat, round
+ * and square.
+ */
+mxXmlCanvas2D.prototype.setLineCap = function(value)
+{
+	if (this.compressed)
+	{
+		if (this.state.lineCap == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setLineCap.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('linecap');
+	elem.setAttribute('cap', value);
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: setLineJoin
+ * 
+ * Sets the line join. Default is 'miter'.
+ * 
+ * Parameters:
+ * 
+ * value - String that represents the line join. Possible values are miter,
+ * round and bevel.
+ */
+mxXmlCanvas2D.prototype.setLineJoin = function(value)
+{
+	if (this.compressed)
+	{
+		if (this.state.lineJoin == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setLineJoin.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('linejoin');
+	elem.setAttribute('join', value);
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: setMiterLimit
+ * 
+ * Sets the miter limit. Default is 10.
+ * 
+ * Parameters:
+ * 
+ * value - Number that represents the miter limit.
+ */
+mxXmlCanvas2D.prototype.setMiterLimit = function(value)
+{
+	if (this.compressed)
+	{
+		if (this.state.miterLimit == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setMiterLimit.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('miterlimit');
+	elem.setAttribute('limit', value);
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: setFontColor
+ * 
+ * Sets the current font color. Default is '#000000'.
+ * 
+ * Parameters:
+ * 
+ * value - Hexadecimal representation of the color or 'none'.
+ */
+mxXmlCanvas2D.prototype.setFontColor = function(value)
+{
+	if (this.textEnabled)
+	{
+		if (value == mxConstants.NONE)
+		{
+			value = null;
+		}
+		
+		if (this.compressed)
+		{
+			if (this.state.fontColor == value)
+			{
+				return;
+			}
+			
+			mxAbstractCanvas2D.prototype.setFontColor.apply(this, arguments);
+		}
+		
+		var elem = this.createElement('fontcolor');
+		elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
+		this.root.appendChild(elem);
+	}
+};
+
+/**
+ * Function: setFontBackgroundColor
+ * 
+ * Sets the current font background color.
+ * 
+ * Parameters:
+ * 
+ * value - Hexadecimal representation of the color or 'none'.
+ */
+mxXmlCanvas2D.prototype.setFontBackgroundColor = function(value)
+{
+	if (this.textEnabled)
+	{
+		if (value == mxConstants.NONE)
+		{
+			value = null;
+		}
+		
+		if (this.compressed)
+		{
+			if (this.state.fontBackgroundColor == value)
+			{
+				return;
+			}
+			
+			mxAbstractCanvas2D.prototype.setFontBackgroundColor.apply(this, arguments);
+		}
+
+		var elem = this.createElement('fontbackgroundcolor');
+		elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
+		this.root.appendChild(elem);
+	}
+};
+
+/**
+ * Function: setFontBorderColor
+ * 
+ * Sets the current font border color.
+ * 
+ * Parameters:
+ * 
+ * value - Hexadecimal representation of the color or 'none'.
+ */
+mxXmlCanvas2D.prototype.setFontBorderColor = function(value)
+{
+	if (this.textEnabled)
+	{
+		if (value == mxConstants.NONE)
+		{
+			value = null;
+		}
+		
+		if (this.compressed)
+		{
+			if (this.state.fontBorderColor == value)
+			{
+				return;
+			}
+			
+			mxAbstractCanvas2D.prototype.setFontBorderColor.apply(this, arguments);
+		}
+		
+		var elem = this.createElement('fontbordercolor');
+		elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
+		this.root.appendChild(elem);
+	}
+};
+
+/**
+ * Function: setFontSize
+ * 
+ * Sets the current font size. Default is <mxConstants.DEFAULT_FONTSIZE>.
+ * 
+ * Parameters:
+ * 
+ * value - Numeric representation of the font size.
+ */
+mxXmlCanvas2D.prototype.setFontSize = function(value)
+{
+	if (this.textEnabled)
+	{
+		if (this.compressed)
+		{
+			if (this.state.fontSize == value)
+			{
+				return;
+			}
+			
+			mxAbstractCanvas2D.prototype.setFontSize.apply(this, arguments);
+		}
+		
+		var elem = this.createElement('fontsize');
+		elem.setAttribute('size', value);
+		this.root.appendChild(elem);
+	}
+};
+
+/**
+ * Function: setFontFamily
+ * 
+ * Sets the current font family. Default is <mxConstants.DEFAULT_FONTFAMILY>.
+ * 
+ * Parameters:
+ * 
+ * value - String representation of the font family. This handles the same
+ * values as the CSS font-family property.
+ */
+mxXmlCanvas2D.prototype.setFontFamily = function(value)
+{
+	if (this.textEnabled)
+	{
+		if (this.compressed)
+		{
+			if (this.state.fontFamily == value)
+			{
+				return;
+			}
+			
+			mxAbstractCanvas2D.prototype.setFontFamily.apply(this, arguments);
+		}
+		
+		var elem = this.createElement('fontfamily');
+		elem.setAttribute('family', value);
+		this.root.appendChild(elem);
+	}
+};
+
+/**
+ * Function: setFontStyle
+ * 
+ * Sets the current font style.
+ * 
+ * Parameters:
+ * 
+ * value - Numeric representation of the font family. This is the sum of the
+ * font styles from <mxConstants>.
+ */
+mxXmlCanvas2D.prototype.setFontStyle = function(value)
+{
+	if (this.textEnabled)
+	{
+		if (value == null)
+		{
+			value = 0;
+		}
+		
+		if (this.compressed)
+		{
+			if (this.state.fontStyle == value)
+			{
+				return;
+			}
+			
+			mxAbstractCanvas2D.prototype.setFontStyle.apply(this, arguments);
+		}
+		
+		var elem = this.createElement('fontstyle');
+		elem.setAttribute('style', value);
+		this.root.appendChild(elem);
+	}
+};
+
+/**
+ * Function: setShadow
+ * 
+ * Enables or disables shadows.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean that specifies if shadows should be enabled.
+ */
+mxXmlCanvas2D.prototype.setShadow = function(value)
+{
+	if (this.compressed)
+	{
+		if (this.state.shadow == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setShadow.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('shadow');
+	elem.setAttribute('enabled', (value) ? '1' : '0');
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: setShadowColor
+ * 
+ * Sets the current shadow color. Default is <mxConstants.SHADOWCOLOR>.
+ * 
+ * Parameters:
+ * 
+ * value - Hexadecimal representation of the color or 'none'.
+ */
+mxXmlCanvas2D.prototype.setShadowColor = function(value)
+{
+	if (this.compressed)
+	{
+		if (value == mxConstants.NONE)
+		{
+			value = null;
+		}
+		
+		if (this.state.shadowColor == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setShadowColor.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('shadowcolor');
+	elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: setShadowAlpha
+ * 
+ * Sets the current shadows alpha. Default is <mxConstants.SHADOW_OPACITY>.
+ * 
+ * Parameters:
+ * 
+ * value - Number that represents the new alpha. Possible values are between
+ * 1 (opaque) and 0 (transparent).
+ */
+mxXmlCanvas2D.prototype.setShadowAlpha = function(value)
+{
+	if (this.compressed)
+	{
+		if (this.state.shadowAlpha == value)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setShadowAlpha.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('shadowalpha');
+	elem.setAttribute('alpha', value);
+	this.root.appendChild(elem);
+	
+};
+
+/**
+ * Function: setShadowOffset
+ * 
+ * Sets the current shadow offset.
+ * 
+ * Parameters:
+ * 
+ * dx - Number that represents the horizontal offset of the shadow.
+ * dy - Number that represents the vertical offset of the shadow.
+ */
+mxXmlCanvas2D.prototype.setShadowOffset = function(dx, dy)
+{
+	if (this.compressed)
+	{
+		if (this.state.shadowDx == dx && this.state.shadowDy == dy)
+		{
+			return;
+		}
+		
+		mxAbstractCanvas2D.prototype.setShadowOffset.apply(this, arguments);
+	}
+	
+	var elem = this.createElement('shadowoffset');
+	elem.setAttribute('dx', dx);
+	elem.setAttribute('dy', dy);
+	this.root.appendChild(elem);
+	
+};
+
+/**
+ * Function: rect
+ * 
+ * Puts a rectangle into the drawing buffer.
+ * 
+ * Parameters:
+ * 
+ * x - Number that represents the x-coordinate of the rectangle.
+ * y - Number that represents the y-coordinate of the rectangle.
+ * w - Number that represents the width of the rectangle.
+ * h - Number that represents the height of the rectangle.
+ */
+mxXmlCanvas2D.prototype.rect = function(x, y, w, h)
+{
+	var elem = this.createElement('rect');
+	elem.setAttribute('x', this.format(x));
+	elem.setAttribute('y', this.format(y));
+	elem.setAttribute('w', this.format(w));
+	elem.setAttribute('h', this.format(h));
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: roundrect
+ * 
+ * Puts a rounded rectangle into the drawing buffer.
+ * 
+ * Parameters:
+ * 
+ * x - Number that represents the x-coordinate of the rectangle.
+ * y - Number that represents the y-coordinate of the rectangle.
+ * w - Number that represents the width of the rectangle.
+ * h - Number that represents the height of the rectangle.
+ * dx - Number that represents the horizontal rounding.
+ * dy - Number that represents the vertical rounding.
+ */
+mxXmlCanvas2D.prototype.roundrect = function(x, y, w, h, dx, dy)
+{
+	var elem = this.createElement('roundrect');
+	elem.setAttribute('x', this.format(x));
+	elem.setAttribute('y', this.format(y));
+	elem.setAttribute('w', this.format(w));
+	elem.setAttribute('h', this.format(h));
+	elem.setAttribute('dx', this.format(dx));
+	elem.setAttribute('dy', this.format(dy));
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: ellipse
+ * 
+ * Puts an ellipse into the drawing buffer.
+ * 
+ * Parameters:
+ * 
+ * x - Number that represents the x-coordinate of the ellipse.
+ * y - Number that represents the y-coordinate of the ellipse.
+ * w - Number that represents the width of the ellipse.
+ * h - Number that represents the height of the ellipse.
+ */
+mxXmlCanvas2D.prototype.ellipse = function(x, y, w, h)
+{
+	var elem = this.createElement('ellipse');
+	elem.setAttribute('x', this.format(x));
+	elem.setAttribute('y', this.format(y));
+	elem.setAttribute('w', this.format(w));
+	elem.setAttribute('h', this.format(h));
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: image
+ * 
+ * Paints an image.
+ * 
+ * Parameters:
+ * 
+ * x - Number that represents the x-coordinate of the image.
+ * y - Number that represents the y-coordinate of the image.
+ * w - Number that represents the width of the image.
+ * h - Number that represents the height of the image.
+ * src - String that specifies the URL of the image.
+ * aspect - Boolean indicating if the aspect of the image should be preserved.
+ * flipH - Boolean indicating if the image should be flipped horizontally.
+ * flipV - Boolean indicating if the image should be flipped vertically.
+ */
+mxXmlCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV)
+{
+	src = this.converter.convert(src);
+	
+	// LATER: Add option for embedding images as base64.
+	var elem = this.createElement('image');
+	elem.setAttribute('x', this.format(x));
+	elem.setAttribute('y', this.format(y));
+	elem.setAttribute('w', this.format(w));
+	elem.setAttribute('h', this.format(h));
+	elem.setAttribute('src', src);
+	elem.setAttribute('aspect', (aspect) ? '1' : '0');
+	elem.setAttribute('flipH', (flipH) ? '1' : '0');
+	elem.setAttribute('flipV', (flipV) ? '1' : '0');
+	this.root.appendChild(elem);
+};
+
+/**
+ * Function: begin
+ * 
+ * Starts a new path and puts it into the drawing buffer.
+ */
+mxXmlCanvas2D.prototype.begin = function()
+{
+	this.root.appendChild(this.createElement('begin'));
+	this.lastX = 0;
+	this.lastY = 0;
+};
+
+/**
+ * Function: moveTo
+ * 
+ * Moves the current path the given point.
+ * 
+ * Parameters:
+ * 
+ * x - Number that represents the x-coordinate of the point.
+ * y - Number that represents the y-coordinate of the point.
+ */
+mxXmlCanvas2D.prototype.moveTo = function(x, y)
+{
+	var elem = this.createElement('move');
+	elem.setAttribute('x', this.format(x));
+	elem.setAttribute('y', this.format(y));
+	this.root.appendChild(elem);
+	this.lastX = x;
+	this.lastY = y;
+};
+
+/**
+ * Function: lineTo
+ * 
+ * Draws a line to the given coordinates.
+ * 
+ * Parameters:
+ * 
+ * x - Number that represents the x-coordinate of the endpoint.
+ * y - Number that represents the y-coordinate of the endpoint.
+ */
+mxXmlCanvas2D.prototype.lineTo = function(x, y)
+{
+	var elem = this.createElement('line');
+	elem.setAttribute('x', this.format(x));
+	elem.setAttribute('y', this.format(y));
+	this.root.appendChild(elem);
+	this.lastX = x;
+	this.lastY = y;
+};
+
+/**
+ * Function: quadTo
+ * 
+ * Adds a quadratic curve to the current path.
+ * 
+ * Parameters:
+ * 
+ * x1 - Number that represents the x-coordinate of the control point.
+ * y1 - Number that represents the y-coordinate of the control point.
+ * x2 - Number that represents the x-coordinate of the endpoint.
+ * y2 - Number that represents the y-coordinate of the endpoint.
+ */
+mxXmlCanvas2D.prototype.quadTo = function(x1, y1, x2, y2)
+{
+	var elem = this.createElement('quad');
+	elem.setAttribute('x1', this.format(x1));
+	elem.setAttribute('y1', this.format(y1));
+	elem.setAttribute('x2', this.format(x2));
+	elem.setAttribute('y2', this.format(y2));
+	this.root.appendChild(elem);
+	this.lastX = x2;
+	this.lastY = y2;
+};
+
+/**
+ * Function: curveTo
+ * 
+ * Adds a bezier curve to the current path.
+ * 
+ * Parameters:
+ * 
+ * x1 - Number that represents the x-coordinate of the first control point.
+ * y1 - Number that represents the y-coordinate of the first control point.
+ * x2 - Number that represents the x-coordinate of the second control point.
+ * y2 - Number that represents the y-coordinate of the second control point.
+ * x3 - Number that represents the x-coordinate of the endpoint.
+ * y3 - Number that represents the y-coordinate of the endpoint.
+ */
+mxXmlCanvas2D.prototype.curveTo = function(x1, y1, x2, y2, x3, y3)
+{
+	var elem = this.createElement('curve');
+	elem.setAttribute('x1', this.format(x1));
+	elem.setAttribute('y1', this.format(y1));
+	elem.setAttribute('x2', this.format(x2));
+	elem.setAttribute('y2', this.format(y2));
+	elem.setAttribute('x3', this.format(x3));
+	elem.setAttribute('y3', this.format(y3));
+	this.root.appendChild(elem);
+	this.lastX = x3;
+	this.lastY = y3;
+};
+
+/**
+ * Function: close
+ * 
+ * Closes the current path.
+ */
+mxXmlCanvas2D.prototype.close = function()
+{
+	this.root.appendChild(this.createElement('close'));
+};
+
+/**
+ * Function: text
+ * 
+ * Paints the given text. Possible values for format are empty string for
+ * plain text and html for HTML markup. Background and border color as well
+ * as clipping is not available in plain text labels for VML. HTML labels
+ * are not available as part of shapes with no foreignObject support in SVG
+ * (eg. IE9, IE10).
+ * 
+ * Parameters:
+ * 
+ * x - Number that represents the x-coordinate of the text.
+ * y - Number that represents the y-coordinate of the text.
+ * w - Number that represents the available width for the text or 0 for automatic width.
+ * h - Number that represents the available height for the text or 0 for automatic height.
+ * str - String that specifies the text to be painted.
+ * align - String that represents the horizontal alignment.
+ * valign - String that represents the vertical alignment.
+ * wrap - Boolean that specifies if word-wrapping is enabled. Requires w > 0.
+ * format - Empty string for plain text or 'html' for HTML markup.
+ * overflow - Specifies the overflow behaviour of the label. Requires w > 0 and/or h > 0.
+ * clip - Boolean that specifies if the label should be clipped. Requires w > 0 and/or h > 0.
+ * rotation - Number that specifies the angle of the rotation around the anchor point of the text.
+ * dir - Optional string that specifies the text direction. Possible values are rtl and lrt.
+ */
+mxXmlCanvas2D.prototype.text = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir)
+{
+	if (this.textEnabled && str != null)
+	{
+		if (mxUtils.isNode(str))
+		{
+			str = mxUtils.getOuterHtml(str);
+		}
+		
+		var elem = this.createElement('text');
+		elem.setAttribute('x', this.format(x));
+		elem.setAttribute('y', this.format(y));
+		elem.setAttribute('w', this.format(w));
+		elem.setAttribute('h', this.format(h));
+		elem.setAttribute('str', str);
+		
+		if (align != null)
+		{
+			elem.setAttribute('align', align);
+		}
+		
+		if (valign != null)
+		{
+			elem.setAttribute('valign', valign);
+		}
+		
+		elem.setAttribute('wrap', (wrap) ? '1' : '0');
+		
+		if (format == null)
+		{
+			format = '';
+		}
+		
+		elem.setAttribute('format', format);
+		
+		if (overflow != null)
+		{
+			elem.setAttribute('overflow', overflow);
+		}
+		
+		if (clip != null)
+		{
+			elem.setAttribute('clip', (clip) ? '1' : '0');
+		}
+		
+		if (rotation != null)
+		{
+			elem.setAttribute('rotation', rotation);
+		}
+		
+		if (dir != null)
+		{
+			elem.setAttribute('dir', dir);
+		}
+		
+		this.root.appendChild(elem);
+	}
+};
+
+/**
+ * Function: stroke
+ * 
+ * Paints the outline of the current drawing buffer.
+ */
+mxXmlCanvas2D.prototype.stroke = function()
+{
+	this.root.appendChild(this.createElement('stroke'));
+};
+
+/**
+ * Function: fill
+ * 
+ * Fills the current drawing buffer.
+ */
+mxXmlCanvas2D.prototype.fill = function()
+{
+	this.root.appendChild(this.createElement('fill'));
+};
+
+/**
+ * Function: fillAndStroke
+ * 
+ * Fills the current drawing buffer and its outline.
+ */
+mxXmlCanvas2D.prototype.fillAndStroke = function()
+{
+	this.root.appendChild(this.createElement('fillstroke'));
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxSvgCanvas2D
+ *
+ * Extends <mxAbstractCanvas2D> to implement a canvas for SVG. This canvas writes all
+ * calls as SVG output to the given SVG root node.
+ * 
+ * (code)
+ * var svgDoc = mxUtils.createXmlDocument();
+ * var root = (svgDoc.createElementNS != null) ?
+ * 		svgDoc.createElementNS(mxConstants.NS_SVG, 'svg') : svgDoc.createElement('svg');
+ * 
+ * if (svgDoc.createElementNS == null)
+ * {
+ *   root.setAttribute('xmlns', mxConstants.NS_SVG);
+ *   root.setAttribute('xmlns:xlink', mxConstants.NS_XLINK);
+ * }
+ * else
+ * {
+ *   root.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', mxConstants.NS_XLINK);
+ * }
+ * 
+ * var bounds = graph.getGraphBounds();
+ * root.setAttribute('width', (bounds.x + bounds.width + 4) + 'px');
+ * root.setAttribute('height', (bounds.y + bounds.height + 4) + 'px');
+ * root.setAttribute('version', '1.1');
+ * 
+ * svgDoc.appendChild(root);
+ * 
+ * var svgCanvas = new mxSvgCanvas2D(root);
+ * (end)
+ * 
+ * A description of the public API is available in <mxXmlCanvas2D>.
+ * 
+ * To disable anti-aliasing in the output, use the following code.
+ * 
+ * (code)
+ * graph.view.canvas.ownerSVGElement.setAttribute('shape-rendering', 'crispEdges');
+ * (end)
+ * 
+ * Or set the respective attribute in the SVG element directly.
+ * 
+ * Constructor: mxSvgCanvas2D
+ *
+ * Constructs a new SVG canvas.
+ * 
+ * Parameters:
+ * 
+ * root - SVG container for the output.
+ * styleEnabled - Optional boolean that specifies if a style section should be
+ * added. The style section sets the default font-size, font-family and
+ * stroke-miterlimit globally. Default is false.
+ */
+function mxSvgCanvas2D(root, styleEnabled)
+{
+	mxAbstractCanvas2D.call(this);
+
+	/**
+	 * Variable: root
+	 * 
+	 * Reference to the container for the SVG content.
+	 */
+	this.root = root;
+
+	/**
+	 * Variable: gradients
+	 * 
+	 * Local cache of gradients for quick lookups.
+	 */
+	this.gradients = [];
+
+	/**
+	 * Variable: defs
+	 * 
+	 * Reference to the defs section of the SVG document. Only for export.
+	 */
+	this.defs = null;
+	
+	/**
+	 * Variable: styleEnabled
+	 * 
+	 * Stores the value of styleEnabled passed to the constructor.
+	 */
+	this.styleEnabled = (styleEnabled != null) ? styleEnabled : false;
+	
+	var svg = null;
+	
+	// Adds optional defs section for export
+	if (root.ownerDocument != document)
+	{
+		var node = root;
+
+		// Finds owner SVG element in XML DOM
+		while (node != null && node.nodeName != 'svg')
+		{
+			node = node.parentNode;
+		}
+		
+		svg = node;
+	}
+
+	if (svg != null)
+	{
+		// Tries to get existing defs section
+		var tmp = svg.getElementsByTagName('defs');
+		
+		if (tmp.length > 0)
+		{
+			this.defs = svg.getElementsByTagName('defs')[0];
+		}
+		
+		// Adds defs section if none exists
+		if (this.defs == null)
+		{
+			this.defs = this.createElement('defs');
+			
+			if (svg.firstChild != null)
+			{
+				svg.insertBefore(this.defs, svg.firstChild);
+			}
+			else
+			{
+				svg.appendChild(this.defs);
+			}
+		}
+
+		// Adds stylesheet
+		if (this.styleEnabled)
+		{
+			this.defs.appendChild(this.createStyle());
+		}
+	}
+};
+
+/**
+ * Extends mxAbstractCanvas2D
+ */
+mxUtils.extend(mxSvgCanvas2D, mxAbstractCanvas2D);
+
+/**
+ * Capability check for DOM parser.
+ */
+(function()
+{
+	mxSvgCanvas2D.prototype.useDomParser = !mxClient.IS_IE && typeof DOMParser === 'function' && typeof XMLSerializer === 'function';
+	
+	if (mxSvgCanvas2D.prototype.useDomParser)
+	{
+		// Checks using a generic test text if the parsing actually works. This is a workaround
+		// for older browsers where the capability check returns true but the parsing fails.
+		try
+		{
+			var doc = new DOMParser().parseFromString('test text', 'text/html');
+			mxSvgCanvas2D.prototype.useDomParser = doc != null;
+		}
+		catch (e)
+		{
+			mxSvgCanvas2D.prototype.useDomParser = false;
+		}
+	}
+})();
+
+/**
+ * Variable: path
+ * 
+ * Holds the current DOM node.
+ */
+mxSvgCanvas2D.prototype.node = null;
+
+/**
+ * Variable: matchHtmlAlignment
+ * 
+ * Specifies if plain text output should match the vertical HTML alignment.
+ * Defaul is true.
+ */
+mxSvgCanvas2D.prototype.matchHtmlAlignment = true;
+
+/**
+ * Variable: textEnabled
+ * 
+ * Specifies if text output should be enabled. Default is true.
+ */
+mxSvgCanvas2D.prototype.textEnabled = true;
+
+/**
+ * Variable: foEnabled
+ * 
+ * Specifies if use of foreignObject for HTML markup is allowed. Default is true.
+ */
+mxSvgCanvas2D.prototype.foEnabled = true;
+
+/**
+ * Variable: foAltText
+ * 
+ * Specifies the fallback text for unsupported foreignObjects in exported
+ * documents. Default is '[Object]'. If this is set to null then no fallback
+ * text is added to the exported document.
+ */
+mxSvgCanvas2D.prototype.foAltText = '[Object]';
+
+/**
+ * Variable: foOffset
+ * 
+ * Offset to be used for foreignObjects.
+ */
+mxSvgCanvas2D.prototype.foOffset = 0;
+
+/**
+ * Variable: textOffset
+ * 
+ * Offset to be used for text elements.
+ */
+mxSvgCanvas2D.prototype.textOffset = 0;
+
+/**
+ * Variable: imageOffset
+ * 
+ * Offset to be used for image elements.
+ */
+mxSvgCanvas2D.prototype.imageOffset = 0;
+
+/**
+ * Variable: strokeTolerance
+ * 
+ * Adds transparent paths for strokes.
+ */
+mxSvgCanvas2D.prototype.strokeTolerance = 0;
+
+/**
+ * Variable: refCount
+ * 
+ * Local counter for references in SVG export.
+ */
+mxSvgCanvas2D.prototype.refCount = 0;
+
+/**
+ * Variable: blockImagePointerEvents
+ * 
+ * Specifies if a transparent rectangle should be added on top of images to absorb
+ * all pointer events. Default is false. This is only needed in Firefox to disable
+ * control-clicks on images.
+ */
+mxSvgCanvas2D.prototype.blockImagePointerEvents = false;
+
+/**
+ * Variable: lineHeightCorrection
+ * 
+ * Correction factor for <mxConstants.LINE_HEIGHT> in HTML output. Default is 1.
+ */
+mxSvgCanvas2D.prototype.lineHeightCorrection = 1;
+
+/**
+ * Variable: pointerEventsValue
+ * 
+ * Default value for active pointer events. Default is all.
+ */
+mxSvgCanvas2D.prototype.pointerEventsValue = 'all';
+
+/**
+ * Variable: fontMetricsPadding
+ * 
+ * Padding to be added for text that is not wrapped to account for differences
+ * in font metrics on different platforms in pixels. Default is 10.
+ */
+mxSvgCanvas2D.prototype.fontMetricsPadding = 10;
+
+/**
+ * Variable: cacheOffsetSize
+ * 
+ * Specifies if offsetWidth and offsetHeight should be cached. Default is true.
+ * This is used to speed up repaint of text in <updateText>.
+ */
+mxSvgCanvas2D.prototype.cacheOffsetSize = true;
+
+/**
+ * Function: format
+ * 
+ * Rounds all numbers to 2 decimal points.
+ */
+mxSvgCanvas2D.prototype.format = function(value)
+{
+	return parseFloat(parseFloat(value).toFixed(2));
+};
+
+/**
+ * Function: getBaseUrl
+ * 
+ * Returns the URL of the page without the hash part. This needs to use href to
+ * include any search part with no params (ie question mark alone). This is a
+ * workaround for the fact that window.location.search is empty if there is
+ * no search string behind the question mark.
+ */
+mxSvgCanvas2D.prototype.getBaseUrl = function()
+{
+	var href = window.location.href;
+	var hash = href.lastIndexOf('#');
+	
+	if (hash > 0)
+	{
+		href = href.substring(0, hash);
+	}
+	
+	return href;
+};
+
+/**
+ * Function: reset
+ * 
+ * Returns any offsets for rendering pixels.
+ */
+mxSvgCanvas2D.prototype.reset = function()
+{
+	mxAbstractCanvas2D.prototype.reset.apply(this, arguments);
+	this.gradients = [];
+};
+
+/**
+ * Function: createStyle
+ * 
+ * Creates the optional style section.
+ */
+mxSvgCanvas2D.prototype.createStyle = function(x)
+{
+	var style = this.createElement('style');
+	style.setAttribute('type', 'text/css');
+	mxUtils.write(style, 'svg{font-family:' + mxConstants.DEFAULT_FONTFAMILY +
+			';font-size:' + mxConstants.DEFAULT_FONTSIZE +
+			';fill:none;stroke-miterlimit:10}');
+	
+	return style;
+};
+
+/**
+ * Function: createElement
+ * 
+ * Private helper function to create SVG elements
+ */
+mxSvgCanvas2D.prototype.createElement = function(tagName, namespace)
+{
+	if (this.root.ownerDocument.createElementNS != null)
+	{
+		return this.root.ownerDocument.createElementNS(namespace || mxConstants.NS_SVG, tagName);
+	}
+	else
+	{
+		var elt = this.root.ownerDocument.createElement(tagName);
+		
+		if (namespace != null)
+		{
+			elt.setAttribute('xmlns', namespace);
+		}
+		
+		return elt;
+	}
+};
+
+/**
+ * Function: getAlternateContent
+ * 
+ * Returns the alternate content for the given foreignObject.
+ */
+mxSvgCanvas2D.prototype.createAlternateContent = function(fo, x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation)
+{
+	if (this.foAltText != null)
+	{
+		var s = this.state;
+		var alt = this.createElement('text');
+		alt.setAttribute('x', Math.round(w / 2));
+		alt.setAttribute('y', Math.round((h + s.fontSize) / 2));
+		alt.setAttribute('fill', s.fontColor || 'black');
+		alt.setAttribute('text-anchor', 'middle');
+		alt.setAttribute('font-size', s.fontSize + 'px');
+		alt.setAttribute('font-family', s.fontFamily);
+		
+		if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
+		{
+			alt.setAttribute('font-weight', 'bold');
+		}
+		
+		if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
+		{
+			alt.setAttribute('font-style', 'italic');
+		}
+		
+		if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
+		{
+			alt.setAttribute('text-decoration', 'underline');
+		}
+		
+		mxUtils.write(alt, this.foAltText);
+		
+		return alt;
+	}
+	else
+	{
+		return null;
+	}
+};
+
+/**
+ * Function: createGradientId
+ * 
+ * Private helper function to create SVG elements
+ */
+mxSvgCanvas2D.prototype.createGradientId = function(start, end, alpha1, alpha2, direction)
+{
+	// Removes illegal characters from gradient ID
+	if (start.charAt(0) == '#')
+	{
+		start = start.substring(1);
+	}
+	
+	if (end.charAt(0) == '#')
+	{
+		end = end.substring(1);
+	}
+	
+	// Workaround for gradient IDs not working in Safari 5 / Chrome 6
+	// if they contain uppercase characters
+	start = start.toLowerCase() + '-' + alpha1;
+	end = end.toLowerCase() + '-' + alpha2;
+
+	// Wrong gradient directions possible?
+	var dir = null;
+	
+	if (direction == null || direction == mxConstants.DIRECTION_SOUTH)
+	{
+		dir = 's';
+	}
+	else if (direction == mxConstants.DIRECTION_EAST)
+	{
+		dir = 'e';
+	}
+	else
+	{
+		var tmp = start;
+		start = end;
+		end = tmp;
+		
+		if (direction == mxConstants.DIRECTION_NORTH)
+		{
+			dir = 's';
+		}
+		else if (direction == mxConstants.DIRECTION_WEST)
+		{
+			dir = 'e';
+		}
+	}
+	
+	return 'mx-gradient-' + start + '-' + end + '-' + dir;
+};
+
+/**
+ * Function: getSvgGradient
+ * 
+ * Private helper function to create SVG elements
+ */
+mxSvgCanvas2D.prototype.getSvgGradient = function(start, end, alpha1, alpha2, direction)
+{
+	var id = this.createGradientId(start, end, alpha1, alpha2, direction);
+	var gradient = this.gradients[id];
+	
+	if (gradient == null)
+	{
+		var svg = this.root.ownerSVGElement;
+
+		var counter = 0;
+		var tmpId = id + '-' + counter;
+
+		if (svg != null)
+		{
+			gradient = svg.ownerDocument.getElementById(tmpId);
+			
+			while (gradient != null && gradient.ownerSVGElement != svg)
+			{
+				tmpId = id + '-' + counter++;
+				gradient = svg.ownerDocument.getElementById(tmpId);
+			}
+		}
+		else
+		{
+			// Uses shorter IDs for export
+			tmpId = 'id' + (++this.refCount);
+		}
+		
+		if (gradient == null)
+		{
+			gradient = this.createSvgGradient(start, end, alpha1, alpha2, direction);
+			gradient.setAttribute('id', tmpId);
+			
+			if (this.defs != null)
+			{
+				this.defs.appendChild(gradient);
+			}
+			else
+			{
+				svg.appendChild(gradient);
+			}
+		}
+
+		this.gradients[id] = gradient;
+	}
+
+	return gradient.getAttribute('id');
+};
+
+/**
+ * Function: createSvgGradient
+ * 
+ * Creates the given SVG gradient.
+ */
+mxSvgCanvas2D.prototype.createSvgGradient = function(start, end, alpha1, alpha2, direction)
+{
+	var gradient = this.createElement('linearGradient');
+	gradient.setAttribute('x1', '0%');
+	gradient.setAttribute('y1', '0%');
+	gradient.setAttribute('x2', '0%');
+	gradient.setAttribute('y2', '0%');
+	
+	if (direction == null || direction == mxConstants.DIRECTION_SOUTH)
+	{
+		gradient.setAttribute('y2', '100%');
+	}
+	else if (direction == mxConstants.DIRECTION_EAST)
+	{
+		gradient.setAttribute('x2', '100%');
+	}
+	else if (direction == mxConstants.DIRECTION_NORTH)
+	{
+		gradient.setAttribute('y1', '100%');
+	}
+	else if (direction == mxConstants.DIRECTION_WEST)
+	{
+		gradient.setAttribute('x1', '100%');
+	}
+	
+	var op = (alpha1 < 1) ? ';stop-opacity:' + alpha1 : '';
+	
+	var stop = this.createElement('stop');
+	stop.setAttribute('offset', '0%');
+	stop.setAttribute('style', 'stop-color:' + start + op);
+	gradient.appendChild(stop);
+	
+	op = (alpha2 < 1) ? ';stop-opacity:' + alpha2 : '';
+	
+	stop = this.createElement('stop');
+	stop.setAttribute('offset', '100%');
+	stop.setAttribute('style', 'stop-color:' + end + op);
+	gradient.appendChild(stop);
+	
+	return gradient;
+};
+
+/**
+ * Function: addNode
+ * 
+ * Private helper function to create SVG elements
+ */
+mxSvgCanvas2D.prototype.addNode = function(filled, stroked)
+{
+	var node = this.node;
+	var s = this.state;
+
+	if (node != null)
+	{
+		if (node.nodeName == 'path')
+		{
+			// Checks if the path is not empty
+			if (this.path != null && this.path.length > 0)
+			{
+				node.setAttribute('d', this.path.join(' '));
+			}
+			else
+			{
+				return;
+			}
+		}
+
+		if (filled && s.fillColor != null)
+		{
+			this.updateFill();
+		}
+		else if (!this.styleEnabled)
+		{
+			// Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=814952
+			if (node.nodeName == 'ellipse' && mxClient.IS_FF)
+			{
+				node.setAttribute('fill', 'transparent');
+			}
+			else
+			{
+				node.setAttribute('fill', 'none');
+			}
+			
+			// Sets the actual filled state for stroke tolerance
+			filled = false;
+		}
+		
+		if (stroked && s.strokeColor != null)
+		{
+			this.updateStroke();
+		}
+		else if (!this.styleEnabled)
+		{
+			node.setAttribute('stroke', 'none');
+		}
+		
+		if (s.transform != null && s.transform.length > 0)
+		{
+			node.setAttribute('transform', s.transform);
+		}
+		
+		if (s.shadow)
+		{
+			this.root.appendChild(this.createShadow(node));
+		}
+	
+		// Adds stroke tolerance
+		if (this.strokeTolerance > 0 && !filled)
+		{
+			this.root.appendChild(this.createTolerance(node));
+		}
+
+		// Adds pointer events
+		if (this.pointerEvents && (node.nodeName != 'path' ||
+			this.path[this.path.length - 1] == this.closeOp))
+		{
+			node.setAttribute('pointer-events', this.pointerEventsValue);
+		}
+		// Enables clicks for nodes inside a link element
+		else if (!this.pointerEvents && this.originalRoot == null)
+		{
+			node.setAttribute('pointer-events', 'none');
+		}
+		
+		// Removes invisible nodes from output if they don't handle events
+		if ((node.nodeName != 'rect' && node.nodeName != 'path' && node.nodeName != 'ellipse') ||
+			(node.getAttribute('fill') != 'none' && node.getAttribute('fill') != 'transparent') ||
+			node.getAttribute('stroke') != 'none' || node.getAttribute('pointer-events') != 'none')
+		{
+			// LATER: Update existing DOM for performance		
+			this.root.appendChild(node);
+		}
+		
+		this.node = null;
+	}
+};
+
+/**
+ * Function: updateFill
+ * 
+ * Transfers the stroke attributes from <state> to <node>.
+ */
+mxSvgCanvas2D.prototype.updateFill = function()
+{
+	var s = this.state;
+	
+	if (s.alpha < 1 || s.fillAlpha < 1)
+	{
+		this.node.setAttribute('fill-opacity', s.alpha * s.fillAlpha);
+	}
+	
+	if (s.fillColor != null)
+	{
+		if (s.gradientColor != null)
+		{
+			var id = this.getSvgGradient(s.fillColor, s.gradientColor, s.gradientFillAlpha, s.gradientAlpha, s.gradientDirection);
+			
+			if (!mxClient.IS_CHROME_APP && !mxClient.IS_IE && !mxClient.IS_IE11 &&
+				!mxClient.IS_EDGE && this.root.ownerDocument == document)
+			{
+				// Workaround for potential base tag and brackets must be escaped
+				var base = this.getBaseUrl().replace(/([\(\)])/g, '\\$1');
+				this.node.setAttribute('fill', 'url(' + base + '#' + id + ')');
+			}
+			else
+			{
+				this.node.setAttribute('fill', 'url(#' + id + ')');
+			}
+		}
+		else
+		{
+			this.node.setAttribute('fill', s.fillColor.toLowerCase());
+		}
+	}
+};
+
+/**
+ * Function: getCurrentStrokeWidth
+ * 
+ * Returns the current stroke width (>= 1), ie. max(1, this.format(this.state.strokeWidth * this.state.scale)).
+ */
+mxSvgCanvas2D.prototype.getCurrentStrokeWidth = function()
+{
+	return Math.max(1, this.format(this.state.strokeWidth * this.state.scale));
+};
+
+/**
+ * Function: updateStroke
+ * 
+ * Transfers the stroke attributes from <state> to <node>.
+ */
+mxSvgCanvas2D.prototype.updateStroke = function()
+{
+	var s = this.state;
+
+	this.node.setAttribute('stroke', s.strokeColor.toLowerCase());
+	
+	if (s.alpha < 1 || s.strokeAlpha < 1)
+	{
+		this.node.setAttribute('stroke-opacity', s.alpha * s.strokeAlpha);
+	}
+	
+	var sw = this.getCurrentStrokeWidth();
+	
+	if (sw != 1)
+	{
+		this.node.setAttribute('stroke-width', sw);
+	}
+	
+	if (this.node.nodeName == 'path')
+	{
+		this.updateStrokeAttributes();
+	}
+	
+	if (s.dashed)
+	{
+		this.node.setAttribute('stroke-dasharray', this.createDashPattern(
+			((s.fixDash) ? 1 : s.strokeWidth) * s.scale));
+	}
+};
+
+/**
+ * Function: updateStrokeAttributes
+ * 
+ * Transfers the stroke attributes from <state> to <node>.
+ */
+mxSvgCanvas2D.prototype.updateStrokeAttributes = function()
+{
+	var s = this.state;
+	
+	// Linejoin miter is default in SVG
+	if (s.lineJoin != null && s.lineJoin != 'miter')
+	{
+		this.node.setAttribute('stroke-linejoin', s.lineJoin);
+	}
+	
+	if (s.lineCap != null)
+	{
+		// flat is called butt in SVG
+		var value = s.lineCap;
+		
+		if (value == 'flat')
+		{
+			value = 'butt';
+		}
+		
+		// Linecap butt is default in SVG
+		if (value != 'butt')
+		{
+			this.node.setAttribute('stroke-linecap', value);
+		}
+	}
+	
+	// Miterlimit 10 is default in our document
+	if (s.miterLimit != null && (!this.styleEnabled || s.miterLimit != 10))
+	{
+		this.node.setAttribute('stroke-miterlimit', s.miterLimit);
+	}
+};
+
+/**
+ * Function: createDashPattern
+ * 
+ * Creates the SVG dash pattern for the given state.
+ */
+mxSvgCanvas2D.prototype.createDashPattern = function(scale)
+{
+	var pat = [];
+	
+	if (typeof(this.state.dashPattern) === 'string')
+	{
+		var dash = this.state.dashPattern.split(' ');
+		
+		if (dash.length > 0)
+		{
+			for (var i = 0; i < dash.length; i++)
+			{
+				pat[i] = Number(dash[i]) * scale;
+			}
+		}
+	}
+	
+	return pat.join(' ');
+};
+
+/**
+ * Function: createTolerance
+ * 
+ * Creates a hit detection tolerance shape for the given node.
+ */
+mxSvgCanvas2D.prototype.createTolerance = function(node)
+{
+	var tol = node.cloneNode(true);
+	var sw = parseFloat(tol.getAttribute('stroke-width') || 1) + this.strokeTolerance;
+	tol.setAttribute('pointer-events', 'stroke');
+	tol.setAttribute('visibility', 'hidden');
+	tol.removeAttribute('stroke-dasharray');
+	tol.setAttribute('stroke-width', sw);
+	tol.setAttribute('fill', 'none');
+	
+	// Workaround for Opera ignoring the visiblity attribute above while
+	// other browsers need a stroke color to perform the hit-detection but
+	// do not ignore the visibility attribute. Side-effect is that Opera's
+	// hit detection for horizontal/vertical edges seems to ignore the tol.
+	tol.setAttribute('stroke', (mxClient.IS_OT) ? 'none' : 'white');
+	
+	return tol;
+};
+
+/**
+ * Function: createShadow
+ * 
+ * Creates a shadow for the given node.
+ */
+mxSvgCanvas2D.prototype.createShadow = function(node)
+{
+	var shadow = node.cloneNode(true);
+	var s = this.state;
+
+	// Firefox uses transparent for no fill in ellipses
+	if (shadow.getAttribute('fill') != 'none' && (!mxClient.IS_FF || shadow.getAttribute('fill') != 'transparent'))
+	{
+		shadow.setAttribute('fill', s.shadowColor);
+	}
+	
+	if (shadow.getAttribute('stroke') != 'none')
+	{
+		shadow.setAttribute('stroke', s.shadowColor);
+	}
+
+	shadow.setAttribute('transform', 'translate(' + this.format(s.shadowDx * s.scale) +
+		',' + this.format(s.shadowDy * s.scale) + ')' + (s.transform || ''));
+	shadow.setAttribute('opacity', s.shadowAlpha);
+	
+	return shadow;
+};
+
+/**
+ * Function: setLink
+ * 
+ * Experimental implementation for hyperlinks.
+ */
+mxSvgCanvas2D.prototype.setLink = function(link)
+{
+	if (link == null)
+	{
+		this.root = this.originalRoot;
+	}
+	else
+	{
+		this.originalRoot = this.root;
+		
+		var node = this.createElement('a');
+		
+		// Workaround for implicit namespace handling in HTML5 export, IE adds NS1 namespace so use code below
+		// in all IE versions except quirks mode. KNOWN: Adds xlink namespace to each image tag in output.
+		if (node.setAttributeNS == null || (this.root.ownerDocument != document && document.documentMode == null))
+		{
+			node.setAttribute('xlink:href', link);
+		}
+		else
+		{
+			node.setAttributeNS(mxConstants.NS_XLINK, 'xlink:href', link);
+		}
+		
+		this.root.appendChild(node);
+		this.root = node;
+	}
+};
+
+/**
+ * Function: rotate
+ * 
+ * Sets the rotation of the canvas. Note that rotation cannot be concatenated.
+ */
+mxSvgCanvas2D.prototype.rotate = function(theta, flipH, flipV, cx, cy)
+{
+	if (theta != 0 || flipH || flipV)
+	{
+		var s = this.state;
+		cx += s.dx;
+		cy += s.dy;
+	
+		cx *= s.scale;
+		cy *= s.scale;
+
+		s.transform = s.transform || '';
+		
+		// This implementation uses custom scale/translate and built-in rotation
+		// Rotation state is part of the AffineTransform in state.transform
+		if (flipH && flipV)
+		{
+			theta += 180;
+		}
+		else if (flipH != flipV)
+		{
+			var tx = (flipH) ? cx : 0;
+			var sx = (flipH) ? -1 : 1;
+	
+			var ty = (flipV) ? cy : 0;
+			var sy = (flipV) ? -1 : 1;
+
+			s.transform += 'translate(' + this.format(tx) + ',' + this.format(ty) + ')' +
+				'scale(' + this.format(sx) + ',' + this.format(sy) + ')' +
+				'translate(' + this.format(-tx) + ',' + this.format(-ty) + ')';
+		}
+		
+		if (flipH ? !flipV : flipV)
+		{
+			theta *= -1;
+		}
+		
+		if (theta != 0)
+		{
+			s.transform += 'rotate(' + this.format(theta) + ',' + this.format(cx) + ',' + this.format(cy) + ')';
+		}
+		
+		s.rotation = s.rotation + theta;
+		s.rotationCx = cx;
+		s.rotationCy = cy;
+	}
+};
+
+/**
+ * Function: begin
+ * 
+ * Extends superclass to create path.
+ */
+mxSvgCanvas2D.prototype.begin = function()
+{
+	mxAbstractCanvas2D.prototype.begin.apply(this, arguments);
+	this.node = this.createElement('path');
+};
+
+/**
+ * Function: rect
+ * 
+ * Private helper function to create SVG elements
+ */
+mxSvgCanvas2D.prototype.rect = function(x, y, w, h)
+{
+	var s = this.state;
+	var n = this.createElement('rect');
+	n.setAttribute('x', this.format((x + s.dx) * s.scale));
+	n.setAttribute('y', this.format((y + s.dy) * s.scale));
+	n.setAttribute('width', this.format(w * s.scale));
+	n.setAttribute('height', this.format(h * s.scale));
+	
+	this.node = n;
+};
+
+/**
+ * Function: roundrect
+ * 
+ * Private helper function to create SVG elements
+ */
+mxSvgCanvas2D.prototype.roundrect = function(x, y, w, h, dx, dy)
+{
+	this.rect(x, y, w, h);
+	
+	if (dx > 0)
+	{
+		this.node.setAttribute('rx', this.format(dx * this.state.scale));
+	}
+	
+	if (dy > 0)
+	{
+		this.node.setAttribute('ry', this.format(dy * this.state.scale));
+	}
+};
+
+/**
+ * Function: ellipse
+ * 
+ * Private helper function to create SVG elements
+ */
+mxSvgCanvas2D.prototype.ellipse = function(x, y, w, h)
+{
+	var s = this.state;
+	var n = this.createElement('ellipse');
+	// No rounding for consistent output with 1.x
+	n.setAttribute('cx', Math.round((x + w / 2 + s.dx) * s.scale));
+	n.setAttribute('cy', Math.round((y + h / 2 + s.dy) * s.scale));
+	n.setAttribute('rx', w / 2 * s.scale);
+	n.setAttribute('ry', h / 2 * s.scale);
+	this.node = n;
+};
+
+/**
+ * Function: image
+ * 
+ * Private helper function to create SVG elements
+ */
+mxSvgCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV)
+{
+	src = this.converter.convert(src);
+	
+	// LATER: Add option for embedding images as base64.
+	aspect = (aspect != null) ? aspect : true;
+	flipH = (flipH != null) ? flipH : false;
+	flipV = (flipV != null) ? flipV : false;
+	
+	var s = this.state;
+	x += s.dx;
+	y += s.dy;
+	
+	var node = this.createElement('image');
+	node.setAttribute('x', this.format(x * s.scale) + this.imageOffset);
+	node.setAttribute('y', this.format(y * s.scale) + this.imageOffset);
+	node.setAttribute('width', this.format(w * s.scale));
+	node.setAttribute('height', this.format(h * s.scale));
+	
+	// Workaround for missing namespace support
+	if (node.setAttributeNS == null)
+	{
+		node.setAttribute('xlink:href', src);
+	}
+	else
+	{
+		node.setAttributeNS(mxConstants.NS_XLINK, 'xlink:href', src);
+	}
+	
+	if (!aspect)
+	{
+		node.setAttribute('preserveAspectRatio', 'none');
+	}
+
+	if (s.alpha < 1 || s.fillAlpha < 1)
+	{
+		node.setAttribute('opacity', s.alpha * s.fillAlpha);
+	}
+	
+	var tr = this.state.transform || '';
+	
+	if (flipH || flipV)
+	{
+		var sx = 1;
+		var sy = 1;
+		var dx = 0;
+		var dy = 0;
+		
+		if (flipH)
+		{
+			sx = -1;
+			dx = -w - 2 * x;
+		}
+		
+		if (flipV)
+		{
+			sy = -1;
+			dy = -h - 2 * y;
+		}
+		
+		// Adds image tansformation to existing transform
+		tr += 'scale(' + sx + ',' + sy + ')translate(' + (dx * s.scale) + ',' + (dy * s.scale) + ')';
+	}
+
+	if (tr.length > 0)
+	{
+		node.setAttribute('transform', tr);
+	}
+	
+	if (!this.pointerEvents)
+	{
+		node.setAttribute('pointer-events', 'none');
+	}
+	
+	this.root.appendChild(node);
+	
+	// Disables control-clicks on images in Firefox to open in new tab
+	// by putting a rect in the foreground that absorbs all events and
+	// disabling all pointer-events on the original image tag.
+	if (this.blockImagePointerEvents)
+	{
+		node.setAttribute('style', 'pointer-events:none');
+		
+		node = this.createElement('rect');
+		node.setAttribute('visibility', 'hidden');
+		node.setAttribute('pointer-events', 'fill');
+		node.setAttribute('x', this.format(x * s.scale));
+		node.setAttribute('y', this.format(y * s.scale));
+		node.setAttribute('width', this.format(w * s.scale));
+		node.setAttribute('height', this.format(h * s.scale));
+		this.root.appendChild(node);
+	}
+};
+
+/**
+ * Function: convertHtml
+ * 
+ * Converts the given HTML string to XHTML.
+ */
+mxSvgCanvas2D.prototype.convertHtml = function(val)
+{
+	if (this.useDomParser)
+	{
+		var doc = new DOMParser().parseFromString(val, 'text/html');
+
+		if (doc != null)
+		{
+			val = new XMLSerializer().serializeToString(doc.body);
+			
+			// Extracts body content from DOM
+			if (val.substring(0, 5) == '<body')
+			{
+				val = val.substring(val.indexOf('>', 5) + 1);
+			}
+			
+			if (val.substring(val.length - 7, val.length) == '</body>')
+			{
+				val = val.substring(0, val.length - 7);
+			}
+		}
+	}
+	else if (document.implementation != null && document.implementation.createDocument != null)
+	{
+		var xd = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null);
+		var xb = xd.createElement('body');
+		xd.documentElement.appendChild(xb);
+		
+		var div = document.createElement('div');
+		div.innerHTML = val;
+		var child = div.firstChild;
+		
+		while (child != null)
+		{
+			var next = child.nextSibling;
+			xb.appendChild(xd.adoptNode(child));
+			child = next;
+		}
+		
+		return xb.innerHTML;
+	}
+	else
+	{
+		var ta = document.createElement('textarea');
+		
+		// Handles special HTML entities < and > and double escaping
+		// and converts unclosed br, hr and img tags to XHTML
+		// LATER: Convert all unclosed tags
+		ta.innerHTML = val.replace(/&amp;/g, '&amp;amp;').
+			replace(/&#60;/g, '&amp;lt;').replace(/&#62;/g, '&amp;gt;').
+			replace(/&lt;/g, '&amp;lt;').replace(/&gt;/g, '&amp;gt;').
+			replace(/</g, '&lt;').replace(/>/g, '&gt;');
+		val = ta.value.replace(/&/g, '&amp;').replace(/&amp;lt;/g, '&lt;').
+			replace(/&amp;gt;/g, '&gt;').replace(/&amp;amp;/g, '&amp;').
+			replace(/<br>/g, '<br />').replace(/<hr>/g, '<hr />').
+			replace(/(<img[^>]+)>/gm, "$1 />");
+	}
+	
+	return val;
+};
+
+/**
+ * Function: createDiv
+ * 
+ * Private helper function to create SVG elements
+ */
+mxSvgCanvas2D.prototype.createDiv = function(str, align, valign, style, overflow)
+{
+	var s = this.state;
+
+	// Inline block for rendering HTML background over SVG in Safari
+	var lh = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (s.fontSize * mxConstants.LINE_HEIGHT) + 'px' :
+		(mxConstants.LINE_HEIGHT * this.lineHeightCorrection);
+	
+	style = 'display:inline-block;font-size:' + s.fontSize + 'px;font-family:' + s.fontFamily +
+		';color:' + s.fontColor + ';line-height:' + lh + ';' + style;
+
+	if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
+	{
+		style += 'font-weight:bold;';
+	}
+
+	if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
+	{
+		style += 'font-style:italic;';
+	}
+	
+	if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
+	{
+		style += 'text-decoration:underline;';
+	}
+	
+	if (align == mxConstants.ALIGN_CENTER)
+	{
+		style += 'text-align:center;';
+	}
+	else if (align == mxConstants.ALIGN_RIGHT)
+	{
+		style += 'text-align:right;';
+	}
+
+	var css = '';
+	
+	if (s.fontBackgroundColor != null)
+	{
+		css += 'background-color:' + s.fontBackgroundColor + ';';
+	}
+	
+	if (s.fontBorderColor != null)
+	{
+		css += 'border:1px solid ' + s.fontBorderColor + ';';
+	}
+	
+	var val = str;
+	
+	if (!mxUtils.isNode(val))
+	{
+		val = this.convertHtml(val);
+		
+		if (overflow != 'fill' && overflow != 'width')
+		{
+			// Inner div always needed to measure wrapped text
+			val = '<div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;' + css + '">' + val + '</div>';
+		}
+		else
+		{
+			style += css;
+		}
+	}
+
+	// Uses DOM API where available. This cannot be used in IE to avoid
+	// an opening and two (!) closing TBODY tags being added to tables.
+	if (!mxClient.IS_IE && document.createElementNS)
+	{
+		var div = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+		div.setAttribute('style', style);
+		
+		if (mxUtils.isNode(val))
+		{
+			// Creates a copy for export
+			if (this.root.ownerDocument != document)
+			{
+				div.appendChild(val.cloneNode(true));
+			}
+			else
+			{
+				div.appendChild(val);
+			}
+		}
+		else
+		{
+			div.innerHTML = val;
+		}
+		
+		return div;
+	}
+	else
+	{
+		// Serializes for export
+		if (mxUtils.isNode(val) && this.root.ownerDocument != document)
+		{
+			val = val.outerHTML;
+		}
+
+		// NOTE: FF 3.6 crashes if content CSS contains "height:100%"
+		return mxUtils.parseXml('<div xmlns="http://www.w3.org/1999/xhtml" style="' + style + 
+			'">' + val + '</div>').documentElement;
+	}
+};
+
+/**
+ * Invalidates the cached offset size for the given node.
+ */
+mxSvgCanvas2D.prototype.invalidateCachedOffsetSize = function(node)
+{
+	delete node.firstChild.mxCachedOffsetWidth;
+	delete node.firstChild.mxCachedFinalOffsetWidth;
+	delete node.firstChild.mxCachedFinalOffsetHeight;
+};
+
+/**
+ * Updates existing DOM nodes for text rendering. LATER: Merge common parts with text function below.
+ */
+mxSvgCanvas2D.prototype.updateText = function(x, y, w, h, align, valign, wrap, overflow, clip, rotation, node)
+{
+	if (node != null && node.firstChild != null && node.firstChild.firstChild != null &&
+		node.firstChild.firstChild.firstChild != null)
+	{
+		// Uses outer group for opacity and transforms to
+		// fix rendering order in Chrome
+		var group = node.firstChild;
+		var fo = group.firstChild;
+		var div = fo.firstChild;
+
+		rotation = (rotation != null) ? rotation : 0;
+		
+		var s = this.state;
+		x += s.dx;
+		y += s.dy;
+		
+		if (clip)
+		{
+			div.style.maxHeight = Math.round(h) + 'px';
+			div.style.maxWidth = Math.round(w) + 'px';
+		}
+		else if (overflow == 'fill')
+		{
+			div.style.width = Math.round(w + 1) + 'px';
+			div.style.height = Math.round(h + 1) + 'px';
+		}
+		else if (overflow == 'width')
+		{
+			div.style.width = Math.round(w + 1) + 'px';
+			
+			if (h > 0)
+			{
+				div.style.maxHeight = Math.round(h) + 'px';
+			}
+		}
+
+		if (wrap && w > 0)
+		{
+			div.style.width = Math.round(w + 1) + 'px';
+		}
+		
+		// Code that depends on the size which is computed after
+		// the element was added to the DOM.
+		var ow = 0;
+		var oh = 0;
+		
+		// Padding avoids clipping on border and wrapping for differing font metrics on platforms
+		var padX = 2;
+		var padY = 2;
+
+		var sizeDiv = div;
+		
+		if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV')
+		{
+			sizeDiv = sizeDiv.firstChild;
+		}
+		
+		var tmp = (group.mxCachedOffsetWidth != null) ? group.mxCachedOffsetWidth : sizeDiv.offsetWidth;
+		ow = tmp + padX;
+
+		// Recomputes the height of the element for wrapped width
+		if (wrap && overflow != 'fill')
+		{
+			if (clip)
+			{
+				ow = Math.min(ow, w);
+			}
+			
+			div.style.width = ow + 'px';
+		}
+		
+		ow = ((group.mxCachedFinalOffsetWidth != null) ? group.mxCachedFinalOffsetWidth :
+			sizeDiv.offsetWidth) + padX;
+		oh = ((group.mxCachedFinalOffsetHeight != null) ? group.mxCachedFinalOffsetHeight :
+			sizeDiv.offsetHeight) - 2;
+
+		if (clip)
+		{
+			oh = Math.min(oh, h);
+			ow = Math.min(ow, w);
+		}
+
+		if (overflow == 'width')
+		{
+			h = oh;
+		}
+		else if (overflow != 'fill')
+		{
+			w = ow;
+			h = oh;
+		}
+
+		var dx = 0;
+		var dy = 0;
+
+		if (align == mxConstants.ALIGN_CENTER)
+		{
+			dx -= w / 2;
+		}
+		else if (align == mxConstants.ALIGN_RIGHT)
+		{
+			dx -= w;
+		}
+		
+		x += dx;
+		
+		// FIXME: LINE_HEIGHT not ideal for all text sizes, fix for export
+		if (valign == mxConstants.ALIGN_MIDDLE)
+		{
+			dy -= h / 2;
+		}
+		else if (valign == mxConstants.ALIGN_BOTTOM)
+		{
+			dy -= h;
+		}
+		
+		// Workaround for rendering offsets
+		// TODO: Check if export needs these fixes, too
+		if (overflow != 'fill' && mxClient.IS_FF && mxClient.IS_WIN)
+		{
+			dy -= 2;
+		}
+		
+		y += dy;
+
+		var tr = (s.scale != 1) ? 'scale(' + s.scale + ')' : '';
+
+		if (s.rotation != 0 && this.rotateHtml)
+		{
+			tr += 'rotate(' + (s.rotation) + ',' + (w / 2) + ',' + (h / 2) + ')';
+			var pt = this.rotatePoint((x + w / 2) * s.scale, (y + h / 2) * s.scale,
+				s.rotation, s.rotationCx, s.rotationCy);
+			x = pt.x - w * s.scale / 2;
+			y = pt.y - h * s.scale / 2;
+		}
+		else
+		{
+			x *= s.scale;
+			y *= s.scale;
+		}
+
+		if (rotation != 0)
+		{
+			tr += 'rotate(' + (rotation) + ',' + (-dx) + ',' + (-dy) + ')';
+		}
+
+		group.setAttribute('transform', 'translate(' + Math.round(x) + ',' + Math.round(y) + ')' + tr);
+		fo.setAttribute('width', Math.round(Math.max(1, w)));
+		fo.setAttribute('height', Math.round(Math.max(1, h)));
+	}
+};
+
+/**
+ * Function: text
+ * 
+ * Paints the given text. Possible values for format are empty string for plain
+ * text and html for HTML markup. Note that HTML markup is only supported if
+ * foreignObject is supported and <foEnabled> is true. (This means IE9 and later
+ * does currently not support HTML text as part of shapes.)
+ */
+mxSvgCanvas2D.prototype.text = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir)
+{
+	if (this.textEnabled && str != null)
+	{
+		rotation = (rotation != null) ? rotation : 0;
+		
+		var s = this.state;
+		x += s.dx;
+		y += s.dy;
+		
+		if (this.foEnabled && format == 'html')
+		{
+			var style = 'vertical-align:top;';
+			
+			if (clip)
+			{
+				style += 'overflow:hidden;max-height:' + Math.round(h) + 'px;max-width:' + Math.round(w) + 'px;';
+			}
+			else if (overflow == 'fill')
+			{
+				style += 'width:' + Math.round(w + 1) + 'px;height:' + Math.round(h + 1) + 'px;overflow:hidden;';
+			}
+			else if (overflow == 'width')
+			{
+				style += 'width:' + Math.round(w + 1) + 'px;';
+				
+				if (h > 0)
+				{
+					style += 'max-height:' + Math.round(h) + 'px;overflow:hidden;';
+				}
+			}
+
+			if (wrap && w > 0)
+			{
+				style += 'width:' + Math.round(w + 1) + 'px;white-space:normal;word-wrap:' +
+					mxConstants.WORD_WRAP + ';';
+			}
+			else
+			{
+				style += 'white-space:nowrap;';
+			}
+			
+			// Uses outer group for opacity and transforms to
+			// fix rendering order in Chrome
+			var group = this.createElement('g');
+			
+			if (s.alpha < 1)
+			{
+				group.setAttribute('opacity', s.alpha);
+			}
+
+			var fo = this.createElement('foreignObject');
+			fo.setAttribute('style', 'overflow:visible;');
+			fo.setAttribute('pointer-events', 'all');
+			
+			var div = this.createDiv(str, align, valign, style, overflow);
+			
+			// Ignores invalid XHTML labels
+			if (div == null)
+			{
+				return;
+			}
+			else if (dir != null)
+			{
+				div.setAttribute('dir', dir);
+			}
+
+			group.appendChild(fo);
+			this.root.appendChild(group);
+			
+			// Code that depends on the size which is computed after
+			// the element was added to the DOM.
+			var ow = 0;
+			var oh = 0;
+			
+			// Padding avoids clipping on border and wrapping for differing font metrics on platforms
+			var padX = 2;
+			var padY = 2;
+
+			// NOTE: IE is always export as it does not support foreign objects
+			if (mxClient.IS_IE && (document.documentMode == 9 || !mxClient.IS_SVG))
+			{
+				// Handles non-standard namespace for getting size in IE
+				var clone = document.createElement('div');
+				
+				clone.style.cssText = div.getAttribute('style');
+				clone.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block';
+				clone.style.position = 'absolute';
+				clone.style.visibility = 'hidden';
+
+				// Inner DIV is needed for text measuring
+				var div2 = document.createElement('div');
+				div2.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block';
+				div2.style.wordWrap = mxConstants.WORD_WRAP;
+				div2.innerHTML = (mxUtils.isNode(str)) ? str.outerHTML : str;
+				clone.appendChild(div2);
+
+				document.body.appendChild(clone);
+
+				// Workaround for different box models
+				if (document.documentMode != 8 && document.documentMode != 9 && s.fontBorderColor != null)
+				{
+					padX += 2;
+					padY += 2;
+				}
+
+				if (wrap && w > 0)
+				{
+					var tmp = div2.offsetWidth;
+					
+					// Workaround for adding padding twice in IE8/IE9 standards mode if label is wrapped
+					var padDx = 0;
+					
+					// For export, if no wrapping occurs, we add a large padding to make
+					// sure there is no wrapping even if the text metrics are different.
+					// This adds support for text metrics on different operating systems.
+					// Disables wrapping if text is not wrapped for given width
+					if (!clip && wrap && w > 0 && this.root.ownerDocument != document && overflow != 'fill')
+					{
+						var ws = clone.style.whiteSpace;
+						div2.style.whiteSpace = 'nowrap';
+						
+						if (tmp < div2.offsetWidth)
+						{
+							clone.style.whiteSpace = ws;
+						}
+					}
+					
+					if (clip)
+					{
+						tmp = Math.min(tmp, w);
+					}
+					
+					clone.style.width = tmp + 'px';
+	
+					// Padding avoids clipping on border
+					ow = div2.offsetWidth + padX + padDx;
+					oh = div2.offsetHeight + padY;
+					
+					// Overrides the width of the DIV via XML DOM by using the
+					// clone DOM style, getting the CSS text for that and
+					// then setting that on the DIV via setAttribute
+					clone.style.display = 'inline-block';
+					clone.style.position = '';
+					clone.style.visibility = '';
+					clone.style.width = ow + 'px';
+					
+					div.setAttribute('style', clone.style.cssText);
+				}
+				else
+				{
+					// Padding avoids clipping on border
+					ow = div2.offsetWidth + padX;
+					oh = div2.offsetHeight + padY;
+				}
+
+				clone.parentNode.removeChild(clone);
+				fo.appendChild(div);
+			}
+			else
+			{
+				// Uses document for text measuring during export
+				if (this.root.ownerDocument != document)
+				{
+					div.style.visibility = 'hidden';
+					document.body.appendChild(div);
+				}
+				else
+				{
+					fo.appendChild(div);
+				}
+
+				var sizeDiv = div;
+				
+				if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV')
+				{
+					sizeDiv = sizeDiv.firstChild;
+					
+					if (wrap && div.style.wordWrap == 'break-word')
+					{
+						sizeDiv.style.width = '100%';
+					}
+				}
+				
+				var tmp = sizeDiv.offsetWidth;
+				
+				// Workaround for text measuring in hidden containers
+				if (tmp == 0 && div.parentNode == fo)
+				{
+					div.style.visibility = 'hidden';
+					document.body.appendChild(div);
+					
+					tmp = sizeDiv.offsetWidth;
+				}
+				
+				if (this.cacheOffsetSize)
+				{
+					group.mxCachedOffsetWidth = tmp;
+				}
+				
+				// Disables wrapping if text is not wrapped for given width
+				if (!clip && wrap && w > 0 && this.root.ownerDocument != document &&
+					overflow != 'fill' && overflow != 'width')
+				{
+					var ws = div.style.whiteSpace;
+					div.style.whiteSpace = 'nowrap';
+					
+					if (tmp < sizeDiv.offsetWidth)
+					{
+						div.style.whiteSpace = ws;
+					}
+				}
+
+				ow = tmp + padX - 1;
+
+				// Recomputes the height of the element for wrapped width
+				if (wrap && overflow != 'fill' && overflow != 'width')
+				{
+					if (clip)
+					{
+						ow = Math.min(ow, w);
+					}
+					
+					div.style.width = ow + 'px';
+				}
+
+				ow = sizeDiv.offsetWidth;
+				oh = sizeDiv.offsetHeight;
+				
+				if (this.cacheOffsetSize)
+				{
+					group.mxCachedFinalOffsetWidth = ow;
+					group.mxCachedFinalOffsetHeight = oh;
+				}
+
+				oh -= padY;
+				
+				if (div.parentNode != fo)
+				{
+					fo.appendChild(div);
+					div.style.visibility = '';
+				}
+			}
+
+			if (clip)
+			{
+				oh = Math.min(oh, h);
+				ow = Math.min(ow, w);
+			}
+
+			if (overflow == 'width')
+			{
+				h = oh;
+			}
+			else if (overflow != 'fill')
+			{
+				w = ow;
+				h = oh;
+			}
+
+			if (s.alpha < 1)
+			{
+				group.setAttribute('opacity', s.alpha);
+			}
+			
+			var dx = 0;
+			var dy = 0;
+
+			if (align == mxConstants.ALIGN_CENTER)
+			{
+				dx -= w / 2;
+			}
+			else if (align == mxConstants.ALIGN_RIGHT)
+			{
+				dx -= w;
+			}
+			
+			x += dx;
+			
+			// FIXME: LINE_HEIGHT not ideal for all text sizes, fix for export
+			if (valign == mxConstants.ALIGN_MIDDLE)
+			{
+				dy -= h / 2;
+			}
+			else if (valign == mxConstants.ALIGN_BOTTOM)
+			{
+				dy -= h;
+			}
+			
+			// Workaround for rendering offsets
+			// TODO: Check if export needs these fixes, too
+			//if (this.root.ownerDocument == document)
+			if (overflow != 'fill' && mxClient.IS_FF && mxClient.IS_WIN)
+			{
+				dy -= 2;
+			}
+			
+			y += dy;
+
+			var tr = (s.scale != 1) ? 'scale(' + s.scale + ')' : '';
+
+			if (s.rotation != 0 && this.rotateHtml)
+			{
+				tr += 'rotate(' + (s.rotation) + ',' + (w / 2) + ',' + (h / 2) + ')';
+				var pt = this.rotatePoint((x + w / 2) * s.scale, (y + h / 2) * s.scale,
+					s.rotation, s.rotationCx, s.rotationCy);
+				x = pt.x - w * s.scale / 2;
+				y = pt.y - h * s.scale / 2;
+			}
+			else
+			{
+				x *= s.scale;
+				y *= s.scale;
+			}
+
+			if (rotation != 0)
+			{
+				tr += 'rotate(' + (rotation) + ',' + (-dx) + ',' + (-dy) + ')';
+			}
+
+			group.setAttribute('transform', 'translate(' + (Math.round(x) + this.foOffset) + ',' +
+				(Math.round(y) + this.foOffset) + ')' + tr);
+			fo.setAttribute('width', Math.round(Math.max(1, w)));
+			fo.setAttribute('height', Math.round(Math.max(1, h)));
+			
+			// Adds alternate content if foreignObject not supported in viewer
+			if (this.root.ownerDocument != document)
+			{
+				var alt = this.createAlternateContent(fo, x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation);
+				
+				if (alt != null)
+				{
+					fo.setAttribute('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility');
+					var sw = this.createElement('switch');
+					sw.appendChild(fo);
+					sw.appendChild(alt);
+					group.appendChild(sw);
+				}
+			}
+		}
+		else
+		{
+			this.plainText(x, y, w, h, str, align, valign, wrap, overflow, clip, rotation, dir);
+		}
+	}
+};
+
+/**
+ * Function: createClip
+ * 
+ * Creates a clip for the given coordinates.
+ */
+mxSvgCanvas2D.prototype.createClip = function(x, y, w, h)
+{
+	x = Math.round(x);
+	y = Math.round(y);
+	w = Math.round(w);
+	h = Math.round(h);
+	
+	var id = 'mx-clip-' + x + '-' + y + '-' + w + '-' + h;
+
+	var counter = 0;
+	var tmp = id + '-' + counter;
+	
+	// Resolves ID conflicts
+	while (document.getElementById(tmp) != null)
+	{
+		tmp = id + '-' + (++counter);
+	}
+	
+	clip = this.createElement('clipPath');
+	clip.setAttribute('id', tmp);
+	
+	var rect = this.createElement('rect');
+	rect.setAttribute('x', x);
+	rect.setAttribute('y', y);
+	rect.setAttribute('width', w);
+	rect.setAttribute('height', h);
+		
+	clip.appendChild(rect);
+	
+	return clip;
+};
+
+/**
+ * Function: text
+ * 
+ * Paints the given text. Possible values for format are empty string for
+ * plain text and html for HTML markup.
+ */
+mxSvgCanvas2D.prototype.plainText = function(x, y, w, h, str, align, valign, wrap, overflow, clip, rotation, dir)
+{
+	rotation = (rotation != null) ? rotation : 0;
+	var s = this.state;
+	var size = s.fontSize;
+	var node = this.createElement('g');
+	var tr = s.transform || '';
+	this.updateFont(node);
+	
+	// Non-rotated text
+	if (rotation != 0)
+	{
+		tr += 'rotate(' + rotation  + ',' + this.format(x * s.scale) + ',' + this.format(y * s.scale) + ')';
+	}
+	
+	if (dir != null)
+	{
+		node.setAttribute('direction', dir);
+	}
+
+	if (clip && w > 0 && h > 0)
+	{
+		var cx = x;
+		var cy = y;
+		
+		if (align == mxConstants.ALIGN_CENTER)
+		{
+			cx -= w / 2;
+		}
+		else if (align == mxConstants.ALIGN_RIGHT)
+		{
+			cx -= w;
+		}
+		
+		if (overflow != 'fill')
+		{
+			if (valign == mxConstants.ALIGN_MIDDLE)
+			{
+				cy -= h / 2;
+			}
+			else if (valign == mxConstants.ALIGN_BOTTOM)
+			{
+				cy -= h;
+			}
+		}
+		
+		// LATER: Remove spacing from clip rectangle
+		var c = this.createClip(cx * s.scale - 2, cy * s.scale - 2, w * s.scale + 4, h * s.scale + 4);
+		
+		if (this.defs != null)
+		{
+			this.defs.appendChild(c);
+		}
+		else
+		{
+			// Makes sure clip is removed with referencing node
+			this.root.appendChild(c);
+		}
+		
+		if (!mxClient.IS_CHROME_APP && !mxClient.IS_IE && !mxClient.IS_IE11 &&
+			!mxClient.IS_EDGE && this.root.ownerDocument == document)
+		{
+			// Workaround for potential base tag
+			var base = this.getBaseUrl().replace(/([\(\)])/g, '\\$1');
+			node.setAttribute('clip-path', 'url(' + base + '#' + c.getAttribute('id') + ')');
+		}
+		else
+		{
+			node.setAttribute('clip-path', 'url(#' + c.getAttribute('id') + ')');
+		}
+	}
+
+	// Default is left
+	var anchor = (align == mxConstants.ALIGN_RIGHT) ? 'end' :
+					(align == mxConstants.ALIGN_CENTER) ? 'middle' :
+					'start';
+
+	// Text-anchor start is default in SVG
+	if (anchor != 'start')
+	{
+		node.setAttribute('text-anchor', anchor);
+	}
+	
+	if (!this.styleEnabled || size != mxConstants.DEFAULT_FONTSIZE)
+	{
+		node.setAttribute('font-size', (size * s.scale) + 'px');
+	}
+	
+	if (tr.length > 0)
+	{
+		node.setAttribute('transform', tr);
+	}
+	
+	if (s.alpha < 1)
+	{
+		node.setAttribute('opacity', s.alpha);
+	}
+	
+	var lines = str.split('\n');
+	var lh = Math.round(size * mxConstants.LINE_HEIGHT);
+	var textHeight = size + (lines.length - 1) * lh;
+
+	var cy = y + size - 1;
+
+	if (valign == mxConstants.ALIGN_MIDDLE)
+	{
+		if (overflow == 'fill')
+		{
+			cy -= h / 2;
+		}
+		else
+		{
+			var dy = ((this.matchHtmlAlignment && clip && h > 0) ? Math.min(textHeight, h) : textHeight) / 2;
+			cy -= dy + 1;
+		}
+	}
+	else if (valign == mxConstants.ALIGN_BOTTOM)
+	{
+		if (overflow == 'fill')
+		{
+			cy -= h;
+		}
+		else
+		{
+			var dy = (this.matchHtmlAlignment && clip && h > 0) ? Math.min(textHeight, h) : textHeight;
+			cy -= dy + 2;
+		}
+	}
+
+	for (var i = 0; i < lines.length; i++)
+	{
+		// Workaround for bounding box of empty lines and spaces
+		if (lines[i].length > 0 && mxUtils.trim(lines[i]).length > 0)
+		{
+			var text = this.createElement('text');
+			// LATER: Match horizontal HTML alignment
+			text.setAttribute('x', this.format(x * s.scale) + this.textOffset);
+			text.setAttribute('y', this.format(cy * s.scale) + this.textOffset);
+			
+			mxUtils.write(text, lines[i]);
+			node.appendChild(text);
+		}
+
+		cy += lh;
+	}
+
+	this.root.appendChild(node);
+	this.addTextBackground(node, str, x, y, w, (overflow == 'fill') ? h : textHeight, align, valign, overflow);
+};
+
+/**
+ * Function: updateFont
+ * 
+ * Updates the text properties for the given node. (NOTE: For this to work in
+ * IE, the given node must be a text or tspan element.)
+ */
+mxSvgCanvas2D.prototype.updateFont = function(node)
+{
+	var s = this.state;
+
+	node.setAttribute('fill', s.fontColor);
+	
+	if (!this.styleEnabled || s.fontFamily != mxConstants.DEFAULT_FONTFAMILY)
+	{
+		node.setAttribute('font-family', s.fontFamily);
+	}
+
+	if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
+	{
+		node.setAttribute('font-weight', 'bold');
+	}
+
+	if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
+	{
+		node.setAttribute('font-style', 'italic');
+	}
+	
+	if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
+	{
+		node.setAttribute('text-decoration', 'underline');
+	}
+};
+
+/**
+ * Function: addTextBackground
+ * 
+ * Background color and border
+ */
+mxSvgCanvas2D.prototype.addTextBackground = function(node, str, x, y, w, h, align, valign, overflow)
+{
+	var s = this.state;
+
+	if (s.fontBackgroundColor != null || s.fontBorderColor != null)
+	{
+		var bbox = null;
+		
+		if (overflow == 'fill' || overflow == 'width')
+		{
+			if (align == mxConstants.ALIGN_CENTER)
+			{
+				x -= w / 2;
+			}
+			else if (align == mxConstants.ALIGN_RIGHT)
+			{
+				x -= w;
+			}
+			
+			if (valign == mxConstants.ALIGN_MIDDLE)
+			{
+				y -= h / 2;
+			}
+			else if (valign == mxConstants.ALIGN_BOTTOM)
+			{
+				y -= h;
+			}
+			
+			bbox = new mxRectangle((x + 1) * s.scale, y * s.scale, (w - 2) * s.scale, (h + 2) * s.scale);
+		}
+		else if (node.getBBox != null && this.root.ownerDocument == document)
+		{
+			// Uses getBBox only if inside document for correct size
+			try
+			{
+				bbox = node.getBBox();
+				var ie = mxClient.IS_IE && mxClient.IS_SVG;
+				bbox = new mxRectangle(bbox.x, bbox.y + ((ie) ? 0 : 1), bbox.width, bbox.height + ((ie) ? 1 : 0));
+			}
+			catch (e)
+			{
+				// Ignores NS_ERROR_FAILURE in FF if container display is none.
+			}
+		}
+		else
+		{
+			// Computes size if not in document or no getBBox available
+			var div = document.createElement('div');
+
+			// Wrapping and clipping can be ignored here
+			div.style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (s.fontSize * mxConstants.LINE_HEIGHT) + 'px' : mxConstants.LINE_HEIGHT;
+			div.style.fontSize = s.fontSize + 'px';
+			div.style.fontFamily = s.fontFamily;
+			div.style.whiteSpace = 'nowrap';
+			div.style.position = 'absolute';
+			div.style.visibility = 'hidden';
+			div.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block';
+			div.style.zoom = '1';
+			
+			if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
+			{
+				div.style.fontWeight = 'bold';
+			}
+
+			if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
+			{
+				div.style.fontStyle = 'italic';
+			}
+			
+			str = mxUtils.htmlEntities(str, false);
+			div.innerHTML = str.replace(/\n/g, '<br/>');
+			
+			document.body.appendChild(div);
+			var w = div.offsetWidth;
+			var h = div.offsetHeight;
+			div.parentNode.removeChild(div);
+			
+			if (align == mxConstants.ALIGN_CENTER)
+			{
+				x -= w / 2;
+			}
+			else if (align == mxConstants.ALIGN_RIGHT)
+			{
+				x -= w;
+			}
+			
+			if (valign == mxConstants.ALIGN_MIDDLE)
+			{
+				y -= h / 2;
+			}
+			else if (valign == mxConstants.ALIGN_BOTTOM)
+			{
+				y -= h;
+			}
+			
+			bbox = new mxRectangle((x + 1) * s.scale, (y + 2) * s.scale, w * s.scale, (h + 1) * s.scale);
+		}
+		
+		if (bbox != null)
+		{
+			var n = this.createElement('rect');
+			n.setAttribute('fill', s.fontBackgroundColor || 'none');
+			n.setAttribute('stroke', s.fontBorderColor || 'none');
+			n.setAttribute('x', Math.floor(bbox.x - 1));
+			n.setAttribute('y', Math.floor(bbox.y - 1));
+			n.setAttribute('width', Math.ceil(bbox.width + 2));
+			n.setAttribute('height', Math.ceil(bbox.height));
+
+			var sw = (s.fontBorderColor != null) ? Math.max(1, this.format(s.scale)) : 0;
+			n.setAttribute('stroke-width', sw);
+			
+			// Workaround for crisp rendering - only required if not exporting
+			if (this.root.ownerDocument == document && mxUtils.mod(sw, 2) == 1)
+			{
+				n.setAttribute('transform', 'translate(0.5, 0.5)');
+			}
+			
+			node.insertBefore(n, node.firstChild);
+		}
+	}
+};
+
+/**
+ * Function: stroke
+ * 
+ * Paints the outline of the current path.
+ */
+mxSvgCanvas2D.prototype.stroke = function()
+{
+	this.addNode(false, true);
+};
+
+/**
+ * Function: fill
+ * 
+ * Fills the current path.
+ */
+mxSvgCanvas2D.prototype.fill = function()
+{
+	this.addNode(true, false);
+};
+
+/**
+ * Function: fillAndStroke
+ * 
+ * Fills and paints the outline of the current path.
+ */
+mxSvgCanvas2D.prototype.fillAndStroke = function()
+{
+	this.addNode(true, true);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ *
+ * Class: mxVmlCanvas2D
+ * 
+ * Implements a canvas to be used for rendering VML. Here is an example of implementing a
+ * fallback for SVG images which are not supported in VML-based browsers.
+ * 
+ * (code)
+ * var mxVmlCanvas2DImage = mxVmlCanvas2D.prototype.image;
+ * mxVmlCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV)
+ * {
+ *   if (src.substring(src.length - 4, src.length) == '.svg')
+ *   {
+ *     src = 'http://www.jgraph.com/images/mxgraph.gif';
+ *   }
+ *   
+ *   mxVmlCanvas2DImage.apply(this, arguments);
+ * };
+ * (end)
+ * 
+ * To disable anti-aliasing in the output, use the following code.
+ * 
+ * (code)
+ * document.createStyleSheet().cssText = mxClient.VML_PREFIX + '\\:*{antialias:false;)}';
+ * (end)
+ * 
+ * A description of the public API is available in <mxXmlCanvas2D>. Note that
+ * there is a known issue in VML where gradients are painted using the outer
+ * bounding box of rotated shapes, not the actual bounds of the shape. See
+ * also <text> for plain text label restrictions in shapes for VML.
+ */
+var mxVmlCanvas2D = function(root)
+{
+	mxAbstractCanvas2D.call(this);
+
+	/**
+	 * Variable: root
+	 * 
+	 * Reference to the container for the SVG content.
+	 */
+	this.root = root;
+};
+
+/**
+ * Extends mxAbstractCanvas2D
+ */
+mxUtils.extend(mxVmlCanvas2D, mxAbstractCanvas2D);
+
+/**
+ * Variable: path
+ * 
+ * Holds the current DOM node.
+ */
+mxVmlCanvas2D.prototype.node = null;
+
+/**
+ * Variable: textEnabled
+ * 
+ * Specifies if text output should be enabledetB. Default is true.
+ */
+mxVmlCanvas2D.prototype.textEnabled = true;
+
+/**
+ * Variable: moveOp
+ * 
+ * Contains the string used for moving in paths. Default is 'm'.
+ */
+mxVmlCanvas2D.prototype.moveOp = 'm';
+
+/**
+ * Variable: lineOp
+ * 
+ * Contains the string used for moving in paths. Default is 'l'.
+ */
+mxVmlCanvas2D.prototype.lineOp = 'l';
+
+/**
+ * Variable: curveOp
+ * 
+ * Contains the string used for bezier curves. Default is 'c'.
+ */
+mxVmlCanvas2D.prototype.curveOp = 'c';
+
+/**
+ * Variable: closeOp
+ * 
+ * Holds the operator for closing curves. Default is 'x e'.
+ */
+mxVmlCanvas2D.prototype.closeOp = 'x';
+
+/**
+ * Variable: rotatedHtmlBackground
+ * 
+ * Background color for rotated HTML. Default is ''. This can be set to eg.
+ * white to improve rendering of rotated text in VML for IE9.
+ */
+mxVmlCanvas2D.prototype.rotatedHtmlBackground = '';
+
+/**
+ * Variable: vmlScale
+ * 
+ * Specifies the scale used to draw VML shapes.
+ */
+mxVmlCanvas2D.prototype.vmlScale = 1;
+
+/**
+ * Function: createElement
+ * 
+ * Creates the given element using the document.
+ */
+mxVmlCanvas2D.prototype.createElement = function(name)
+{
+	return document.createElement(name);
+};
+
+/**
+ * Function: createVmlElement
+ * 
+ * Creates a new element using <createElement> and prefixes the given name with
+ * <mxClient.VML_PREFIX>.
+ */
+mxVmlCanvas2D.prototype.createVmlElement = function(name)
+{
+	return this.createElement(mxClient.VML_PREFIX + ':' + name);
+};
+
+/**
+ * Function: addNode
+ * 
+ * Adds the current node to the <root>.
+ */
+mxVmlCanvas2D.prototype.addNode = function(filled, stroked)
+{
+	var node = this.node;
+	var s = this.state;
+	
+	if (node != null)
+	{
+		if (node.nodeName == 'shape')
+		{
+			// Checks if the path is not empty
+			if (this.path != null && this.path.length > 0)
+			{
+				node.path = this.path.join(' ') + ' e';
+				node.style.width = this.root.style.width;
+				node.style.height = this.root.style.height;
+				node.coordsize = parseInt(node.style.width) + ' ' + parseInt(node.style.height);
+			}
+			else
+			{
+				return;
+			}
+		}
+
+		node.strokeweight = this.format(Math.max(1, s.strokeWidth * s.scale / this.vmlScale)) + 'px';
+		
+		if (s.shadow)
+		{
+			this.root.appendChild(this.createShadow(node,
+				filled && s.fillColor != null,
+				stroked && s.strokeColor != null));
+		}
+		
+		if (stroked && s.strokeColor != null)
+		{
+			node.stroked = 'true';
+			node.strokecolor = s.strokeColor;
+		}
+		else
+		{
+			node.stroked = 'false';
+		}
+
+		node.appendChild(this.createStroke());
+
+		if (filled && s.fillColor != null)
+		{
+			node.appendChild(this.createFill());
+		}
+		else if (this.pointerEvents && (node.nodeName != 'shape' ||
+			this.path[this.path.length - 1] == this.closeOp))
+		{
+			node.appendChild(this.createTransparentFill());
+		}
+		else
+		{
+			node.filled = 'false';
+		}
+
+		// LATER: Update existing DOM for performance
+		this.root.appendChild(node);
+	}
+};
+
+/**
+ * Function: createTransparentFill
+ * 
+ * Creates a transparent fill.
+ */
+mxVmlCanvas2D.prototype.createTransparentFill = function()
+{
+	var fill = this.createVmlElement('fill');
+	fill.src = mxClient.imageBasePath + '/transparent.gif';
+	fill.type = 'tile';
+	
+	return fill;
+};
+
+/**
+ * Function: createFill
+ * 
+ * Creates a fill for the current state.
+ */
+mxVmlCanvas2D.prototype.createFill = function()
+{
+	var s = this.state;
+	
+	// Gradients in foregrounds not supported because special gradients
+	// with bounds must be created for each element in graphics-canvases
+	var fill = this.createVmlElement('fill');
+	fill.color = s.fillColor;
+
+	if (s.gradientColor != null)
+	{
+		fill.type = 'gradient';
+		fill.method = 'none';
+		fill.color2 = s.gradientColor;
+		var angle = 180 - s.rotation;
+		
+		if (s.gradientDirection == mxConstants.DIRECTION_WEST)
+		{
+			angle -= 90 + ((this.root.style.flip == 'x') ? 180 : 0);
+		}
+		else if (s.gradientDirection == mxConstants.DIRECTION_EAST)
+		{
+			angle += 90 + ((this.root.style.flip == 'x') ? 180 : 0);
+		}
+		else if (s.gradientDirection == mxConstants.DIRECTION_NORTH)
+		{
+			angle -= 180 + ((this.root.style.flip == 'y') ? -180 : 0);
+		}
+		else
+		{
+			 angle += ((this.root.style.flip == 'y') ? -180 : 0);
+		}
+		
+		if (this.root.style.flip == 'x' || this.root.style.flip == 'y')
+		{
+			angle *= -1;
+		}
+
+		// LATER: Fix outer bounding box for rotated shapes used in VML.
+		fill.angle = mxUtils.mod(angle, 360);
+		fill.opacity = (s.alpha * s.gradientFillAlpha * 100) + '%';
+		fill.setAttribute(mxClient.OFFICE_PREFIX + ':opacity2', (s.alpha * s.gradientAlpha * 100) + '%');
+	}
+	else if (s.alpha < 1 || s.fillAlpha < 1)
+	{
+		fill.opacity = (s.alpha * s.fillAlpha * 100) + '%';			
+	}
+	
+	return fill;
+};
+/**
+ * Function: createStroke
+ * 
+ * Creates a fill for the current state.
+ */
+mxVmlCanvas2D.prototype.createStroke = function()
+{
+	var s = this.state;
+	var stroke = this.createVmlElement('stroke');
+	stroke.endcap = s.lineCap || 'flat';
+	stroke.joinstyle = s.lineJoin || 'miter';
+	stroke.miterlimit = s.miterLimit || '10';
+	
+	if (s.alpha < 1 || s.strokeAlpha < 1)
+	{
+		stroke.opacity = (s.alpha * s.strokeAlpha * 100) + '%';
+	}
+	
+	if (s.dashed)
+	{
+		stroke.dashstyle = this.getVmlDashStyle();
+	}
+	
+	return stroke;
+};
+
+/**
+ * Function: getVmlDashPattern
+ * 
+ * Returns a VML dash pattern for the current dashPattern.
+ * See http://msdn.microsoft.com/en-us/library/bb264085(v=vs.85).aspx
+ */
+mxVmlCanvas2D.prototype.getVmlDashStyle = function()
+{
+	var result = 'dash';
+	
+	if (typeof(this.state.dashPattern) === 'string')
+	{
+		var tok = this.state.dashPattern.split(' ');
+		
+		if (tok.length > 0 && tok[0] == 1)
+		{
+			result = '0 2';
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: createShadow
+ * 
+ * Creates a shadow for the given node.
+ */
+mxVmlCanvas2D.prototype.createShadow = function(node, filled, stroked)
+{
+	var s = this.state;
+	var rad = -s.rotation * (Math.PI / 180);
+	var cos = Math.cos(rad);
+	var sin = Math.sin(rad);
+
+	var dx = s.shadowDx * s.scale;
+	var dy = s.shadowDy * s.scale;
+
+	if (this.root.style.flip == 'x')
+	{
+		dx *= -1;
+	}
+	else if (this.root.style.flip == 'y')
+	{
+		dy *= -1;
+	}
+	
+	var shadow = node.cloneNode(true);
+	shadow.style.marginLeft = Math.round(dx * cos - dy * sin) + 'px';
+	shadow.style.marginTop = Math.round(dx * sin + dy * cos) + 'px';
+
+	// Workaround for wrong cloning in IE8 standards mode
+	if (document.documentMode == 8)
+	{
+		shadow.strokeweight = node.strokeweight;
+		
+		if (node.nodeName == 'shape')
+		{
+			shadow.path = this.path.join(' ') + ' e';
+			shadow.style.width = this.root.style.width;
+			shadow.style.height = this.root.style.height;
+			shadow.coordsize = parseInt(node.style.width) + ' ' + parseInt(node.style.height);
+		}
+	}
+	
+	if (stroked)
+	{
+		shadow.strokecolor = s.shadowColor;
+		shadow.appendChild(this.createShadowStroke());
+	}
+	else
+	{
+		shadow.stroked = 'false';
+	}
+	
+	if (filled)
+	{
+		shadow.appendChild(this.createShadowFill());
+	}
+	else
+	{
+		shadow.filled = 'false';
+	}
+	
+	return shadow;
+};
+
+/**
+ * Function: createShadowFill
+ * 
+ * Creates the fill for the shadow.
+ */
+mxVmlCanvas2D.prototype.createShadowFill = function()
+{
+	var fill = this.createVmlElement('fill');
+	fill.color = this.state.shadowColor;
+	fill.opacity = (this.state.alpha * this.state.shadowAlpha * 100) + '%';
+	
+	return fill;
+};
+
+/**
+ * Function: createShadowStroke
+ * 
+ * Creates the stroke for the shadow.
+ */
+mxVmlCanvas2D.prototype.createShadowStroke = function()
+{
+	var stroke = this.createStroke();
+	stroke.opacity = (this.state.alpha * this.state.shadowAlpha * 100) + '%';
+	
+	return stroke;
+};
+
+/**
+ * Function: rotate
+ * 
+ * Sets the rotation of the canvas. Note that rotation cannot be concatenated.
+ */
+mxVmlCanvas2D.prototype.rotate = function(theta, flipH, flipV, cx, cy)
+{
+	if (flipH && flipV)
+	{
+		theta += 180;
+	}
+	else if (flipH)
+	{
+		this.root.style.flip = 'x';
+	}
+	else if (flipV)
+	{
+		this.root.style.flip = 'y';
+	}
+
+	if (flipH ? !flipV : flipV)
+	{
+		theta *= -1;
+	}
+
+	this.root.style.rotation = theta;
+	this.state.rotation = this.state.rotation + theta;
+	this.state.rotationCx = cx;
+	this.state.rotationCy = cy;
+};
+
+/**
+ * Function: begin
+ * 
+ * Extends superclass to create path.
+ */
+mxVmlCanvas2D.prototype.begin = function()
+{
+	mxAbstractCanvas2D.prototype.begin.apply(this, arguments);
+	this.node = this.createVmlElement('shape');
+	this.node.style.position = 'absolute';
+};
+
+/**
+ * Function: quadTo
+ * 
+ * Replaces quadratic curve with bezier curve in VML.
+ */
+mxVmlCanvas2D.prototype.quadTo = function(x1, y1, x2, y2)
+{
+	var s = this.state;
+
+	var cpx0 = (this.lastX + s.dx) * s.scale;
+	var cpy0 = (this.lastY + s.dy) * s.scale;
+	var qpx1 = (x1 + s.dx) * s.scale;
+	var qpy1 = (y1 + s.dy) * s.scale;
+	var cpx3 = (x2 + s.dx) * s.scale;
+	var cpy3 = (y2 + s.dy) * s.scale;
+	
+	var cpx1 = cpx0 + 2/3 * (qpx1 - cpx0);
+	var cpy1 = cpy0 + 2/3 * (qpy1 - cpy0);
+	
+	var cpx2 = cpx3 + 2/3 * (qpx1 - cpx3);
+	var cpy2 = cpy3 + 2/3 * (qpy1 - cpy3);
+	
+	this.path.push('c ' + this.format(cpx1) + ' ' + this.format(cpy1) +
+			' ' + this.format(cpx2) + ' ' + this.format(cpy2) +
+			' ' + this.format(cpx3) + ' ' + this.format(cpy3));
+	this.lastX = (cpx3 / s.scale) - s.dx;
+	this.lastY = (cpy3 / s.scale) - s.dy;
+	
+};
+
+/**
+ * Function: createRect
+ * 
+ * Sets the glass gradient.
+ */
+mxVmlCanvas2D.prototype.createRect = function(nodeName, x, y, w, h)
+{
+	var s = this.state;
+	var n = this.createVmlElement(nodeName);
+	n.style.position = 'absolute';
+	n.style.left = this.format((x + s.dx) * s.scale) + 'px';
+	n.style.top = this.format((y + s.dy) * s.scale) + 'px';
+	n.style.width = this.format(w * s.scale) + 'px';
+	n.style.height = this.format(h * s.scale) + 'px';
+	
+	return n;
+};
+
+/**
+ * Function: rect
+ * 
+ * Sets the current path to a rectangle.
+ */
+mxVmlCanvas2D.prototype.rect = function(x, y, w, h)
+{
+	this.node = this.createRect('rect', x, y, w, h);
+};
+
+/**
+ * Function: roundrect
+ * 
+ * Sets the current path to a rounded rectangle.
+ */
+mxVmlCanvas2D.prototype.roundrect = function(x, y, w, h, dx, dy)
+{
+	this.node = this.createRect('roundrect', x, y, w, h);
+	// SetAttribute needed here for IE8
+	this.node.setAttribute('arcsize', Math.max(dx * 100 / w, dy * 100 / h) + '%');
+};
+
+/**
+ * Function: ellipse
+ * 
+ * Sets the current path to an ellipse.
+ */
+mxVmlCanvas2D.prototype.ellipse = function(x, y, w, h)
+{
+	this.node = this.createRect('oval', x, y, w, h);
+};
+
+/**
+ * Function: image
+ * 
+ * Paints an image.
+ */
+mxVmlCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV)
+{
+	var node = null;
+	
+	if (!aspect)
+	{
+		node = this.createRect('image', x, y, w, h);
+		node.src = src;
+	}
+	else
+	{
+		// Uses fill with aspect to avoid asynchronous update of size
+		node = this.createRect('rect', x, y, w, h);
+		node.stroked = 'false';
+		
+		// Handles image aspect via fill
+		var fill = this.createVmlElement('fill');
+		fill.aspect = (aspect) ? 'atmost' : 'ignore';
+		fill.rotate = 'true';
+		fill.type = 'frame';
+		fill.src = src;
+
+		node.appendChild(fill);
+	}
+	
+	if (flipH && flipV)
+	{
+		node.style.rotation = '180';
+	}
+	else if (flipH)
+	{
+		node.style.flip = 'x';
+	}
+	else if (flipV)
+	{
+		node.style.flip = 'y';
+	}
+	
+	if (this.state.alpha < 1 || this.state.fillAlpha < 1)
+	{
+		// KNOWN: Borders around transparent images in IE<9. Using fill.opacity
+		// fixes this problem by adding a white background in all IE versions.
+		node.style.filter += 'alpha(opacity=' + (this.state.alpha * this.state.fillAlpha * 100) + ')';
+	}
+
+	this.root.appendChild(node);
+};
+
+/**
+ * Function: createText
+ * 
+ * Creates the innermost element that contains the HTML text.
+ */
+mxVmlCanvas2D.prototype.createDiv = function(str, align, valign, overflow)
+{
+	var div = this.createElement('div');
+	var state = this.state;
+
+	var css = '';
+	
+	if (state.fontBackgroundColor != null)
+	{
+		css += 'background-color:' + state.fontBackgroundColor + ';';
+	}
+	
+	if (state.fontBorderColor != null)
+	{
+		css += 'border:1px solid ' + state.fontBorderColor + ';';
+	}
+	
+	if (mxUtils.isNode(str))
+	{
+		div.appendChild(str);
+	}
+	else
+	{
+		if (overflow != 'fill' && overflow != 'width')
+		{
+			var div2 = this.createElement('div');
+			div2.style.cssText = css;
+			div2.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block';
+			div2.style.zoom = '1';
+			div2.style.textDecoration = 'inherit';
+			div2.innerHTML = str;
+			div.appendChild(div2);
+		}
+		else
+		{
+			div.style.cssText = css;
+			div.innerHTML = str;
+		}
+	}
+	
+	var style = div.style;
+
+	style.fontSize = (state.fontSize / this.vmlScale) + 'px';
+	style.fontFamily = state.fontFamily;
+	style.color = state.fontColor;
+	style.verticalAlign = 'top';
+	style.textAlign = align || 'left';
+	style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (state.fontSize * mxConstants.LINE_HEIGHT / this.vmlScale) + 'px' : mxConstants.LINE_HEIGHT;
+
+	if ((state.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
+	{
+		style.fontWeight = 'bold';
+	}
+
+	if ((state.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
+	{
+		style.fontStyle = 'italic';
+	}
+	
+	if ((state.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
+	{
+		style.textDecoration = 'underline';
+	}
+	
+	return div;
+};
+
+/**
+ * Function: text
+ * 
+ * Paints the given text. Possible values for format are empty string for plain
+ * text and html for HTML markup. Clipping, text background and border are not
+ * supported for plain text in VML.
+ */
+mxVmlCanvas2D.prototype.text = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir)
+{
+	if (this.textEnabled && str != null)
+	{
+		var s = this.state;
+		
+		if (format == 'html')
+		{
+			if (s.rotation != null)
+			{
+				var pt = this.rotatePoint(x, y, s.rotation, s.rotationCx, s.rotationCy);
+				
+				x = pt.x;
+				y = pt.y;
+			}
+
+			if (document.documentMode == 8 && !mxClient.IS_EM)
+			{
+				x += s.dx;
+				y += s.dy;
+				
+				// Workaround for rendering offsets
+				if (overflow != 'fill' && valign == mxConstants.ALIGN_TOP)
+				{
+					y -= 1;
+				}
+			}
+			else
+			{
+				x *= s.scale;
+				y *= s.scale;
+			}
+
+			// Adds event transparency in IE8 standards without the transparent background
+			// filter which cannot be used due to bugs in the zoomed bounding box (too slow)
+			// FIXME: No event transparency if inside v:rect (ie part of shape)
+			// KNOWN: Offset wrong for rotated text with word that are longer than the wrapping
+			// width in IE8 because real width of text cannot be determined here.
+			// This should be fixed in mxText.updateBoundingBox by calling before this and
+			// passing the real width to this method if not clipped and wrapped.
+			var abs = (document.documentMode == 8 && !mxClient.IS_EM) ? this.createVmlElement('group') : this.createElement('div');
+			abs.style.position = 'absolute';
+			abs.style.display = 'inline';
+			abs.style.left = this.format(x) + 'px';
+			abs.style.top = this.format(y) + 'px';
+			abs.style.zoom = s.scale;
+
+			var box = this.createElement('div');
+			box.style.position = 'relative';
+			box.style.display = 'inline';
+			
+			var margin = mxUtils.getAlignmentAsPoint(align, valign);
+			var dx = margin.x;
+			var dy = margin.y;
+
+			var div = this.createDiv(str, align, valign, overflow);
+			var inner = this.createElement('div');
+			
+			if (dir != null)
+			{
+				div.setAttribute('dir', dir);
+			}
+
+			if (wrap && w > 0)
+			{
+				if (!clip)
+				{
+					div.style.width = Math.round(w) + 'px';
+				}
+				
+				div.style.wordWrap = mxConstants.WORD_WRAP;
+				div.style.whiteSpace = 'normal';
+				
+				// LATER: Check if other cases need to be handled
+				if (div.style.wordWrap == 'break-word')
+				{
+					var tmp = div;
+					
+					if (tmp.firstChild != null && tmp.firstChild.nodeName == 'DIV')
+					{
+						tmp.firstChild.style.width = '100%';
+					}
+				}
+			}
+			else
+			{
+				div.style.whiteSpace = 'nowrap';
+			}
+			
+			var rot = s.rotation + (rotation || 0);
+			
+			if (this.rotateHtml && rot != 0)
+			{
+				inner.style.display = 'inline';
+				inner.style.zoom = '1';
+				inner.appendChild(div);
+
+				// Box not needed for rendering in IE8 standards
+				if (document.documentMode == 8 && !mxClient.IS_EM && this.root.nodeName != 'DIV')
+				{
+					box.appendChild(inner);
+					abs.appendChild(box);
+				}
+				else
+				{
+					abs.appendChild(inner);
+				}
+			}
+			else if (document.documentMode == 8 && !mxClient.IS_EM)
+			{
+				box.appendChild(div);
+				abs.appendChild(box);
+			}
+			else
+			{
+				div.style.display = 'inline';
+				abs.appendChild(div);
+			}
+			
+			// Inserts the node into the DOM
+			if (this.root.nodeName != 'DIV')
+			{
+				// Rectangle to fix position in group
+				var rect = this.createVmlElement('rect');
+				rect.stroked = 'false';
+				rect.filled = 'false';
+
+				rect.appendChild(abs);
+				this.root.appendChild(rect);
+			}
+			else
+			{
+				this.root.appendChild(abs);
+			}
+			
+			if (clip)
+			{
+				div.style.overflow = 'hidden';
+				div.style.width = Math.round(w) + 'px';
+				
+				if (!mxClient.IS_QUIRKS)
+				{
+					div.style.maxHeight = Math.round(h) + 'px';
+				}
+			}
+			else if (overflow == 'fill')
+			{
+				// KNOWN: Affects horizontal alignment in quirks
+				// but fill should only be used with align=left
+				div.style.overflow = 'hidden';
+				div.style.width = (Math.max(0, w) + 1) + 'px';
+				div.style.height = (Math.max(0, h) + 1) + 'px';
+			}
+			else if (overflow == 'width')
+			{
+				// KNOWN: Affects horizontal alignment in quirks
+				// but fill should only be used with align=left
+				div.style.overflow = 'hidden';
+				div.style.width = (Math.max(0, w) + 1) + 'px';
+				div.style.maxHeight = (Math.max(0, h) + 1) + 'px';
+			}
+			
+			if (this.rotateHtml && rot != 0)
+			{
+				var rad = rot * (Math.PI / 180);
+				
+				// Precalculate cos and sin for the rotation
+				var real_cos = parseFloat(parseFloat(Math.cos(rad)).toFixed(8));
+				var real_sin = parseFloat(parseFloat(Math.sin(-rad)).toFixed(8));
+
+				rad %= 2 * Math.PI;
+				if (rad < 0) rad += 2 * Math.PI;
+				rad %= Math.PI;
+				if (rad > Math.PI / 2) rad = Math.PI - rad;
+				
+				var cos = Math.cos(rad);
+				var sin = Math.sin(rad);
+
+				// Adds div to document to measure size
+				if (document.documentMode == 8 && !mxClient.IS_EM)
+				{
+					div.style.display = 'inline-block';
+					inner.style.display = 'inline-block';
+					box.style.display = 'inline-block';
+				}
+				
+				div.style.visibility = 'hidden';
+				div.style.position = 'absolute';
+				document.body.appendChild(div);
+				
+				var sizeDiv = div;
+				
+				if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV')
+				{
+					sizeDiv = sizeDiv.firstChild;
+				}
+				
+				var tmp = sizeDiv.offsetWidth + 3;
+				var oh = sizeDiv.offsetHeight;
+				
+				if (clip)
+				{
+					w = Math.min(w, tmp);
+					oh = Math.min(oh, h);
+				}
+				else
+				{
+					w = tmp;
+				}
+
+				// Handles words that are longer than the given wrapping width
+				if (wrap)
+				{
+					div.style.width = w + 'px';
+				}
+				
+				// Simulates max-height in quirks
+				if (mxClient.IS_QUIRKS && (clip || overflow == 'width') && oh > h)
+				{
+					oh = h;
+					
+					// Quirks does not support maxHeight
+					div.style.height = oh + 'px';
+				}
+				
+				h = oh;
+
+				var top_fix = (h - h * cos + w * -sin) / 2 - real_sin * w * (dx + 0.5) + real_cos * h * (dy + 0.5);
+				var left_fix = (w - w * cos + h * -sin) / 2 + real_cos * w * (dx + 0.5) + real_sin * h * (dy + 0.5);
+
+				if (abs.nodeName == 'group' && this.root.nodeName == 'DIV')
+				{
+					// Workaround for bug where group gets moved away if left and top are non-zero in IE8 standards
+					var pos = this.createElement('div');
+					pos.style.display = 'inline-block';
+					pos.style.position = 'absolute';
+					pos.style.left = this.format(x + (left_fix - w / 2) * s.scale) + 'px';
+					pos.style.top = this.format(y + (top_fix - h / 2) * s.scale) + 'px';
+					
+					abs.parentNode.appendChild(pos);
+					pos.appendChild(abs);
+				}
+				else
+				{
+					var sc = (document.documentMode == 8 && !mxClient.IS_EM) ? 1 : s.scale;
+					
+					abs.style.left = this.format(x + (left_fix - w / 2) * sc) + 'px';
+					abs.style.top = this.format(y + (top_fix - h / 2) * sc) + 'px';
+				}
+				
+				// KNOWN: Rotated text rendering quality is bad for IE9 quirks
+				inner.style.filter = "progid:DXImageTransform.Microsoft.Matrix(M11="+real_cos+", M12="+
+					real_sin+", M21="+(-real_sin)+", M22="+real_cos+", sizingMethod='auto expand')";
+				inner.style.backgroundColor = this.rotatedHtmlBackground;
+				
+				if (this.state.alpha < 1)
+				{
+					inner.style.filter += 'alpha(opacity=' + (this.state.alpha * 100) + ')';
+				}
+
+				// Restore parent node for DIV
+				inner.appendChild(div);
+				div.style.position = '';
+				div.style.visibility = '';
+			}
+			else if (document.documentMode != 8 || mxClient.IS_EM)
+			{
+				div.style.verticalAlign = 'top';
+				
+				if (this.state.alpha < 1)
+				{
+					abs.style.filter = 'alpha(opacity=' + (this.state.alpha * 100) + ')';
+				}
+				
+				// Adds div to document to measure size
+				var divParent = div.parentNode;
+				div.style.visibility = 'hidden';
+				document.body.appendChild(div);
+				
+				w = div.offsetWidth;
+				var oh = div.offsetHeight;
+				
+				// Simulates max-height in quirks
+				if (mxClient.IS_QUIRKS && clip && oh > h)
+				{
+					oh = h;
+					
+					// Quirks does not support maxHeight
+					div.style.height = oh + 'px';
+				}
+				
+				h = oh;
+				
+				div.style.visibility = '';
+				divParent.appendChild(div);
+				
+				abs.style.left = this.format(x + w * dx * this.state.scale) + 'px';
+				abs.style.top = this.format(y + h * dy * this.state.scale) + 'px';
+			}
+			else
+			{
+				if (this.state.alpha < 1)
+				{
+					div.style.filter = 'alpha(opacity=' + (this.state.alpha * 100) + ')';
+				}
+				
+				// Faster rendering in IE8 without offsetWidth/Height
+				box.style.left = (dx * 100) + '%';
+				box.style.top = (dy * 100) + '%';
+			}
+		}
+		else
+		{
+			this.plainText(x, y, w, h, mxUtils.htmlEntities(str, false), align, valign, wrap, format, overflow, clip, rotation, dir);
+		}
+	}
+};
+
+/**
+ * Function: plainText
+ * 
+ * Paints the outline of the current path.
+ */
+mxVmlCanvas2D.prototype.plainText = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir)
+{
+	// TextDirection is ignored since this code is not used (format is always HTML in the text function)
+	var s = this.state;
+	x = (x + s.dx) * s.scale;
+	y = (y + s.dy) * s.scale;
+	
+	var node = this.createVmlElement('shape');
+	node.style.width = '1px';
+	node.style.height = '1px';
+	node.stroked = 'false';
+
+	var fill = this.createVmlElement('fill');
+	fill.color = s.fontColor;
+	fill.opacity = (s.alpha * 100) + '%';
+	node.appendChild(fill);
+	
+	var path = this.createVmlElement('path');
+	path.textpathok = 'true';
+	path.v = 'm ' + this.format(0) + ' ' + this.format(0) + ' l ' + this.format(1) + ' ' + this.format(0);
+	
+	node.appendChild(path);
+	
+	// KNOWN: Font family and text decoration ignored
+	var tp = this.createVmlElement('textpath');
+	tp.style.cssText = 'v-text-align:' + align;
+	tp.style.align = align;
+	tp.style.fontFamily = s.fontFamily;
+	tp.string = str;
+	tp.on = 'true';
+	
+	// Scale via fontsize instead of node.style.zoom for correct offsets in IE8
+	var size = s.fontSize * s.scale / this.vmlScale;
+	tp.style.fontSize = size + 'px';
+	
+	// Bold
+	if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
+	{
+		tp.style.fontWeight = 'bold';
+	}
+	
+	// Italic
+	if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
+	{
+		tp.style.fontStyle = 'italic';
+	}
+
+	// Underline
+	if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
+	{
+		tp.style.textDecoration = 'underline';
+	}
+
+	var lines = str.split('\n');
+	var textHeight = size + (lines.length - 1) * size * mxConstants.LINE_HEIGHT;
+	var dx = 0;
+	var dy = 0;
+
+	if (valign == mxConstants.ALIGN_BOTTOM)
+	{
+		dy = - textHeight / 2;
+	}
+	else if (valign != mxConstants.ALIGN_MIDDLE) // top
+	{
+		dy = textHeight / 2;
+	}
+
+	if (rotation != null)
+	{
+		node.style.rotation = rotation;
+		var rad = rotation * (Math.PI / 180);
+		dx = Math.sin(rad) * dy;
+		dy = Math.cos(rad) * dy;
+	}
+
+	// FIXME: Clipping is relative to bounding box
+	/*if (clip)
+	{
+		node.style.clip = 'rect(0px ' + this.format(w) + 'px ' + this.format(h) + 'px 0px)';
+	}*/
+	
+	node.appendChild(tp);
+	node.style.left = this.format(x - dx) + 'px';
+	node.style.top = this.format(y + dy) + 'px';
+	
+	this.root.appendChild(node);
+};
+
+/**
+ * Function: stroke
+ * 
+ * Paints the outline of the current path.
+ */
+mxVmlCanvas2D.prototype.stroke = function()
+{
+	this.addNode(false, true);
+};
+
+/**
+ * Function: fill
+ * 
+ * Fills the current path.
+ */
+mxVmlCanvas2D.prototype.fill = function()
+{
+	this.addNode(true, false);
+};
+
+/**
+ * Function: fillAndStroke
+ * 
+ * Fills and paints the outline of the current path.
+ */
+mxVmlCanvas2D.prototype.fillAndStroke = function()
+{
+	this.addNode(true, true);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGuide
+ *
+ * Implements the alignment of selection cells to other cells in the graph.
+ * 
+ * Constructor: mxGuide
+ * 
+ * Constructs a new guide object.
+ */
+function mxGuide(graph, states)
+{
+	this.graph = graph;
+	this.setStates(states);
+};
+
+/**
+ * Variable: graph
+ *
+ * Reference to the enclosing <mxGraph> instance.
+ */
+mxGuide.prototype.graph = null;
+
+/**
+ * Variable: states
+ * 
+ * Contains the <mxCellStates> that are used for alignment.
+ */
+mxGuide.prototype.states = null;
+
+/**
+ * Variable: horizontal
+ *
+ * Specifies if horizontal guides are enabled. Default is true.
+ */
+mxGuide.prototype.horizontal = true;
+
+/**
+ * Variable: vertical
+ *
+ * Specifies if vertical guides are enabled. Default is true.
+ */
+mxGuide.prototype.vertical = true;
+
+/**
+ * Variable: vertical
+ *
+ * Holds the <mxShape> for the horizontal guide.
+ */
+mxGuide.prototype.guideX = null;
+
+/**
+ * Variable: vertical
+ *
+ * Holds the <mxShape> for the vertical guide.
+ */
+mxGuide.prototype.guideY = null;
+
+/**
+ * Function: setStates
+ * 
+ * Sets the <mxCellStates> that should be used for alignment.
+ */
+mxGuide.prototype.setStates = function(states)
+{
+	this.states = states;
+};
+
+/**
+ * Function: isEnabledForEvent
+ * 
+ * Returns true if the guide should be enabled for the given native event. This
+ * implementation always returns true.
+ */
+mxGuide.prototype.isEnabledForEvent = function(evt)
+{
+	return true;
+};
+
+/**
+ * Function: getGuideTolerance
+ * 
+ * Returns the tolerance for the guides. Default value is gridSize / 2.
+ */
+mxGuide.prototype.getGuideTolerance = function()
+{
+	return this.graph.gridSize / 2;
+};
+
+/**
+ * Function: createGuideShape
+ * 
+ * Returns the mxShape to be used for painting the respective guide. This
+ * implementation returns a new, dashed and crisp <mxPolyline> using
+ * <mxConstants.GUIDE_COLOR> and <mxConstants.GUIDE_STROKEWIDTH> as the format.
+ * 
+ * Parameters:
+ * 
+ * horizontal - Boolean that specifies which guide should be created.
+ */
+mxGuide.prototype.createGuideShape = function(horizontal)
+{
+	var guide = new mxPolyline([], mxConstants.GUIDE_COLOR, mxConstants.GUIDE_STROKEWIDTH);
+	guide.isDashed = true;
+	
+	return guide;
+};
+
+/**
+ * Function: move
+ * 
+ * Moves the <bounds> by the given <mxPoint> and returnt the snapped point.
+ */
+mxGuide.prototype.move = function(bounds, delta, gridEnabled)
+{
+	if (this.states != null && (this.horizontal || this.vertical) && bounds != null && delta != null)
+	{
+		var trx = this.graph.getView().translate;
+		var scale = this.graph.getView().scale;
+		var dx = delta.x;
+		var dy = delta.y;
+		
+		var overrideX = false;
+		var stateX = null;
+		var valueX = null;
+		var overrideY = false;
+		var stateY = null;
+		var valueY = null;
+		
+		var tt = this.getGuideTolerance();
+		var ttX = tt;
+		var ttY = tt;
+		
+		var b = bounds.clone();
+		b.x += delta.x;
+		b.y += delta.y;
+		
+		var left = b.x;
+		var right = b.x + b.width;
+		var center = b.getCenterX();
+		var top = b.y;
+		var bottom = b.y + b.height;
+		var middle = b.getCenterY();
+	
+		// Snaps the left, center and right to the given x-coordinate
+		function snapX(x, state)
+		{
+			x += this.graph.panDx;
+			var override = false;
+			
+			if (Math.abs(x - center) < ttX)
+			{
+				dx = x - bounds.getCenterX();
+				ttX = Math.abs(x - center);
+				override = true;
+			}
+			else if (Math.abs(x - left) < ttX)
+			{
+				dx = x - bounds.x;
+				ttX = Math.abs(x - left);
+				override = true;
+			}
+			else if (Math.abs(x - right) < ttX)
+			{
+				dx = x - bounds.x - bounds.width;
+				ttX = Math.abs(x - right);
+				override = true;
+			}
+			
+			if (override)
+			{
+				stateX = state;
+				valueX = Math.round(x - this.graph.panDx);
+				
+				if (this.guideX == null)
+				{
+					this.guideX = this.createGuideShape(true);
+					
+					// Makes sure to use either VML or SVG shapes in order to implement
+					// event-transparency on the background area of the rectangle since
+					// HTML shapes do not let mouseevents through even when transparent
+					this.guideX.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+						mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+					this.guideX.pointerEvents = false;
+					this.guideX.init(this.graph.getView().getOverlayPane());
+				}
+			}
+			
+			overrideX = overrideX || override;
+		};
+		
+		// Snaps the top, middle or bottom to the given y-coordinate
+		function snapY(y)
+		{
+			y += this.graph.panDy;
+			var override = false;
+			
+			if (Math.abs(y - middle) < ttY)
+			{
+				dy = y - bounds.getCenterY();
+				ttY = Math.abs(y -  middle);
+				override = true;
+			}
+			else if (Math.abs(y - top) < ttY)
+			{
+				dy = y - bounds.y;
+				ttY = Math.abs(y - top);
+				override = true;
+			}
+			else if (Math.abs(y - bottom) < ttY)
+			{
+				dy = y - bounds.y - bounds.height;
+				ttY = Math.abs(y - bottom);
+				override = true;
+			}
+			
+			if (override)
+			{
+				stateY = state;
+				valueY = Math.round(y - this.graph.panDy);
+				
+				if (this.guideY == null)
+				{
+					this.guideY = this.createGuideShape(false);
+					
+					// Makes sure to use either VML or SVG shapes in order to implement
+					// event-transparency on the background area of the rectangle since
+					// HTML shapes do not let mouseevents through even when transparent
+					this.guideY.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+						mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+					this.guideY.pointerEvents = false;
+					this.guideY.init(this.graph.getView().getOverlayPane());
+				}
+			}
+			
+			overrideY = overrideY || override;
+		};
+		
+		for (var i = 0; i < this.states.length; i++)
+		{
+			var state =  this.states[i];
+			
+			if (state != null)
+			{
+				// Align x
+				if (this.horizontal)
+				{
+					snapX.call(this, state.getCenterX(), state);
+					snapX.call(this, state.x, state);
+					snapX.call(this, state.x + state.width, state);
+				}
+	
+				// Align y
+				if (this.vertical)
+				{
+					snapY.call(this, state.getCenterY(), state);
+					snapY.call(this, state.y, state);
+					snapY.call(this, state.y + state.height, state);
+				}
+			}
+		}
+
+		// Moves cells that are off-grid back to the grid on move
+		if (gridEnabled)
+		{
+			if (!overrideX)
+			{
+				var tx = bounds.x - (this.graph.snap(bounds.x /
+					scale - trx.x) + trx.x) * scale;
+				dx = this.graph.snap(dx / scale) * scale - tx;
+			}
+			
+			if (!overrideY)
+			{
+				var ty = bounds.y - (this.graph.snap(bounds.y /
+					scale - trx.y) + trx.y) * scale;
+				dy = this.graph.snap(dy / scale) * scale - ty;
+			}
+		}
+		
+		// Redraws the guides
+		var c = this.graph.container;
+		
+		if (!overrideX && this.guideX != null)
+		{
+			this.guideX.node.style.visibility = 'hidden';
+		}
+		else if (this.guideX != null)
+		{
+			if (stateX != null && bounds != null)
+			{
+				minY = Math.min(bounds.y + dy - this.graph.panDy, stateX.y);
+				maxY = Math.max(bounds.y + bounds.height + dy - this.graph.panDy, stateX.y + stateX.height);
+			}
+			
+			if (minY != null && maxY != null)
+			{
+				this.guideX.points = [new mxPoint(valueX, minY), new mxPoint(valueX, maxY)];
+			}
+			else
+			{
+				this.guideX.points = [new mxPoint(valueX, -this.graph.panDy), new mxPoint(valueX, c.scrollHeight - 3 - this.graph.panDy)];
+			}
+			
+			this.guideX.stroke = this.getGuideColor(stateX, true);
+			this.guideX.node.style.visibility = 'visible';
+			this.guideX.redraw();
+		}
+		
+		if (!overrideY && this.guideY != null)
+		{
+			this.guideY.node.style.visibility = 'hidden';
+		}
+		else if (this.guideY != null)
+		{
+			if (stateY != null && bounds != null)
+			{
+				minX = Math.min(bounds.x + dx - this.graph.panDx, stateY.x);
+				maxX = Math.max(bounds.x + bounds.width + dx - this.graph.panDx, stateY.x + stateY.width);
+			}
+			
+			if (minX != null && maxX != null)
+			{
+				this.guideY.points = [new mxPoint(minX, valueY), new mxPoint(maxX, valueY)];
+			}
+			else
+			{
+				this.guideY.points = [new mxPoint(-this.graph.panDx, valueY), new mxPoint(c.scrollWidth - 3 - this.graph.panDx, valueY)];
+			}
+			
+			this.guideY.stroke = this.getGuideColor(stateY, false);
+			this.guideY.node.style.visibility = 'visible';
+			this.guideY.redraw();
+		}
+		
+		delta = new mxPoint(dx, dy);
+	}
+	
+	return delta;
+};
+
+/**
+ * Function: hide
+ * 
+ * Hides all current guides.
+ */
+mxGuide.prototype.getGuideColor = function(state, horizontal)
+{
+	return mxConstants.GUIDE_COLOR;
+};
+
+/**
+ * Function: hide
+ * 
+ * Hides all current guides.
+ */
+mxGuide.prototype.hide = function()
+{
+	this.setVisible(false);
+};
+
+/**
+ * Function: setVisible
+ * 
+ * Shows or hides the current guides.
+ */
+mxGuide.prototype.setVisible = function(visible)
+{
+	if (this.guideX != null)
+	{
+		this.guideX.node.style.visibility = (visible) ? 'visible' : 'hidden';
+	}
+	
+	if (this.guideY != null)
+	{
+		this.guideY.node.style.visibility = (visible) ? 'visible' : 'hidden';
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys all resources that this object uses.
+ */
+mxGuide.prototype.destroy = function()
+{
+	if (this.guideX != null)
+	{
+		this.guideX.destroy();
+		this.guideX = null;
+	}
+	
+	if (this.guideY != null)
+	{
+		this.guideY.destroy();
+		this.guideY = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxStencil
+ *
+ * Implements a generic shape which is based on a XML node as a description.
+ * 
+ * shape:
+ * 
+ * The outer element is *shape*, that has attributes:
+ * 
+ * - "name", string, required. The stencil name that uniquely identifies the shape.
+ * - "w" and "h" are optional decimal view bounds. This defines your co-ordinate
+ * system for the graphics operations in the shape. The default is 100,100.
+ * - "aspect", optional string. Either "variable", the default, or "fixed". Fixed
+ * means always render the shape with the aspect ratio defined by the ratio w/h.
+ * Variable causes the ratio to match that of the geometry of the current vertex.
+ * - "strokewidth", optional string. Either an integer or the string "inherit".
+ * "inherit" indicates that the strokeWidth of the cell is only changed on scaling,
+ * not on resizing. Default is "1".
+ * If numeric values are used, the strokeWidth of the cell is changed on both
+ * scaling and resizing and the value defines the multiple that is applied to
+ * the width.
+ * 
+ * connections:
+ * 
+ * If you want to define specific fixed connection points on the shape use the
+ * *connections* element. Each *constraint* element within connections defines
+ * a fixed connection point on the shape. Constraints have attributes:
+ * 
+ * - "perimeter", required. 1 or 0. 0 sets the connection point where specified
+ * by x,y. 1 Causes the position of the connection point to be extrapolated from
+ * the center of the shape, through x,y to the point of intersection with the
+ * perimeter of the shape.
+ * - "x" and "y" are the position of the fixed point relative to the bounds of
+ * the shape. They can be automatically adjusted if perimeter=1. So, (0,0) is top
+ * left, (0.5,0.5) the center, (1,0.5) the center of the right hand edge of the
+ * bounds, etc. Values may be less than 0 or greater than 1 to be positioned
+ * outside of the shape.
+ * - "name", optional string. A unique identifier for the port on the shape.
+ * 
+ * background and foreground:
+ * 
+ * The path of the graphics drawing is split into two elements, *foreground* and
+ * *background*. The split is to define which part any shadow applied to the shape
+ * is derived from (the background). This, generally, means the background is the
+ * line tracing of the outside of the shape, but not always.
+ * 
+ * Any stroke, fill or fillstroke of a background must be the first element of the
+ * foreground element, they must not be used within *background*. If the background
+ * is empty, this is not required.
+ * 
+ * Because the background cannot have any fill or stroke, it can contain only one
+ * *path*, *rect*, *roundrect* or *ellipse* element (or none). It can also not
+ * include *image*, *text* or *include-shape*.
+ * 
+ * Note that the state, styling and drawing in mxGraph stencils is very close in
+ * design to that of HTML 5 canvas. Tutorials on this subject, if you're not
+ * familiar with the topic, will give a good high-level introduction to the
+ * concepts used.
+ * 
+ * State:
+ * 
+ * Rendering within the foreground and background elements has the concept of
+ * state. There are two types of operations other than state save/load, styling
+ * and drawing. The styling operations change the current state, so you can save
+ * the current state with <save/> and pull the last saved state from the state
+ * stack using <restore/>.
+ * 
+ * Styling:
+ * 
+ * The elements that change colors within the current state all take a hash
+ * prefixed hex color code ("#FFEA80").
+ * 
+ * - *strokecolor*, this sets the color that drawing paths will be rendered in
+ * when a stroke or fillstroke command is issued.
+ * - *fillcolor*, this sets the color that the inside of closed paths will be
+ * rendered in when a fill or fillstroke command is issued.
+ * - *fontcolor*, this sets the color that fonts are rendered in when text is drawn.
+ * 
+ * *alpha* defines the degree of transparency used between 1.0 for fully opaque
+ * and 0.0 for fully transparent.
+ * 
+ * *strokewidth* defines the integer thickness of drawing elements rendered by
+ * stroking. Use fixed="1" to apply the value as-is, without scaling.
+ * 
+ * *dashed* is "1" for dashing enabled and "0" for disabled.
+ * 
+ * When *dashed* is enabled the current dash pattern, defined by *dashpattern*,
+ * is used on strokes. dashpattern is a sequence of space separated "on, off"
+ * lengths that define what distance to paint the stroke for, then what distance
+ * to paint nothing for, repeat... The default is "3 3". You could define a more
+ * complex pattern with "5 3 2 6", for example. Generally, it makes sense to have
+ * an even number of elements in the dashpattern, but that's not required.
+ * 
+ * *linejoin*, *linecap* and *miterlimit* are best explained by the Mozilla page
+ * on Canvas styling (about halfway down). The values are all the same except we
+ * use "flat" for linecap, instead of Canvas' "butt".
+ * 
+ * For font styling there are.
+ * 
+ * - *fontsize*, an integer,
+ * - *fontstyle*, an ORed bit pattern of bold (1), italic (2) and underline (4),
+ * i.e bold underline is "5".
+ * - *fontfamily*, is a string defining the typeface to be used.
+ * 
+ * Drawing:
+ * 
+ * Most drawing is contained within a *path* element. Again, the graphic
+ * primitives are very similar to that of HTML 5 canvas.
+ * 
+ * - *move* to attributes required decimals (x,y).
+ * - *line* to attributes required decimals (x,y).
+ * - *quad* to required decimals (x2,y2) via control point required decimals
+ * (x1,y1).
+ * - *curve* to required decimals (x3,y3), via control points required decimals
+ * (x1,y1) and (x2,y2).
+ * - *arc*, this doesn't follow the HTML Canvas signatures, instead it's a copy
+ * of the SVG arc command. The SVG specification documentation gives the best
+ * description of its behaviors. The attributes are named identically, they are
+ * decimals and all required.
+ * - *close* ends the current subpath and causes an automatic straight line to
+ * be drawn from the current point to the initial point of the current subpath.
+ * 
+ * Complex drawing:
+ * 
+ * In addition to the graphics primitive operations there are non-primitive
+ * operations. These provide an easy method to draw some basic shapes.
+ * 
+ * - *rect*, attributes "x", "y", "w", "h", all required decimals
+ * - *roundrect*, attributes "x", "y", "w", "h", all required decimals. Also
+ * "arcsize" an optional decimal attribute defining how large, the corner curves
+ * are.
+ * - *ellipse*, attributes "x", "y", "w", "h", all required decimals.
+ * 
+ * Note that these 3 shapes and all paths must be followed by either a fill,
+ * stroke, or fillstroke.
+ * 
+ * Text:
+ * 
+ * *text* elements have the following attributes.
+ * 
+ * - "str", the text string to display, required.
+ * - "x" and "y", the decimal location (x,y) of the text element, required.
+ * - "align", the horizontal alignment of the text element, either "left",
+ * "center" or "right". Optional, default is "left".
+ * - "valign", the vertical alignment of the text element, either "top", "middle"
+ * or "bottom". Optional, default is "top".
+ * - "localized", 0 or 1, if 1 then the "str" actually contains a key to use to
+ * fetch the value out of mxResources. Optional, default is
+ * <mxStencil.defaultLocalized>.
+ * - "vertical", 0 or 1, if 1 the label is rendered vertically (rotated by 90
+ * degrees). Optional, default is 0.
+ * - "rotation", angle in degrees (0 to 360). The angle to rotate the text by.
+ * Optional, default is 0.
+ * - "align-shape", 0 or 1, if 0 ignore the rotation of the shape when setting
+ * the text rotation. Optional, default is 1.
+ * 
+ * If <allowEval> is true, then the text content of the this element can define
+ * a function which is invoked with the shape as the only argument and returns
+ * the value for the text element (ignored if the str attribute is not null).
+ * 
+ * Images:
+ * 
+ * *image* elements can either be external URLs, or data URIs, where supported
+ * (not in IE 7-). Attributes are:
+ * 
+ * - "src", required string. Either a data URI or URL.
+ * - "x", "y", required decimals. The (x,y) position of the image.
+ * - "w", "h", required decimals. The width and height of the image.
+ * - "flipH" and "flipV", optional 0 or 1. Whether to flip the image along the
+ * horizontal/vertical axis. Default is 0 for both.
+ * 
+ * If <allowEval> is true, then the text content of the this element can define
+ * a function which is invoked with the shape as the only argument and returns
+ * the value for the image source (ignored if the src attribute is not null).
+ * 
+ * Sub-shapes:
+ * 
+ * *include-shape* allow stencils to be rendered within the current stencil by
+ * referencing the sub-stencil by name. Attributes are:
+ * 
+ * - "name", required string. The unique shape name of the stencil.
+ * - "x", "y", "w", "h", required decimals. The (x,y) position of the sub-shape
+ * and its width and height.
+ * 
+ * Constructor: mxStencil
+ * 
+ * Constructs a new generic shape by setting <desc> to the given XML node and
+ * invoking <parseDescription> and <parseConstraints>.
+ * 
+ * Parameters:
+ * 
+ * desc - XML node that contains the stencil description.
+ */
+function mxStencil(desc)
+{
+	this.desc = desc;
+	this.parseDescription();
+	this.parseConstraints();
+};
+
+/**
+ * Variable: defaultLocalized
+ * 
+ * Static global variable that specifies the default value for the localized
+ * attribute of the text element. Default is false.
+ */
+mxStencil.defaultLocalized = false;
+
+/**
+ * Function: allowEval
+ * 
+ * Static global switch that specifies if the use of eval is allowed for
+ * evaluating text content and images. Default is false. Set this to true
+ * if stencils can not contain user input.
+ */
+mxStencil.allowEval = false;
+
+/**
+ * Variable: desc
+ *
+ * Holds the XML node with the stencil description.
+ */
+mxStencil.prototype.desc = null;
+
+/**
+ * Variable: constraints
+ * 
+ * Holds an array of <mxConnectionConstraints> as defined in the shape.
+ */
+mxStencil.prototype.constraints = null;
+
+/**
+ * Variable: aspect
+ *
+ * Holds the aspect of the shape. Default is 'auto'.
+ */
+mxStencil.prototype.aspect = null;
+
+/**
+ * Variable: w0
+ *
+ * Holds the width of the shape. Default is 100.
+ */
+mxStencil.prototype.w0 = null;
+
+/**
+ * Variable: h0
+ *
+ * Holds the height of the shape. Default is 100.
+ */
+mxStencil.prototype.h0 = null;
+
+/**
+ * Variable: bgNodes
+ *
+ * Holds the XML node with the stencil description.
+ */
+mxStencil.prototype.bgNode = null;
+
+/**
+ * Variable: fgNodes
+ *
+ * Holds the XML node with the stencil description.
+ */
+mxStencil.prototype.fgNode = null;
+
+/**
+ * Variable: strokewidth
+ *
+ * Holds the strokewidth direction from the description.
+ */
+mxStencil.prototype.strokewidth = null;
+
+/**
+ * Function: parseDescription
+ *
+ * Reads <w0>, <h0>, <aspect>, <bgNodes> and <fgNodes> from <desc>.
+ */
+mxStencil.prototype.parseDescription = function()
+{
+	// LATER: Preprocess nodes for faster painting
+	this.fgNode = this.desc.getElementsByTagName('foreground')[0];
+	this.bgNode = this.desc.getElementsByTagName('background')[0];
+	this.w0 = Number(this.desc.getAttribute('w') || 100);
+	this.h0 = Number(this.desc.getAttribute('h') || 100);
+	
+	// Possible values for aspect are: variable and fixed where
+	// variable means fill the available space and fixed means
+	// use w0 and h0 to compute the aspect.
+	var aspect = this.desc.getAttribute('aspect');
+	this.aspect = (aspect != null) ? aspect : 'variable';
+	
+	// Possible values for strokewidth are all numbers and "inherit"
+	// where the inherit means take the value from the style (ie. the
+	// user-defined stroke-width). Note that the strokewidth is scaled
+	// by the minimum scaling that is used to draw the shape (sx, sy).
+	var sw = this.desc.getAttribute('strokewidth');
+	this.strokewidth = (sw != null) ? sw : '1';
+};
+
+/**
+ * Function: parseConstraints
+ *
+ * Reads the constraints from <desc> into <constraints> using
+ * <parseConstraint>.
+ */
+mxStencil.prototype.parseConstraints = function()
+{
+	var conns = this.desc.getElementsByTagName('connections')[0];
+	
+	if (conns != null)
+	{
+		var tmp = mxUtils.getChildNodes(conns);
+		
+		if (tmp != null && tmp.length > 0)
+		{
+			this.constraints = [];
+			
+			for (var i = 0; i < tmp.length; i++)
+			{
+				this.constraints.push(this.parseConstraint(tmp[i]));
+			}
+		}
+	}
+};
+
+/**
+ * Function: parseConstraint
+ *
+ * Parses the given XML node and returns its <mxConnectionConstraint>.
+ */
+mxStencil.prototype.parseConstraint = function(node)
+{
+	var x = Number(node.getAttribute('x'));
+	var y = Number(node.getAttribute('y'));
+	var perimeter = node.getAttribute('perimeter') == '1';
+	var name = node.getAttribute('name');
+	
+	return new mxConnectionConstraint(new mxPoint(x, y), perimeter, name);
+};
+
+/**
+ * Function: evaluateTextAttribute
+ * 
+ * Gets the given attribute as a text. The return value from <evaluateAttribute>
+ * is used as a key to <mxResources.get> if the localized attribute in the text
+ * node is 1 or if <defaultLocalized> is true.
+ */
+mxStencil.prototype.evaluateTextAttribute = function(node, attribute, shape)
+{
+	var result = this.evaluateAttribute(node, attribute, shape);
+	var loc = node.getAttribute('localized');
+	
+	if ((mxStencil.defaultLocalized && loc == null) || loc == '1')
+	{
+		result = mxResources.get(result);
+	}
+
+	return result;
+};
+
+/**
+ * Function: evaluateAttribute
+ *
+ * Gets the attribute for the given name from the given node. If the attribute
+ * does not exist then the text content of the node is evaluated and if it is
+ * a function it is invoked with <shape> as the only argument and the return
+ * value is used as the attribute value to be returned.
+ */
+mxStencil.prototype.evaluateAttribute = function(node, attribute, shape)
+{
+	var result = node.getAttribute(attribute);
+	
+	if (result == null)
+	{
+		var text = mxUtils.getTextContent(node);
+		
+		if (text != null && mxStencil.allowEval)
+		{
+			var funct = mxUtils.eval(text);
+			
+			if (typeof(funct) == 'function')
+			{
+				result = funct(shape);
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: drawShape
+ *
+ * Draws this stencil inside the given bounds.
+ */
+mxStencil.prototype.drawShape = function(canvas, shape, x, y, w, h)
+{
+	// TODO: Internal structure (array of special structs?), relative and absolute
+	// coordinates (eg. note shape, process vs star, actor etc.), text rendering
+	// and non-proportional scaling, how to implement pluggable edge shapes
+	// (start, segment, end blocks), pluggable markers, how to implement
+	// swimlanes (title area) with this API, add icon, horizontal/vertical
+	// label, indicator for all shapes, rotation
+	var direction = mxUtils.getValue(shape.style, mxConstants.STYLE_DIRECTION, null);
+	var aspect = this.computeAspect(shape.style, x, y, w, h, direction);
+	var minScale = Math.min(aspect.width, aspect.height);
+	var sw = (this.strokewidth == 'inherit') ?
+			Number(mxUtils.getNumber(shape.style, mxConstants.STYLE_STROKEWIDTH, 1)) :
+			Number(this.strokewidth) * minScale;
+	canvas.setStrokeWidth(sw);
+
+	this.drawChildren(canvas, shape, x, y, w, h, this.bgNode, aspect, false);
+	this.drawChildren(canvas, shape, x, y, w, h, this.fgNode, aspect, true);
+};
+
+/**
+ * Function: drawChildren
+ *
+ * Draws this stencil inside the given bounds.
+ */
+mxStencil.prototype.drawChildren = function(canvas, shape, x, y, w, h, node, aspect, disableShadow)
+{
+	if (node != null && w > 0 && h > 0)
+	{
+		var tmp = node.firstChild;
+		
+		while (tmp != null)
+		{
+			if (tmp.nodeType == mxConstants.NODETYPE_ELEMENT)
+			{
+				this.drawNode(canvas, shape, tmp, aspect, disableShadow);
+			}
+			
+			tmp = tmp.nextSibling;
+		}
+	}
+};
+
+/**
+ * Function: computeAspect
+ *
+ * Returns a rectangle that contains the offset in x and y and the horizontal
+ * and vertical scale in width and height used to draw this shape inside the
+ * given <mxRectangle>.
+ * 
+ * Parameters:
+ * 
+ * shape - <mxShape> to be drawn.
+ * bounds - <mxRectangle> that should contain the stencil.
+ * direction - Optional direction of the shape to be darwn.
+ */
+mxStencil.prototype.computeAspect = function(shape, x, y, w, h, direction)
+{
+	var x0 = x;
+	var y0 = y;
+	var sx = w / this.w0;
+	var sy = h / this.h0;
+	
+	var inverse = (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH);
+
+	if (inverse)
+	{
+		sy = w / this.h0;
+		sx = h / this.w0;
+		
+		var delta = (w - h) / 2;
+
+		x0 += delta;
+		y0 -= delta;
+	}
+
+	if (this.aspect == 'fixed')
+	{
+		sy = Math.min(sx, sy);
+		sx = sy;
+		
+		// Centers the shape inside the available space
+		if (inverse)
+		{
+			x0 += (h - this.w0 * sx) / 2;
+			y0 += (w - this.h0 * sy) / 2;
+		}
+		else
+		{
+			x0 += (w - this.w0 * sx) / 2;
+			y0 += (h - this.h0 * sy) / 2;
+		}
+	}
+
+	return new mxRectangle(x0, y0, sx, sy);
+};
+
+/**
+ * Function: drawNode
+ *
+ * Draws this stencil inside the given bounds.
+ */
+mxStencil.prototype.drawNode = function(canvas, shape, node, aspect, disableShadow)
+{
+	var name = node.nodeName;
+	var x0 = aspect.x;
+	var y0 = aspect.y;
+	var sx = aspect.width;
+	var sy = aspect.height;
+	var minScale = Math.min(sx, sy);
+
+	if (name == 'save')
+	{
+		canvas.save();
+	}
+	else if (name == 'restore')
+	{
+		canvas.restore();
+	}
+	else if (name == 'path')
+	{
+		canvas.begin();
+
+		// Renders the elements inside the given path
+		var childNode = node.firstChild;
+		
+		while (childNode != null)
+		{
+			if (childNode.nodeType == mxConstants.NODETYPE_ELEMENT)
+			{
+				this.drawNode(canvas, shape, childNode, aspect, disableShadow);
+			}
+			
+			childNode = childNode.nextSibling;
+		}
+	}
+	else if (name == 'close')
+	{
+		canvas.close();
+	}
+	else if (name == 'move')
+	{
+		canvas.moveTo(x0 + Number(node.getAttribute('x')) * sx, y0 + Number(node.getAttribute('y')) * sy);
+	}
+	else if (name == 'line')
+	{
+		canvas.lineTo(x0 + Number(node.getAttribute('x')) * sx, y0 + Number(node.getAttribute('y')) * sy);
+	}
+	else if (name == 'quad')
+	{
+		canvas.quadTo(x0 + Number(node.getAttribute('x1')) * sx,
+				y0 + Number(node.getAttribute('y1')) * sy,
+				x0 + Number(node.getAttribute('x2')) * sx,
+				y0 + Number(node.getAttribute('y2')) * sy);
+	}
+	else if (name == 'curve')
+	{
+		canvas.curveTo(x0 + Number(node.getAttribute('x1')) * sx,
+				y0 + Number(node.getAttribute('y1')) * sy,
+				x0 + Number(node.getAttribute('x2')) * sx,
+				y0 + Number(node.getAttribute('y2')) * sy,
+				x0 + Number(node.getAttribute('x3')) * sx,
+				y0 + Number(node.getAttribute('y3')) * sy);
+	}
+	else if (name == 'arc')
+	{
+		canvas.arcTo(Number(node.getAttribute('rx')) * sx,
+				Number(node.getAttribute('ry')) * sy,
+				Number(node.getAttribute('x-axis-rotation')),
+				Number(node.getAttribute('large-arc-flag')),
+				Number(node.getAttribute('sweep-flag')),
+				x0 + Number(node.getAttribute('x')) * sx,
+				y0 + Number(node.getAttribute('y')) * sy);
+	}
+	else if (name == 'rect')
+	{
+		canvas.rect(x0 + Number(node.getAttribute('x')) * sx,
+				y0 + Number(node.getAttribute('y')) * sy,
+				Number(node.getAttribute('w')) * sx,
+				Number(node.getAttribute('h')) * sy);
+	}
+	else if (name == 'roundrect')
+	{
+		var arcsize = Number(node.getAttribute('arcsize'));
+
+		if (arcsize == 0)
+		{
+			arcsize = mxConstants.RECTANGLE_ROUNDING_FACTOR * 100;
+		}
+		
+		var w = Number(node.getAttribute('w')) * sx;
+		var h = Number(node.getAttribute('h')) * sy;
+		var factor = Number(arcsize) / 100;
+		var r = Math.min(w * factor, h * factor);
+		
+		canvas.roundrect(x0 + Number(node.getAttribute('x')) * sx,
+				y0 + Number(node.getAttribute('y')) * sy,
+				w, h, r, r);
+	}
+	else if (name == 'ellipse')
+	{
+		canvas.ellipse(x0 + Number(node.getAttribute('x')) * sx,
+			y0 + Number(node.getAttribute('y')) * sy,
+			Number(node.getAttribute('w')) * sx,
+			Number(node.getAttribute('h')) * sy);
+	}
+	else if (name == 'image')
+	{
+		if (!shape.outline)
+		{
+			var src = this.evaluateAttribute(node, 'src', shape);
+			
+			canvas.image(x0 + Number(node.getAttribute('x')) * sx,
+				y0 + Number(node.getAttribute('y')) * sy,
+				Number(node.getAttribute('w')) * sx,
+				Number(node.getAttribute('h')) * sy,
+				src, false, node.getAttribute('flipH') == '1',
+				node.getAttribute('flipV') == '1');
+		}
+	}
+	else if (name == 'text')
+	{
+		if (!shape.outline)
+		{
+			var str = this.evaluateTextAttribute(node, 'str', shape);
+			var rotation = node.getAttribute('vertical') == '1' ? -90 : 0;
+			
+			if (node.getAttribute('align-shape') == '0')
+			{
+				var dr = shape.rotation;
+	
+				// Depends on flipping
+				var flipH = mxUtils.getValue(shape.style, mxConstants.STYLE_FLIPH, 0) == 1;
+				var flipV = mxUtils.getValue(shape.style, mxConstants.STYLE_FLIPV, 0) == 1;
+				
+				if (flipH && flipV)
+				{
+					rotation -= dr;
+				}
+				else if (flipH || flipV)
+				{
+					rotation += dr;
+				}
+				else
+				{
+					rotation -= dr;
+				}
+			}
+	
+			rotation -= node.getAttribute('rotation');
+	
+			canvas.text(x0 + Number(node.getAttribute('x')) * sx,
+					y0 + Number(node.getAttribute('y')) * sy,
+					0, 0, str, node.getAttribute('align') || 'left',
+					node.getAttribute('valign') || 'top', false, '',
+					null, false, rotation);
+		}
+	}
+	else if (name == 'include-shape')
+	{
+		var stencil = mxStencilRegistry.getStencil(node.getAttribute('name'));
+		
+		if (stencil != null)
+		{
+			var x = x0 + Number(node.getAttribute('x')) * sx;
+			var y = y0 + Number(node.getAttribute('y')) * sy;
+			var w = Number(node.getAttribute('w')) * sx;
+			var h = Number(node.getAttribute('h')) * sy;
+			
+			stencil.drawShape(canvas, shape, x, y, w, h);
+		}
+	}
+	else if (name == 'fillstroke')
+	{
+		canvas.fillAndStroke();
+	}
+	else if (name == 'fill')
+	{
+		canvas.fill();
+	}
+	else if (name == 'stroke')
+	{
+		canvas.stroke();
+	}
+	else if (name == 'strokewidth')
+	{
+		var s = (node.getAttribute('fixed') == '1') ? 1 : minScale;
+		canvas.setStrokeWidth(Number(node.getAttribute('width')) * s);
+	}
+	else if (name == 'dashed')
+	{
+		canvas.setDashed(node.getAttribute('dashed') == '1');
+	}
+	else if (name == 'dashpattern')
+	{
+		var value = node.getAttribute('pattern');
+		
+		if (value != null)
+		{
+			var tmp = value.split(' ');
+			var pat = [];
+			
+			for (var i = 0; i < tmp.length; i++)
+			{
+				if (tmp[i].length > 0)
+				{
+					pat.push(Number(tmp[i]) * minScale);
+				}
+			}
+			
+			value = pat.join(' ');
+			canvas.setDashPattern(value);
+		}
+	}
+	else if (name == 'strokecolor')
+	{
+		canvas.setStrokeColor(node.getAttribute('color'));
+	}
+	else if (name == 'linecap')
+	{
+		canvas.setLineCap(node.getAttribute('cap'));
+	}
+	else if (name == 'linejoin')
+	{
+		canvas.setLineJoin(node.getAttribute('join'));
+	}
+	else if (name == 'miterlimit')
+	{
+		canvas.setMiterLimit(Number(node.getAttribute('limit')));
+	}
+	else if (name == 'fillcolor')
+	{
+		canvas.setFillColor(node.getAttribute('color'));
+	}
+	else if (name == 'alpha')
+	{
+		canvas.setAlpha(node.getAttribute('alpha'));
+	}
+	else if (name == 'fontcolor')
+	{
+		canvas.setFontColor(node.getAttribute('color'));
+	}
+	else if (name == 'fontstyle')
+	{
+		canvas.setFontStyle(node.getAttribute('style'));
+	}
+	else if (name == 'fontfamily')
+	{
+		canvas.setFontFamily(node.getAttribute('family'));
+	}
+	else if (name == 'fontsize')
+	{
+		canvas.setFontSize(Number(node.getAttribute('size')) * minScale);
+	}
+	
+	if (disableShadow && (name == 'fillstroke' || name == 'fill' || name == 'stroke'))
+	{
+		disableShadow = false;
+		canvas.setShadow(false);
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxShape
+ *
+ * Base class for all shapes. A shape in mxGraph is a
+ * separate implementation for SVG, VML and HTML. Which
+ * implementation to use is controlled by the <dialect>
+ * property which is assigned from within the <mxCellRenderer>
+ * when the shape is created. The dialect must be assigned
+ * for a shape, and it does normally depend on the browser and
+ * the confiuration of the graph (see <mxGraph> rendering hint).
+ *
+ * For each supported shape in SVG and VML, a corresponding
+ * shape exists in mxGraph, namely for text, image, rectangle,
+ * rhombus, ellipse and polyline. The other shapes are a
+ * combination of these shapes (eg. label and swimlane)
+ * or they consist of one or more (filled) path objects
+ * (eg. actor and cylinder). The HTML implementation is
+ * optional but may be required for a HTML-only view of
+ * the graph.
+ *
+ * Custom Shapes:
+ *
+ * To extend from this class, the basic code looks as follows.
+ * In the special case where the custom shape consists only of
+ * one filled region or one filled region and an additional stroke
+ * the <mxActor> and <mxCylinder> should be subclassed,
+ * respectively.
+ *
+ * (code)
+ * function CustomShape() { }
+ * 
+ * CustomShape.prototype = new mxShape();
+ * CustomShape.prototype.constructor = CustomShape; 
+ * (end)
+ *
+ * To register a custom shape in an existing graph instance,
+ * one must register the shape under a new name in the graph's
+ * cell renderer as follows:
+ *
+ * (code)
+ * mxCellRenderer.registerShape('customShape', CustomShape);
+ * (end)
+ *
+ * The second argument is the name of the constructor.
+ *
+ * In order to use the shape you can refer to the given name above
+ * in a stylesheet. For example, to change the shape for the default
+ * vertex style, the following code is used:
+ *
+ * (code)
+ * var style = graph.getStylesheet().getDefaultVertexStyle();
+ * style[mxConstants.STYLE_SHAPE] = 'customShape';
+ * (end)
+ * 
+ * Constructor: mxShape
+ *
+ * Constructs a new shape.
+ */
+function mxShape(stencil)
+{
+	this.stencil = stencil;
+	this.initStyles();
+};
+
+/**
+ * Variable: dialect
+ *
+ * Holds the dialect in which the shape is to be painted.
+ * This can be one of the DIALECT constants in <mxConstants>.
+ */
+mxShape.prototype.dialect = null;
+
+/**
+ * Variable: scale
+ *
+ * Holds the scale in which the shape is being painted.
+ */
+mxShape.prototype.scale = 1;
+
+/**
+ * Variable: antiAlias
+ * 
+ * Rendering hint for configuring the canvas.
+ */
+mxShape.prototype.antiAlias = true;
+
+/**
+ * Variable: bounds
+ *
+ * Holds the <mxRectangle> that specifies the bounds of this shape.
+ */
+mxShape.prototype.bounds = null;
+
+/**
+ * Variable: points
+ *
+ * Holds the array of <mxPoints> that specify the points of this shape.
+ */
+mxShape.prototype.points = null;
+
+/**
+ * Variable: node
+ *
+ * Holds the outermost DOM node that represents this shape.
+ */
+mxShape.prototype.node = null;
+ 
+/**
+ * Variable: state
+ * 
+ * Optional reference to the corresponding <mxCellState>.
+ */
+mxShape.prototype.state = null;
+
+/**
+ * Variable: style
+ *
+ * Optional reference to the style of the corresponding <mxCellState>.
+ */
+mxShape.prototype.style = null;
+
+/**
+ * Variable: boundingBox
+ *
+ * Contains the bounding box of the shape, that is, the smallest rectangle
+ * that includes all pixels of the shape.
+ */
+mxShape.prototype.boundingBox = null;
+
+/**
+ * Variable: stencil
+ *
+ * Holds the <mxStencil> that defines the shape.
+ */
+mxShape.prototype.stencil = null;
+
+/**
+ * Variable: svgStrokeTolerance
+ *
+ * Event-tolerance for SVG strokes (in px). Default is 8. This is only passed
+ * to the canvas in <createSvgCanvas> if <pointerEvents> is true.
+ */
+mxShape.prototype.svgStrokeTolerance = 8;
+
+/**
+ * Variable: pointerEvents
+ * 
+ * Specifies if pointer events should be handled. Default is true.
+ */
+mxShape.prototype.pointerEvents = true;
+
+/**
+ * Variable: svgPointerEvents
+ * 
+ * Specifies if pointer events should be handled. Default is true.
+ */
+mxShape.prototype.svgPointerEvents = 'all';
+
+/**
+ * Variable: shapePointerEvents
+ * 
+ * Specifies if pointer events outside of shape should be handled. Default
+ * is false.
+ */
+mxShape.prototype.shapePointerEvents = false;
+
+/**
+ * Variable: stencilPointerEvents
+ * 
+ * Specifies if pointer events outside of stencils should be handled. Default
+ * is false. Set this to true for backwards compatibility with the 1.x branch.
+ */
+mxShape.prototype.stencilPointerEvents = false;
+
+/**
+ * Variable: vmlScale
+ * 
+ * Scale for improving the precision of VML rendering. Default is 1.
+ */
+mxShape.prototype.vmlScale = 1;
+
+/**
+ * Variable: outline
+ * 
+ * Specifies if the shape should be drawn as an outline. This disables all
+ * fill colors and can be used to disable other drawing states that should
+ * not be painted for outlines. Default is false. This should be set before
+ * calling <apply>.
+ */
+mxShape.prototype.outline = false;
+
+/**
+ * Variable: visible
+ * 
+ * Specifies if the shape is visible. Default is true.
+ */
+mxShape.prototype.visible = true;
+
+/**
+ * Variable: useSvgBoundingBox
+ * 
+ * Allows to use the SVG bounding box in SVG. Default is false for performance
+ * reasons.
+ */
+mxShape.prototype.useSvgBoundingBox = false;
+
+/**
+ * Function: init
+ *
+ * Initializes the shape by creaing the DOM node using <create>
+ * and adding it into the given container.
+ *
+ * Parameters:
+ *
+ * container - DOM node that will contain the shape.
+ */
+mxShape.prototype.init = function(container)
+{
+	if (this.node == null)
+	{
+		this.node = this.create(container);
+		
+		if (container != null)
+		{
+			container.appendChild(this.node);
+		}
+	}
+};
+
+/**
+ * Function: initStyles
+ *
+ * Sets the styles to their default values.
+ */
+mxShape.prototype.initStyles = function(container)
+{
+	this.strokewidth = 1;
+	this.rotation = 0;
+	this.opacity = 100;
+	this.fillOpacity = 100;
+	this.strokeOpacity = 100;
+	this.flipH = false;
+	this.flipV = false;
+};
+
+/**
+ * Function: isParseVml
+ * 
+ * Specifies if any VML should be added via insertAdjacentHtml to the DOM. This
+ * is only needed in IE8 and only if the shape contains VML markup. This method
+ * returns true.
+ */
+mxShape.prototype.isParseVml = function()
+{
+	return true;
+};
+
+/**
+ * Function: isHtmlAllowed
+ * 
+ * Returns true if HTML is allowed for this shape. This implementation always
+ * returns false.
+ */
+mxShape.prototype.isHtmlAllowed = function()
+{
+	return false;
+};
+
+/**
+ * Function: getSvgScreenOffset
+ * 
+ * Returns 0, or 0.5 if <strokewidth> % 2 == 1.
+ */
+mxShape.prototype.getSvgScreenOffset = function()
+{
+	var sw = this.stencil && this.stencil.strokewidth != 'inherit' ? Number(this.stencil.strokewidth) : this.strokewidth;
+	
+	return (mxUtils.mod(Math.max(1, Math.round(sw * this.scale)), 2) == 1) ? 0.5 : 0;
+};
+
+/**
+ * Function: create
+ *
+ * Creates and returns the DOM node(s) for the shape in
+ * the given container. This implementation invokes
+ * <createSvg>, <createHtml> or <createVml> depending
+ * on the <dialect> and style settings.
+ *
+ * Parameters:
+ *
+ * container - DOM node that will contain the shape.
+ */
+mxShape.prototype.create = function(container)
+{
+	var node = null;
+	
+	if (container != null && container.ownerSVGElement != null)
+	{
+		node = this.createSvg(container);
+	}
+	else if (document.documentMode == 8 || !mxClient.IS_VML ||
+		(this.dialect != mxConstants.DIALECT_VML && this.isHtmlAllowed()))
+	{
+		node = this.createHtml(container);
+	}
+	else
+	{
+		node = this.createVml(container);
+	}
+	
+	return node;
+};
+
+/**
+ * Function: createSvg
+ *
+ * Creates and returns the SVG node(s) to represent this shape.
+ */
+mxShape.prototype.createSvg = function()
+{
+	return document.createElementNS(mxConstants.NS_SVG, 'g');
+};
+
+/**
+ * Function: createVml
+ *
+ * Creates and returns the VML node to represent this shape.
+ */
+mxShape.prototype.createVml = function()
+{
+	var node = document.createElement(mxClient.VML_PREFIX + ':group');
+	node.style.position = 'absolute';
+	
+	return node;
+};
+
+/**
+ * Function: createHtml
+ *
+ * Creates and returns the HTML DOM node(s) to represent
+ * this shape. This implementation falls back to <createVml>
+ * so that the HTML creation is optional.
+ */
+mxShape.prototype.createHtml = function()
+{
+	var node = document.createElement('div');
+	node.style.position = 'absolute';
+	
+	return node;
+};
+
+/**
+ * Function: reconfigure
+ *
+ * Reconfigures this shape. This will update the colors etc in
+ * addition to the bounds or points.
+ */
+mxShape.prototype.reconfigure = function()
+{
+	this.redraw();
+};
+
+/**
+ * Function: redraw
+ *
+ * Creates and returns the SVG node(s) to represent this shape.
+ */
+mxShape.prototype.redraw = function()
+{
+	this.updateBoundsFromPoints();
+	
+	if (this.visible && this.checkBounds())
+	{
+		this.node.style.visibility = 'visible';
+		this.clear();
+		
+		if (this.node.nodeName == 'DIV' && (this.isHtmlAllowed() || !mxClient.IS_VML))
+		{
+			this.redrawHtmlShape();
+		}
+		else
+		{	
+			this.redrawShape();
+		}
+
+		this.updateBoundingBox();
+	}
+	else
+	{
+		this.node.style.visibility = 'hidden';
+		this.boundingBox = null;
+	}
+};
+
+/**
+ * Function: clear
+ * 
+ * Removes all child nodes and resets all CSS.
+ */
+mxShape.prototype.clear = function()
+{
+	if (this.node.ownerSVGElement != null)
+	{
+		while (this.node.lastChild != null)
+		{
+			this.node.removeChild(this.node.lastChild);
+		}
+	}
+	else
+	{
+		this.node.style.cssText = 'position:absolute;' + ((this.cursor != null) ?
+			('cursor:' + this.cursor + ';') : '');
+		this.node.innerHTML = '';
+	}
+};
+
+/**
+ * Function: updateBoundsFromPoints
+ * 
+ * Updates the bounds based on the points.
+ */
+mxShape.prototype.updateBoundsFromPoints = function()
+{
+	var pts = this.points;
+	
+	if (pts != null && pts.length > 0 && pts[0] != null)
+	{
+		this.bounds = new mxRectangle(Number(pts[0].x), Number(pts[0].y), 1, 1);
+		
+		for (var i = 1; i < this.points.length; i++)
+		{
+			if (pts[i] != null)
+			{
+				this.bounds.add(new mxRectangle(Number(pts[i].x), Number(pts[i].y), 1, 1));
+			}
+		}
+	}
+};
+
+/**
+ * Function: getLabelBounds
+ * 
+ * Returns the <mxRectangle> for the label bounds of this shape, based on the
+ * given scaled and translated bounds of the shape. This method should not
+ * change the rectangle in-place. This implementation returns the given rect.
+ */
+mxShape.prototype.getLabelBounds = function(rect)
+{
+	var d = mxUtils.getValue(this.style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST);
+	var bounds = rect;
+	
+	// Normalizes argument for getLabelMargins hook
+	if (d != mxConstants.DIRECTION_SOUTH && d != mxConstants.DIRECTION_NORTH &&
+		this.state != null && this.state.text != null &&
+		this.state.text.isPaintBoundsInverted())
+	{
+		bounds = bounds.clone();
+		var tmp = bounds.width;
+		bounds.width = bounds.height;
+		bounds.height = tmp;
+	}
+		
+	var m = this.getLabelMargins(bounds);
+	
+	if (m != null)
+	{
+		var flipH = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPH, false) == '1';
+		var flipV = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPV, false) == '1';
+		
+		// Handles special case for vertical labels
+		if (this.state != null && this.state.text != null &&
+			this.state.text.isPaintBoundsInverted())
+		{
+			var tmp = m.x;
+			m.x = m.height;
+			m.height = m.width;
+			m.width = m.y;
+			m.y = tmp;
+
+			tmp = flipH;
+			flipH = flipV;
+			flipV = tmp;
+		}
+		
+		return mxUtils.getDirectedBounds(rect, m, this.style, flipH, flipV);
+	}
+	
+	return rect;
+};
+
+/**
+ * Function: getLabelMargins
+ * 
+ * Returns the scaled top, left, bottom and right margin to be used for
+ * computing the label bounds as an <mxRectangle>, where the bottom and right
+ * margin are defined in the width and height of the rectangle, respectively.
+ */
+mxShape.prototype.getLabelMargins= function(rect)
+{
+	return null;
+};
+
+/**
+ * Function: checkBounds
+ * 
+ * Returns true if the bounds are not null and all of its variables are numeric.
+ */
+mxShape.prototype.checkBounds = function()
+{
+	return (!isNaN(this.scale) && isFinite(this.scale) && this.scale > 0 &&
+			this.bounds != null && !isNaN(this.bounds.x) && !isNaN(this.bounds.y) &&
+			!isNaN(this.bounds.width) && !isNaN(this.bounds.height) &&
+			this.bounds.width > 0 && this.bounds.height > 0);
+};
+
+/**
+ * Function: createVmlGroup
+ *
+ * Returns the temporary element used for rendering in IE8 standards mode.
+ */
+mxShape.prototype.createVmlGroup = function()
+{
+	var node = document.createElement(mxClient.VML_PREFIX + ':group');
+	node.style.position = 'absolute';
+	node.style.width = this.node.style.width;
+	node.style.height = this.node.style.height;
+	
+	return node;
+};
+
+/**
+ * Function: redrawShape
+ *
+ * Updates the SVG or VML shape.
+ */
+mxShape.prototype.redrawShape = function()
+{
+	var canvas = this.createCanvas();
+	
+	if (canvas != null)
+	{
+		// Specifies if events should be handled
+		canvas.pointerEvents = this.pointerEvents;
+	
+		this.paint(canvas);
+	
+		if (this.node != canvas.root)
+		{
+			// Forces parsing in IE8 standards mode - slow! avoid
+			this.node.insertAdjacentHTML('beforeend', canvas.root.outerHTML);
+		}
+	
+		if (this.node.nodeName == 'DIV' && document.documentMode == 8)
+		{
+			// Makes DIV transparent to events for IE8 in IE8 standards
+			// mode (Note: Does not work for IE9 in IE8 standards mode
+			// and not for IE11 in enterprise mode)
+			this.node.style.filter = '';
+			
+			// Adds event transparency in IE8 standards
+			mxUtils.addTransparentBackgroundFilter(this.node);
+		}
+		
+		this.destroyCanvas(canvas);
+	}
+};
+
+/**
+ * Function: createCanvas
+ * 
+ * Creates a new canvas for drawing this shape. May return null.
+ */
+mxShape.prototype.createCanvas = function()
+{
+	var canvas = null;
+	
+	// LATER: Check if reusing existing DOM nodes improves performance
+	if (this.node.ownerSVGElement != null)
+	{
+		canvas = this.createSvgCanvas();
+	}
+	else if (mxClient.IS_VML)
+	{
+		this.updateVmlContainer();
+		canvas = this.createVmlCanvas();
+	}
+	
+	if (canvas != null && this.outline)
+	{
+		canvas.setStrokeWidth(this.strokewidth);
+		canvas.setStrokeColor(this.stroke);
+		
+		if (this.isDashed != null)
+		{
+			canvas.setDashed(this.isDashed);
+		}
+		
+		canvas.setStrokeWidth = function() {};
+		canvas.setStrokeColor = function() {};
+		canvas.setFillColor = function() {};
+		canvas.setGradient = function() {};
+		canvas.setDashed = function() {};
+	}
+
+	return canvas;
+};
+
+/**
+ * Function: createSvgCanvas
+ * 
+ * Creates and returns an <mxSvgCanvas2D> for rendering this shape.
+ */
+mxShape.prototype.createSvgCanvas = function()
+{
+	var canvas = new mxSvgCanvas2D(this.node, false);
+	canvas.strokeTolerance = (this.pointerEvents) ? this.svgStrokeTolerance : 0;
+	canvas.pointerEventsValue = this.svgPointerEvents;
+	canvas.blockImagePointerEvents = mxClient.IS_FF;
+	var off = this.getSvgScreenOffset();
+
+	if (off != 0)
+	{
+		this.node.setAttribute('transform', 'translate(' + off + ',' + off + ')');
+	}
+	else
+	{
+		this.node.removeAttribute('transform');
+	}
+	
+	if (!this.antiAlias)
+	{
+		// Rounds all numbers in the SVG output to integers
+		canvas.format = function(value)
+		{
+			return Math.round(parseFloat(value));
+		};
+	}
+	
+	return canvas;
+};
+
+/**
+ * Function: createVmlCanvas
+ * 
+ * Creates and returns an <mxVmlCanvas2D> for rendering this shape.
+ */
+mxShape.prototype.createVmlCanvas = function()
+{
+	// Workaround for VML rendering bug in IE8 standards mode
+	var node = (document.documentMode == 8 && this.isParseVml()) ? this.createVmlGroup() : this.node;
+	var canvas = new mxVmlCanvas2D(node, false);
+	
+	if (node.tagUrn != '')
+	{
+		var w = Math.max(1, Math.round(this.bounds.width));
+		var h = Math.max(1, Math.round(this.bounds.height));
+		node.coordsize = (w * this.vmlScale) + ',' + (h * this.vmlScale);
+		canvas.scale(this.vmlScale);
+		canvas.vmlScale = this.vmlScale;
+	}
+
+	// Painting relative to top, left shape corner
+	var s = this.scale;
+	canvas.translate(-Math.round(this.bounds.x / s), -Math.round(this.bounds.y / s));
+	
+	return canvas;
+};
+
+/**
+ * Function: updateVmlContainer
+ * 
+ * Updates the bounds of the VML container.
+ */
+mxShape.prototype.updateVmlContainer = function()
+{
+	this.node.style.left = Math.round(this.bounds.x) + 'px';
+	this.node.style.top = Math.round(this.bounds.y) + 'px';
+	var w = Math.max(1, Math.round(this.bounds.width));
+	var h = Math.max(1, Math.round(this.bounds.height));
+	this.node.style.width = w + 'px';
+	this.node.style.height = h + 'px';
+	this.node.style.overflow = 'visible';
+};
+
+/**
+ * Function: redrawHtml
+ *
+ * Allow optimization by replacing VML with HTML.
+ */
+mxShape.prototype.redrawHtmlShape = function()
+{
+	// LATER: Refactor methods
+	this.updateHtmlBounds(this.node);
+	this.updateHtmlFilters(this.node);
+	this.updateHtmlColors(this.node);
+};
+
+/**
+ * Function: updateHtmlFilters
+ *
+ * Allow optimization by replacing VML with HTML.
+ */
+mxShape.prototype.updateHtmlFilters = function(node)
+{
+	var f = '';
+	
+	if (this.opacity < 100)
+	{
+		f += 'alpha(opacity=' + (this.opacity) + ')';
+	}
+	
+	if (this.isShadow)
+	{
+		// FIXME: Cannot implement shadow transparency with filter
+		f += 'progid:DXImageTransform.Microsoft.dropShadow (' +
+			'OffX=\'' + Math.round(mxConstants.SHADOW_OFFSET_X * this.scale) + '\', ' +
+			'OffY=\'' + Math.round(mxConstants.SHADOW_OFFSET_Y * this.scale) + '\', ' +
+			'Color=\'' + mxConstants.VML_SHADOWCOLOR + '\')';
+	}
+	
+	if (this.fill != null && this.fill != mxConstants.NONE && this.gradient && this.gradient != mxConstants.NONE)
+	{
+		var start = this.fill;
+		var end = this.gradient;
+		var type = '0';
+		
+		var lookup = {east:0,south:1,west:2,north:3};
+		var dir = (this.direction != null) ? lookup[this.direction] : 0;
+		
+		if (this.gradientDirection != null)
+		{
+			dir = mxUtils.mod(dir + lookup[this.gradientDirection] - 1, 4);
+		}
+
+		if (dir == 1)
+		{
+			type = '1';
+			var tmp = start;
+			start = end;
+			end = tmp;
+		}
+		else if (dir == 2)
+		{
+			var tmp = start;
+			start = end;
+			end = tmp;
+		}
+		else if (dir == 3)
+		{
+			type = '1';
+		}
+		
+		f += 'progid:DXImageTransform.Microsoft.gradient(' +
+			'startColorStr=\'' + start + '\', endColorStr=\'' + end +
+			'\', gradientType=\'' + type + '\')';
+	}
+
+	node.style.filter = f;
+};
+
+/**
+ * Function: mixedModeHtml
+ *
+ * Allow optimization by replacing VML with HTML.
+ */
+mxShape.prototype.updateHtmlColors = function(node)
+{
+	var color = this.stroke;
+	
+	if (color != null && color != mxConstants.NONE)
+	{
+		node.style.borderColor = color;
+
+		if (this.isDashed)
+		{
+			node.style.borderStyle = 'dashed';
+		}
+		else if (this.strokewidth > 0)
+		{
+			node.style.borderStyle = 'solid';
+		}
+
+		node.style.borderWidth = Math.max(1, Math.ceil(this.strokewidth * this.scale)) + 'px';
+	}
+	else
+	{
+		node.style.borderWidth = '0px';
+	}
+
+	color = (this.outline) ? null : this.fill;
+	
+	if (color != null && color != mxConstants.NONE)
+	{
+		node.style.backgroundColor = color;
+		node.style.backgroundImage = 'none';
+	}
+	else if (this.pointerEvents)
+	{
+		 node.style.backgroundColor = 'transparent';
+	}
+	else if (document.documentMode == 8)
+	{
+		mxUtils.addTransparentBackgroundFilter(node);
+	}
+	else
+	{
+		this.setTransparentBackgroundImage(node);
+	}
+};
+
+/**
+ * Function: mixedModeHtml
+ *
+ * Allow optimization by replacing VML with HTML.
+ */
+mxShape.prototype.updateHtmlBounds = function(node)
+{
+	var sw = (document.documentMode >= 9) ? 0 : Math.ceil(this.strokewidth * this.scale);
+	node.style.borderWidth = Math.max(1, sw) + 'px';
+	node.style.overflow = 'hidden';
+	
+	node.style.left = Math.round(this.bounds.x - sw / 2) + 'px';
+	node.style.top = Math.round(this.bounds.y - sw / 2) + 'px';
+
+	if (document.compatMode == 'CSS1Compat')
+	{
+		sw = -sw;
+	}
+	
+	node.style.width = Math.round(Math.max(0, this.bounds.width + sw)) + 'px';
+	node.style.height = Math.round(Math.max(0, this.bounds.height + sw)) + 'px';
+};
+
+/**
+ * Function: destroyCanvas
+ * 
+ * Destroys the given canvas which was used for drawing. This implementation
+ * increments the reference counts on all shared gradients used in the canvas.
+ */
+mxShape.prototype.destroyCanvas = function(canvas)
+{
+	// Manages reference counts
+	if (canvas instanceof mxSvgCanvas2D)
+	{
+		// Increments ref counts
+		for (var key in canvas.gradients)
+		{
+			var gradient = canvas.gradients[key];
+			
+			if (gradient != null)
+			{
+				gradient.mxRefCount = (gradient.mxRefCount || 0) + 1;
+			}
+		}
+		
+		this.releaseSvgGradients(this.oldGradients);
+		this.oldGradients = canvas.gradients;
+	}
+};
+
+/**
+ * Function: paint
+ * 
+ * Generic rendering code.
+ */
+mxShape.prototype.paint = function(c)
+{
+	// Scale is passed-through to canvas
+	var s = this.scale;
+	var x = this.bounds.x / s;
+	var y = this.bounds.y / s;
+	var w = this.bounds.width / s;
+	var h = this.bounds.height / s;
+
+	if (this.isPaintBoundsInverted())
+	{
+		var t = (w - h) / 2;
+		x += t;
+		y -= t;
+		var tmp = w;
+		w = h;
+		h = tmp;
+	}
+	
+	this.updateTransform(c, x, y, w, h);
+	this.configureCanvas(c, x, y, w, h);
+
+	// Adds background rectangle to capture events
+	var bg = null;
+	
+	if ((this.stencil == null && this.points == null && this.shapePointerEvents) ||
+		(this.stencil != null && this.stencilPointerEvents))
+	{
+		var bb = this.createBoundingBox();
+		
+		if (this.dialect == mxConstants.DIALECT_SVG)
+		{
+			bg = this.createTransparentSvgRectangle(bb.x, bb.y, bb.width, bb.height);
+			this.node.appendChild(bg);
+		}
+		else
+		{
+			var rect = c.createRect('rect', bb.x / s, bb.y / s, bb.width / s, bb.height / s);
+			rect.appendChild(c.createTransparentFill());
+			rect.stroked = 'false';
+			c.root.appendChild(rect);
+		}
+	}
+
+	if (this.stencil != null)
+	{
+		this.stencil.drawShape(c, this, x, y, w, h);
+	}
+	else
+	{
+		// Stencils have separate strokewidth
+		c.setStrokeWidth(this.strokewidth);
+		
+		if (this.points != null)
+		{
+			// Paints edge shape
+			var pts = [];
+			
+			for (var i = 0; i < this.points.length; i++)
+			{
+				if (this.points[i] != null)
+				{
+					pts.push(new mxPoint(this.points[i].x / s, this.points[i].y / s));
+				}
+			}
+
+			this.paintEdgeShape(c, pts);
+		}
+		else
+		{
+			// Paints vertex shape
+			this.paintVertexShape(c, x, y, w, h);
+		}
+	}
+	
+	if (bg != null && c.state != null && c.state.transform != null)
+	{
+		bg.setAttribute('transform', c.state.transform);
+	}
+};
+
+/**
+ * Function: configureCanvas
+ * 
+ * Sets the state of the canvas for drawing the shape.
+ */
+mxShape.prototype.configureCanvas = function(c, x, y, w, h)
+{
+	var dash = null;
+	
+	if (this.style != null)
+	{
+		dash = this.style['dashPattern'];		
+	}
+
+	c.setAlpha(this.opacity / 100);
+	c.setFillAlpha(this.fillOpacity / 100);
+	c.setStrokeAlpha(this.strokeOpacity / 100);
+
+	// Sets alpha, colors and gradients
+	if (this.isShadow != null)
+	{
+		c.setShadow(this.isShadow);
+	}
+	
+	// Dash pattern
+	if (this.isDashed != null)
+	{
+		c.setDashed(this.isDashed, (this.style != null) ?
+			mxUtils.getValue(this.style, mxConstants.STYLE_FIX_DASH, false) == 1 : false);
+	}
+
+	if (dash != null)
+	{
+		c.setDashPattern(dash);
+	}
+
+	if (this.fill != null && this.fill != mxConstants.NONE && this.gradient && this.gradient != mxConstants.NONE)
+	{
+		var b = this.getGradientBounds(c, x, y, w, h);
+		c.setGradient(this.fill, this.gradient, b.x, b.y, b.width, b.height, this.gradientDirection);
+	}
+	else
+	{
+		c.setFillColor(this.fill);
+	}
+
+	c.setStrokeColor(this.stroke);
+};
+
+/**
+ * Function: getGradientBounds
+ * 
+ * Returns the bounding box for the gradient box for this shape.
+ */
+mxShape.prototype.getGradientBounds = function(c, x, y, w, h)
+{
+	return new mxRectangle(x, y, w, h);
+};
+
+/**
+ * Function: updateTransform
+ * 
+ * Sets the scale and rotation on the given canvas.
+ */
+mxShape.prototype.updateTransform = function(c, x, y, w, h)
+{
+	// NOTE: Currently, scale is implemented in state and canvas. This will
+	// move to canvas in a later version, so that the states are unscaled
+	// and untranslated and do not need an update after zooming or panning.
+	c.scale(this.scale);
+	c.rotate(this.getShapeRotation(), this.flipH, this.flipV, x + w / 2, y + h / 2);
+};
+
+/**
+ * Function: paintVertexShape
+ * 
+ * Paints the vertex shape.
+ */
+mxShape.prototype.paintVertexShape = function(c, x, y, w, h)
+{
+	this.paintBackground(c, x, y, w, h);
+	c.setShadow(false);
+	this.paintForeground(c, x, y, w, h);
+};
+
+/**
+ * Function: paintBackground
+ * 
+ * Hook for subclassers. This implementation is empty.
+ */
+mxShape.prototype.paintBackground = function(c, x, y, w, h) { };
+
+/**
+ * Function: paintForeground
+ * 
+ * Hook for subclassers. This implementation is empty.
+ */
+mxShape.prototype.paintForeground = function(c, x, y, w, h) { };
+
+/**
+ * Function: paintEdgeShape
+ * 
+ * Hook for subclassers. This implementation is empty.
+ */
+mxShape.prototype.paintEdgeShape = function(c, pts) { };
+
+/**
+ * Function: getArcSize
+ * 
+ * Returns the arc size for the given dimension.
+ */
+mxShape.prototype.getArcSize = function(w, h)
+{
+	var r = 0;
+	
+	if (mxUtils.getValue(this.style, mxConstants.STYLE_ABSOLUTE_ARCSIZE, 0) == '1')
+	{
+		r = Math.min(w / 2, Math.min(h / 2, mxUtils.getValue(this.style,
+			mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2));
+	}
+	else
+	{
+		var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE,
+			mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100;
+		r = Math.min(w * f, h * f);
+	}
+	
+	return r;
+};
+
+/**
+ * Function: paintGlassEffect
+ * 
+ * Paints the glass gradient effect.
+ */
+mxShape.prototype.paintGlassEffect = function(c, x, y, w, h, arc)
+{
+	var sw = Math.ceil(this.strokewidth / 2);
+	var size = 0.4;
+	
+	c.setGradient('#ffffff', '#ffffff', x, y, w, h * 0.6, 'south', 0.9, 0.1);
+	c.begin();
+	arc += 2 * sw;
+		
+	if (this.isRounded)
+	{
+		c.moveTo(x - sw + arc, y - sw);
+		c.quadTo(x - sw, y - sw, x - sw, y - sw + arc);
+		c.lineTo(x - sw, y + h * size);
+		c.quadTo(x + w * 0.5, y + h * 0.7, x + w + sw, y + h * size);
+		c.lineTo(x + w + sw, y - sw + arc);
+		c.quadTo(x + w + sw, y - sw, x + w + sw - arc, y - sw);
+	}
+	else
+	{
+		c.moveTo(x - sw, y - sw);
+		c.lineTo(x - sw, y + h * size);
+		c.quadTo(x + w * 0.5, y + h * 0.7, x + w + sw, y + h * size);
+		c.lineTo(x + w + sw, y - sw);
+	}
+	
+	c.close();
+	c.fill();
+};
+
+/**
+ * Function: addPoints
+ * 
+ * Paints the given points with rounded corners.
+ */
+mxShape.prototype.addPoints = function(c, pts, rounded, arcSize, close, exclude, initialMove)
+{
+	if (pts != null && pts.length > 0)
+	{
+		initialMove = (initialMove != null) ? initialMove : true;
+		var pe = pts[pts.length - 1];
+		
+		// Adds virtual waypoint in the center between start and end point
+		if (close && rounded)
+		{
+			pts = pts.slice();
+			var p0 = pts[0];
+			var wp = new mxPoint(pe.x + (p0.x - pe.x) / 2, pe.y + (p0.y - pe.y) / 2);
+			pts.splice(0, 0, wp);
+		}
+	
+		var pt = pts[0];
+		var i = 1;
+	
+		// Draws the line segments
+		if (initialMove)
+		{
+			c.moveTo(pt.x, pt.y);
+		}
+		else
+		{
+			c.lineTo(pt.x, pt.y);
+		}
+		
+		while (i < ((close) ? pts.length : pts.length - 1))
+		{
+			var tmp = pts[mxUtils.mod(i, pts.length)];
+			var dx = pt.x - tmp.x;
+			var dy = pt.y - tmp.y;
+	
+			if (rounded && (dx != 0 || dy != 0) && (exclude == null || mxUtils.indexOf(exclude, i - 1) < 0))
+			{
+				// Draws a line from the last point to the current
+				// point with a spacing of size off the current point
+				// into direction of the last point
+				var dist = Math.sqrt(dx * dx + dy * dy);
+				var nx1 = dx * Math.min(arcSize, dist / 2) / dist;
+				var ny1 = dy * Math.min(arcSize, dist / 2) / dist;
+	
+				var x1 = tmp.x + nx1;
+				var y1 = tmp.y + ny1;
+				c.lineTo(x1, y1);
+	
+				// Draws a curve from the last point to the current
+				// point with a spacing of size off the current point
+				// into direction of the next point
+				var next = pts[mxUtils.mod(i + 1, pts.length)];
+				
+				// Uses next non-overlapping point
+				while (i < pts.length - 2 && Math.round(next.x - tmp.x) == 0 && Math.round(next.y - tmp.y) == 0)
+				{
+					next = pts[mxUtils.mod(i + 2, pts.length)];
+					i++;
+				}
+				
+				dx = next.x - tmp.x;
+				dy = next.y - tmp.y;
+	
+				dist = Math.max(1, Math.sqrt(dx * dx + dy * dy));
+				var nx2 = dx * Math.min(arcSize, dist / 2) / dist;
+				var ny2 = dy * Math.min(arcSize, dist / 2) / dist;
+	
+				var x2 = tmp.x + nx2;
+				var y2 = tmp.y + ny2;
+	
+				c.quadTo(tmp.x, tmp.y, x2, y2);
+				tmp = new mxPoint(x2, y2);
+			}
+			else
+			{
+				c.lineTo(tmp.x, tmp.y);
+			}
+	
+			pt = tmp;
+			i++;
+		}
+	
+		if (close)
+		{
+			c.close();
+		}
+		else
+		{
+			c.lineTo(pe.x, pe.y);
+		}
+	}
+};
+
+/**
+ * Function: resetStyles
+ * 
+ * Resets all styles.
+ */
+mxShape.prototype.resetStyles = function()
+{
+	this.initStyles();
+
+	this.spacing = 0;
+	
+	delete this.fill;
+	delete this.gradient;
+	delete this.gradientDirection;
+	delete this.stroke;
+	delete this.startSize;
+	delete this.endSize;
+	delete this.startArrow;
+	delete this.endArrow;
+	delete this.direction;
+	delete this.isShadow;
+	delete this.isDashed;
+	delete this.isRounded;
+	delete this.glass;
+};
+
+/**
+ * Function: apply
+ * 
+ * Applies the style of the given <mxCellState> to the shape. This
+ * implementation assigns the following styles to local fields:
+ * 
+ * - <mxConstants.STYLE_FILLCOLOR> => fill
+ * - <mxConstants.STYLE_GRADIENTCOLOR> => gradient
+ * - <mxConstants.STYLE_GRADIENT_DIRECTION> => gradientDirection
+ * - <mxConstants.STYLE_OPACITY> => opacity
+ * - <mxConstants.STYLE_FILL_OPACITY> => fillOpacity
+ * - <mxConstants.STYLE_STROKE_OPACITY> => strokeOpacity
+ * - <mxConstants.STYLE_STROKECOLOR> => stroke
+ * - <mxConstants.STYLE_STROKEWIDTH> => strokewidth
+ * - <mxConstants.STYLE_SHADOW> => isShadow
+ * - <mxConstants.STYLE_DASHED> => isDashed
+ * - <mxConstants.STYLE_SPACING> => spacing
+ * - <mxConstants.STYLE_STARTSIZE> => startSize
+ * - <mxConstants.STYLE_ENDSIZE> => endSize
+ * - <mxConstants.STYLE_ROUNDED> => isRounded
+ * - <mxConstants.STYLE_STARTARROW> => startArrow
+ * - <mxConstants.STYLE_ENDARROW> => endArrow
+ * - <mxConstants.STYLE_ROTATION> => rotation
+ * - <mxConstants.STYLE_DIRECTION> => direction
+ * - <mxConstants.STYLE_GLASS> => glass
+ *
+ * This keeps a reference to the <style>. If you need to keep a reference to
+ * the cell, you can override this method and store a local reference to
+ * state.cell or the <mxCellState> itself. If <outline> should be true, make
+ * sure to set it before calling this method.
+ *
+ * Parameters:
+ *
+ * state - <mxCellState> of the corresponding cell.
+ */
+mxShape.prototype.apply = function(state)
+{
+	this.state = state;
+	this.style = state.style;
+
+	if (this.style != null)
+	{
+		this.fill = mxUtils.getValue(this.style, mxConstants.STYLE_FILLCOLOR, this.fill);
+		this.gradient = mxUtils.getValue(this.style, mxConstants.STYLE_GRADIENTCOLOR, this.gradient);
+		this.gradientDirection = mxUtils.getValue(this.style, mxConstants.STYLE_GRADIENT_DIRECTION, this.gradientDirection);
+		this.opacity = mxUtils.getValue(this.style, mxConstants.STYLE_OPACITY, this.opacity);
+		this.fillOpacity = mxUtils.getValue(this.style, mxConstants.STYLE_FILL_OPACITY, this.fillOpacity);
+		this.strokeOpacity = mxUtils.getValue(this.style, mxConstants.STYLE_STROKE_OPACITY, this.strokeOpacity);
+		this.stroke = mxUtils.getValue(this.style, mxConstants.STYLE_STROKECOLOR, this.stroke);
+		this.strokewidth = mxUtils.getNumber(this.style, mxConstants.STYLE_STROKEWIDTH, this.strokewidth);
+		this.spacing = mxUtils.getValue(this.style, mxConstants.STYLE_SPACING, this.spacing);
+		this.startSize = mxUtils.getNumber(this.style, mxConstants.STYLE_STARTSIZE, this.startSize);
+		this.endSize = mxUtils.getNumber(this.style, mxConstants.STYLE_ENDSIZE, this.endSize);
+		this.startArrow = mxUtils.getValue(this.style, mxConstants.STYLE_STARTARROW, this.startArrow);
+		this.endArrow = mxUtils.getValue(this.style, mxConstants.STYLE_ENDARROW, this.endArrow);
+		this.rotation = mxUtils.getValue(this.style, mxConstants.STYLE_ROTATION, this.rotation);
+		this.direction = mxUtils.getValue(this.style, mxConstants.STYLE_DIRECTION, this.direction);
+		this.flipH = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPH, 0) == 1;
+		this.flipV = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPV, 0) == 1;	
+		
+		// Legacy support for stencilFlipH/V
+		if (this.stencil != null)
+		{
+			this.flipH = mxUtils.getValue(this.style, 'stencilFlipH', 0) == 1 || this.flipH;
+			this.flipV = mxUtils.getValue(this.style, 'stencilFlipV', 0) == 1 || this.flipV;
+		}
+		
+		if (this.direction == mxConstants.DIRECTION_NORTH || this.direction == mxConstants.DIRECTION_SOUTH)
+		{
+			var tmp = this.flipH;
+			this.flipH = this.flipV;
+			this.flipV = tmp;
+		}
+
+		this.isShadow = mxUtils.getValue(this.style, mxConstants.STYLE_SHADOW, this.isShadow) == 1;
+		this.isDashed = mxUtils.getValue(this.style, mxConstants.STYLE_DASHED, this.isDashed) == 1;
+		this.isRounded = mxUtils.getValue(this.style, mxConstants.STYLE_ROUNDED, this.isRounded) == 1;
+		this.glass = mxUtils.getValue(this.style, mxConstants.STYLE_GLASS, this.glass) == 1;
+		
+		if (this.fill == mxConstants.NONE)
+		{
+			this.fill = null;
+		}
+
+		if (this.gradient == mxConstants.NONE)
+		{
+			this.gradient = null;
+		}
+
+		if (this.stroke == mxConstants.NONE)
+		{
+			this.stroke = null;
+		}
+	}
+};
+
+/**
+ * Function: setCursor
+ * 
+ * Sets the cursor on the given shape.
+ *
+ * Parameters:
+ *
+ * cursor - The cursor to be used.
+ */
+mxShape.prototype.setCursor = function(cursor)
+{
+	if (cursor == null)
+	{
+		cursor = '';
+	}
+	
+	this.cursor = cursor;
+
+	if (this.node != null)
+	{
+		this.node.style.cursor = cursor;
+	}
+};
+
+/**
+ * Function: getCursor
+ * 
+ * Returns the current cursor.
+ */
+mxShape.prototype.getCursor = function()
+{
+	return this.cursor;
+};
+
+/**
+ * Function: updateBoundingBox
+ *
+ * Updates the <boundingBox> for this shape using <createBoundingBox> and
+ * <augmentBoundingBox> and stores the result in <boundingBox>.
+ */
+mxShape.prototype.updateBoundingBox = function()
+{
+	// Tries to get bounding box from SVG subsystem
+	// LATER: Use getBoundingClientRect for fallback in VML
+	if (this.useSvgBoundingBox && this.node != null && this.node.ownerSVGElement != null)
+	{
+		try
+		{
+			var b = this.node.getBBox();
+	
+			if (b.width > 0 && b.height > 0)
+			{
+				this.boundingBox = new mxRectangle(b.x, b.y, b.width, b.height);
+				
+				// Adds strokeWidth
+				this.boundingBox.grow(this.strokewidth * this.scale / 2);
+				
+				return;
+			}
+		}
+		catch(e)
+		{
+			// fallback to code below
+		}
+	}
+
+	if (this.bounds != null)
+	{
+		var bbox = this.createBoundingBox();
+		
+		if (bbox != null)
+		{
+			this.augmentBoundingBox(bbox);
+			var rot = this.getShapeRotation();
+			
+			if (rot != 0)
+			{
+				bbox = mxUtils.getBoundingBox(bbox, rot);
+			}
+		}
+
+		this.boundingBox = bbox;
+	}
+};
+
+/**
+ * Function: createBoundingBox
+ *
+ * Returns a new rectangle that represents the bounding box of the bare shape
+ * with no shadows or strokewidths.
+ */
+mxShape.prototype.createBoundingBox = function()
+{
+	var bb = this.bounds.clone();
+
+	if ((this.stencil != null && (this.direction == mxConstants.DIRECTION_NORTH ||
+		this.direction == mxConstants.DIRECTION_SOUTH)) || this.isPaintBoundsInverted())
+	{
+		bb.rotate90();
+	}
+	
+	return bb;
+};
+
+/**
+ * Function: augmentBoundingBox
+ *
+ * Augments the bounding box with the strokewidth and shadow offsets.
+ */
+mxShape.prototype.augmentBoundingBox = function(bbox)
+{
+	if (this.isShadow)
+	{
+		bbox.width += Math.ceil(mxConstants.SHADOW_OFFSET_X * this.scale);
+		bbox.height += Math.ceil(mxConstants.SHADOW_OFFSET_Y * this.scale);
+	}
+	
+	// Adds strokeWidth
+	bbox.grow(this.strokewidth * this.scale / 2);
+};
+
+/**
+ * Function: isPaintBoundsInverted
+ * 
+ * Returns true if the bounds should be inverted.
+ */
+mxShape.prototype.isPaintBoundsInverted = function()
+{
+	// Stencil implements inversion via aspect
+	return this.stencil == null && (this.direction == mxConstants.DIRECTION_NORTH ||
+			this.direction == mxConstants.DIRECTION_SOUTH);
+};
+
+/**
+ * Function: getRotation
+ * 
+ * Returns the rotation from the style.
+ */
+mxShape.prototype.getRotation = function()
+{
+	return (this.rotation != null) ? this.rotation : 0;
+};
+
+/**
+ * Function: getTextRotation
+ * 
+ * Returns the rotation for the text label.
+ */
+mxShape.prototype.getTextRotation = function()
+{
+	var rot = this.getRotation();
+	
+	if (mxUtils.getValue(this.style, mxConstants.STYLE_HORIZONTAL, 1) != 1)
+	{
+		rot += mxText.prototype.verticalTextRotation;
+	}
+	
+	return rot;
+};
+
+/**
+ * Function: getShapeRotation
+ * 
+ * Returns the actual rotation of the shape.
+ */
+mxShape.prototype.getShapeRotation = function()
+{
+	var rot = this.getRotation();
+	
+	if (this.direction != null)
+	{
+		if (this.direction == mxConstants.DIRECTION_NORTH)
+		{
+			rot += 270;
+		}
+		else if (this.direction == mxConstants.DIRECTION_WEST)
+		{
+			rot += 180;
+		}
+		else if (this.direction == mxConstants.DIRECTION_SOUTH)
+		{
+			rot += 90;
+		}
+	}
+	
+	return rot;
+};
+
+/**
+ * Function: createTransparentSvgRectangle
+ * 
+ * Adds a transparent rectangle that catches all events.
+ */
+mxShape.prototype.createTransparentSvgRectangle = function(x, y, w, h)
+{
+	var rect = document.createElementNS(mxConstants.NS_SVG, 'rect');
+	rect.setAttribute('x', x);
+	rect.setAttribute('y', y);
+	rect.setAttribute('width', w);
+	rect.setAttribute('height', h);
+	rect.setAttribute('fill', 'none');
+	rect.setAttribute('stroke', 'none');
+	rect.setAttribute('pointer-events', 'all');
+	
+	return rect;
+};
+
+/**
+ * Function: setTransparentBackgroundImage
+ * 
+ * Sets a transparent background CSS style to catch all events.
+ * 
+ * Paints the line shape.
+ */
+mxShape.prototype.setTransparentBackgroundImage = function(node)
+{
+	node.style.backgroundImage = 'url(\'' + mxClient.imageBasePath + '/transparent.gif\')';
+};
+
+/**
+ * Function: releaseSvgGradients
+ * 
+ * Paints the line shape.
+ */
+mxShape.prototype.releaseSvgGradients = function(grads)
+{
+	if (grads != null)
+	{
+		for (var key in grads)
+		{
+			var gradient = grads[key];
+			
+			if (gradient != null)
+			{
+				gradient.mxRefCount = (gradient.mxRefCount || 0) - 1;
+				
+				if (gradient.mxRefCount == 0 && gradient.parentNode != null)
+				{
+					gradient.parentNode.removeChild(gradient);
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: destroy
+ *
+ * Destroys the shape by removing it from the DOM and releasing the DOM
+ * node associated with the shape using <mxEvent.release>.
+ */
+mxShape.prototype.destroy = function()
+{
+	if (this.node != null)
+	{
+		mxEvent.release(this.node);
+		
+		if (this.node.parentNode != null)
+		{
+			this.node.parentNode.removeChild(this.node);
+		}
+		
+		this.node = null;
+	}
+	
+	// Decrements refCount and removes unused
+	this.releaseSvgGradients(this.oldGradients);
+	this.oldGradients = null;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ * 
+ * Code to add stencils.
+ * 
+ * (code)
+ * var req = mxUtils.load('test/stencils.xml');
+ * var root = req.getDocumentElement();
+ * var shape = root.firstChild;
+ * 
+ * while (shape != null)
+ * {
+ * 	 if (shape.nodeType == mxConstants.NODETYPE_ELEMENT)
+ *   {
+ *     mxStencilRegistry.addStencil(shape.getAttribute('name'), new mxStencil(shape));
+ *   }
+ *   
+ *   shape = shape.nextSibling;
+ * }
+ * (end)
+ */
+var mxStencilRegistry =
+{
+	/**
+	 * Class: mxStencilRegistry
+	 * 
+	 * A singleton class that provides a registry for stencils and the methods
+	 * for painting those stencils onto a canvas or into a DOM.
+	 */
+	stencils: {},
+	
+	/**
+	 * Function: addStencil
+	 * 
+	 * Adds the given <mxStencil>.
+	 */
+	addStencil: function(name, stencil)
+	{
+		mxStencilRegistry.stencils[name] = stencil;
+	},
+	
+	/**
+	 * Function: getStencil
+	 * 
+	 * Returns the <mxStencil> for the given name.
+	 */
+	getStencil: function(name)
+	{
+		return mxStencilRegistry.stencils[name];
+	}
+
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxMarker =
+{
+	/**
+	 * Class: mxMarker
+	 * 
+	 * A static class that implements all markers for VML and SVG using a
+	 * registry. NOTE: The signatures in this class will change.
+	 * 
+	 * Variable: markers
+	 * 
+	 * Maps from markers names to functions to paint the markers.
+	 */
+	markers: [],
+	
+	/**
+	 * Function: addMarker
+	 * 
+	 * Adds a factory method that updates a given endpoint and returns a
+	 * function to paint the marker onto the given canvas.
+	 */
+	addMarker: function(type, funct)
+	{
+		mxMarker.markers[type] = funct;
+	},
+	
+	/**
+	 * Function: createMarker
+	 * 
+	 * Returns a function to paint the given marker.
+	 */
+	createMarker: function(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled)
+	{
+		var funct = mxMarker.markers[type];
+		
+		return (funct != null) ? funct(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) : null;
+	}
+
+};
+
+/**
+ * Adds the classic and block marker factory method.
+ */
+(function()
+{
+	function createArrow(widthFactor)
+	{
+		widthFactor = (widthFactor != null) ? widthFactor : 2;
+		
+		return function(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled)
+		{
+			// The angle of the forward facing arrow sides against the x axis is
+			// 26.565 degrees, 1/sin(26.565) = 2.236 / 2 = 1.118 ( / 2 allows for
+			// only half the strokewidth is processed ).
+			var endOffsetX = unitX * sw * 1.118;
+			var endOffsetY = unitY * sw * 1.118;
+			
+			unitX = unitX * (size + sw);
+			unitY = unitY * (size + sw);
+	
+			var pt = pe.clone();
+			pt.x -= endOffsetX;
+			pt.y -= endOffsetY;
+			
+			var f = (type != mxConstants.ARROW_CLASSIC && type != mxConstants.ARROW_CLASSIC_THIN) ? 1 : 3 / 4;
+			pe.x += -unitX * f - endOffsetX;
+			pe.y += -unitY * f - endOffsetY;
+			
+			return function()
+			{
+				canvas.begin();
+				canvas.moveTo(pt.x, pt.y);
+				canvas.lineTo(pt.x - unitX - unitY / widthFactor, pt.y - unitY + unitX / widthFactor);
+			
+				if (type == mxConstants.ARROW_CLASSIC || type == mxConstants.ARROW_CLASSIC_THIN)
+				{
+					canvas.lineTo(pt.x - unitX * 3 / 4, pt.y - unitY * 3 / 4);
+				}
+			
+				canvas.lineTo(pt.x + unitY / widthFactor - unitX, pt.y - unitY - unitX / widthFactor);
+				canvas.close();
+	
+				if (filled)
+				{
+					canvas.fillAndStroke();
+				}
+				else
+				{
+					canvas.stroke();
+				}
+			};
+		}
+	};
+	
+	mxMarker.addMarker('classic', createArrow(2));
+	mxMarker.addMarker('classicThin', createArrow(3));
+	mxMarker.addMarker('block', createArrow(2));
+	mxMarker.addMarker('blockThin', createArrow(3));
+	
+	function createOpenArrow(widthFactor)
+	{
+		widthFactor = (widthFactor != null) ? widthFactor : 2;
+		
+		return function(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled)
+		{
+			// The angle of the forward facing arrow sides against the x axis is
+			// 26.565 degrees, 1/sin(26.565) = 2.236 / 2 = 1.118 ( / 2 allows for
+			// only half the strokewidth is processed ).
+			var endOffsetX = unitX * sw * 1.118;
+			var endOffsetY = unitY * sw * 1.118;
+			
+			unitX = unitX * (size + sw);
+			unitY = unitY * (size + sw);
+			
+			var pt = pe.clone();
+			pt.x -= endOffsetX;
+			pt.y -= endOffsetY;
+			
+			pe.x += -endOffsetX * 2;
+			pe.y += -endOffsetY * 2;
+
+			return function()
+			{
+				canvas.begin();
+				canvas.moveTo(pt.x - unitX - unitY / widthFactor, pt.y - unitY + unitX / widthFactor);
+				canvas.lineTo(pt.x, pt.y);
+				canvas.lineTo(pt.x + unitY / widthFactor - unitX, pt.y - unitY - unitX / widthFactor);
+				canvas.stroke();
+			};
+		}
+	};
+	
+	mxMarker.addMarker('open', createOpenArrow(2));
+	mxMarker.addMarker('openThin', createOpenArrow(3));
+	
+	mxMarker.addMarker('oval', function(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled)
+	{
+		var a = size / 2;
+		
+		var pt = pe.clone();
+		pe.x -= unitX * a;
+		pe.y -= unitY * a;
+
+		return function()
+		{
+			canvas.ellipse(pt.x - a, pt.y - a, size, size);
+						
+			if (filled)
+			{
+				canvas.fillAndStroke();
+			}
+			else
+			{
+				canvas.stroke();
+			}
+		};
+	});
+
+	function diamond(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled)
+	{
+		// The angle of the forward facing arrow sides against the x axis is
+		// 45 degrees, 1/sin(45) = 1.4142 / 2 = 0.7071 ( / 2 allows for
+		// only half the strokewidth is processed ). Or 0.9862 for thin diamond.
+		// Note these values and the tk variable below are dependent, update
+		// both together (saves trig hard coding it).
+		var swFactor = (type == mxConstants.ARROW_DIAMOND) ?  0.7071 : 0.9862;
+		var endOffsetX = unitX * sw * swFactor;
+		var endOffsetY = unitY * sw * swFactor;
+		
+		unitX = unitX * (size + sw);
+		unitY = unitY * (size + sw);
+		
+		var pt = pe.clone();
+		pt.x -= endOffsetX;
+		pt.y -= endOffsetY;
+		
+		pe.x += -unitX - endOffsetX;
+		pe.y += -unitY - endOffsetY;
+		
+		// thickness factor for diamond
+		var tk = ((type == mxConstants.ARROW_DIAMOND) ?  2 : 3.4);
+		
+		return function()
+		{
+			canvas.begin();
+			canvas.moveTo(pt.x, pt.y);
+			canvas.lineTo(pt.x - unitX / 2 - unitY / tk, pt.y + unitX / tk - unitY / 2);
+			canvas.lineTo(pt.x - unitX, pt.y - unitY);
+			canvas.lineTo(pt.x - unitX / 2 + unitY / tk, pt.y - unitY / 2 - unitX / tk);
+			canvas.close();
+			
+			if (filled)
+			{
+				canvas.fillAndStroke();
+			}
+			else
+			{
+				canvas.stroke();
+			}
+		};
+	};
+
+	mxMarker.addMarker('diamond', diamond);
+	mxMarker.addMarker('diamondThin', diamond);
+})();
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxActor
+ *
+ * Extends <mxShape> to implement an actor shape. If a custom shape with one
+ * filled area is needed, then this shape's <redrawPath> should be overridden.
+ * 
+ * Example:
+ * 
+ * (code)
+ * function SampleShape() { }
+ * 
+ * SampleShape.prototype = new mxActor();
+ * SampleShape.prototype.constructor = vsAseShape;
+ * 
+ * mxCellRenderer.prototype.defaultShapes['sample'] = SampleShape;
+ * SampleShape.prototype.redrawPath = function(path, x, y, w, h)
+ * {
+ *   path.moveTo(0, 0);
+ *   path.lineTo(w, h);
+ *   // ...
+ *   path.close();
+ * }
+ * (end)
+ * 
+ * This shape is registered under <mxConstants.SHAPE_ACTOR> in
+ * <mxCellRenderer>.
+ * 
+ * Constructor: mxActor
+ *
+ * Constructs a new actor shape.
+ * 
+ * Parameters:
+ * 
+ * bounds - <mxRectangle> that defines the bounds. This is stored in
+ * <mxShape.bounds>.
+ * fill - String that defines the fill color. This is stored in <fill>.
+ * stroke - String that defines the stroke color. This is stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ */
+function mxActor(bounds, fill, stroke, strokewidth)
+{
+	mxShape.call(this);
+	this.bounds = bounds;
+	this.fill = fill;
+	this.stroke = stroke;
+	this.strokewidth = (strokewidth != null) ? strokewidth : 1;
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxActor, mxShape);
+
+/**
+ * Function: paintVertexShape
+ * 
+ * Redirects to redrawPath for subclasses to work.
+ */
+mxActor.prototype.paintVertexShape = function(c, x, y, w, h)
+{
+	c.translate(x, y);
+	c.begin();
+	this.redrawPath(c, x, y, w, h);
+	c.fillAndStroke();
+};
+
+/**
+ * Function: redrawPath
+ *
+ * Draws the path for this shape.
+ */
+mxActor.prototype.redrawPath = function(c, x, y, w, h)
+{
+	var width = w/3;
+	c.moveTo(0, h);
+	c.curveTo(0, 3 * h / 5, 0, 2 * h / 5, w / 2, 2 * h / 5);
+	c.curveTo(w / 2 - width, 2 * h / 5, w / 2 - width, 0, w / 2, 0);
+	c.curveTo(w / 2 + width, 0, w / 2 + width, 2 * h / 5, w / 2, 2 * h / 5);
+	c.curveTo(w, 2 * h / 5, w, 3 * h / 5, w, h);
+	c.close();
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCloud
+ *
+ * Extends <mxActor> to implement a cloud shape.
+ * 
+ * This shape is registered under <mxConstants.SHAPE_CLOUD> in
+ * <mxCellRenderer>.
+ * 
+ * Constructor: mxCloud
+ *
+ * Constructs a new cloud shape.
+ * 
+ * Parameters:
+ * 
+ * bounds - <mxRectangle> that defines the bounds. This is stored in
+ * <mxShape.bounds>.
+ * fill - String that defines the fill color. This is stored in <fill>.
+ * stroke - String that defines the stroke color. This is stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ */
+function mxCloud(bounds, fill, stroke, strokewidth)
+{
+	mxActor.call(this);
+	this.bounds = bounds;
+	this.fill = fill;
+	this.stroke = stroke;
+	this.strokewidth = (strokewidth != null) ? strokewidth : 1;
+};
+
+/**
+ * Extends mxActor.
+ */
+mxUtils.extend(mxCloud, mxActor);
+
+/**
+ * Function: redrawPath
+ *
+ * Draws the path for this shape.
+ */
+mxCloud.prototype.redrawPath = function(c, x, y, w, h)
+{
+	c.moveTo(0.25 * w, 0.25 * h);
+	c.curveTo(0.05 * w, 0.25 * h, 0, 0.5 * h, 0.16 * w, 0.55 * h);
+	c.curveTo(0, 0.66 * h, 0.18 * w, 0.9 * h, 0.31 * w, 0.8 * h);
+	c.curveTo(0.4 * w, h, 0.7 * w, h, 0.8 * w, 0.8 * h);
+	c.curveTo(w, 0.8 * h, w, 0.6 * h, 0.875 * w, 0.5 * h);
+	c.curveTo(w, 0.3 * h, 0.8 * w, 0.1 * h, 0.625 * w, 0.2 * h);
+	c.curveTo(0.5 * w, 0.05 * h, 0.3 * w, 0.05 * h, 0.25 * w, 0.25 * h);
+	c.close();
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxRectangleShape
+ *
+ * Extends <mxShape> to implement a rectangle shape.
+ * This shape is registered under <mxConstants.SHAPE_RECTANGLE>
+ * in <mxCellRenderer>.
+ * 
+ * Constructor: mxRectangleShape
+ *
+ * Constructs a new rectangle shape.
+ * 
+ * Parameters:
+ * 
+ * bounds - <mxRectangle> that defines the bounds. This is stored in
+ * <mxShape.bounds>.
+ * fill - String that defines the fill color. This is stored in <fill>.
+ * stroke - String that defines the stroke color. This is stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ */
+function mxRectangleShape(bounds, fill, stroke, strokewidth)
+{
+	mxShape.call(this);
+	this.bounds = bounds;
+	this.fill = fill;
+	this.stroke = stroke;
+	this.strokewidth = (strokewidth != null) ? strokewidth : 1;
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxRectangleShape, mxShape);
+
+/**
+ * Function: isHtmlAllowed
+ *
+ * Returns true for non-rounded, non-rotated shapes with no glass gradient.
+ */
+mxRectangleShape.prototype.isHtmlAllowed = function()
+{
+	var events = true;
+	
+	if (this.style != null)
+	{
+		events = mxUtils.getValue(this.style, mxConstants.STYLE_POINTER_EVENTS, '1') == '1';		
+	}
+	
+	return !this.isRounded && !this.glass && this.rotation == 0 && (events ||
+		(this.fill != null && this.fill != mxConstants.NONE));
+};
+
+/**
+ * Function: paintBackground
+ * 
+ * Generic background painting implementation.
+ */
+mxRectangleShape.prototype.paintBackground = function(c, x, y, w, h)
+{
+	var events = true;
+	
+	if (this.style != null)
+	{
+		events = mxUtils.getValue(this.style, mxConstants.STYLE_POINTER_EVENTS, '1') == '1';		
+	}
+	
+	if (events || (this.fill != null && this.fill != mxConstants.NONE) ||
+		(this.stroke != null && this.stroke != mxConstants.NONE))
+	{
+		if (!events && (this.fill == null || this.fill == mxConstants.NONE))
+		{
+			c.pointerEvents = false;
+		}
+		
+		if (this.isRounded)
+		{
+			var r = 0;
+			
+			if (mxUtils.getValue(this.style, mxConstants.STYLE_ABSOLUTE_ARCSIZE, 0) == '1')
+			{
+				r = Math.min(w / 2, Math.min(h / 2, mxUtils.getValue(this.style,
+					mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2));
+			}
+			else
+			{
+				var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE,
+					mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100;
+				r = Math.min(w * f, h * f);
+			}
+			
+			c.roundrect(x, y, w, h, r, r);
+		}
+		else
+		{
+			c.rect(x, y, w, h);
+		}
+			
+		c.fillAndStroke();
+	}
+};
+
+/**
+ * Function: paintForeground
+ * 
+ * Generic background painting implementation.
+ */
+mxRectangleShape.prototype.paintForeground = function(c, x, y, w, h)
+{
+	if (this.glass && !this.outline && this.fill != null && this.fill != mxConstants.NONE)
+	{
+		this.paintGlassEffect(c, x, y, w, h, this.getArcSize(w + this.strokewidth, h + this.strokewidth));
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxEllipse
+ *
+ * Extends <mxShape> to implement an ellipse shape.
+ * This shape is registered under <mxConstants.SHAPE_ELLIPSE>
+ * in <mxCellRenderer>.
+ * 
+ * Constructor: mxEllipse
+ *
+ * Constructs a new ellipse shape.
+ * 
+ * Parameters:
+ * 
+ * bounds - <mxRectangle> that defines the bounds. This is stored in
+ * <mxShape.bounds>.
+ * fill - String that defines the fill color. This is stored in <fill>.
+ * stroke - String that defines the stroke color. This is stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ */
+function mxEllipse(bounds, fill, stroke, strokewidth)
+{
+	mxShape.call(this);
+	this.bounds = bounds;
+	this.fill = fill;
+	this.stroke = stroke;
+	this.strokewidth = (strokewidth != null) ? strokewidth : 1;
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxEllipse, mxShape);
+
+/**
+ * Function: paintVertexShape
+ * 
+ * Paints the ellipse shape.
+ */
+mxEllipse.prototype.paintVertexShape = function(c, x, y, w, h)
+{
+	c.ellipse(x, y, w, h);
+	c.fillAndStroke();
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDoubleEllipse
+ *
+ * Extends <mxShape> to implement a double ellipse shape. This shape is
+ * registered under <mxConstants.SHAPE_DOUBLE_ELLIPSE> in <mxCellRenderer>.
+ * Use the following override to only fill the inner ellipse in this shape:
+ * 
+ * (code)
+ * mxDoubleEllipse.prototype.paintVertexShape = function(c, x, y, w, h)
+ * {
+ *   c.ellipse(x, y, w, h);
+ *   c.stroke();
+ *   
+ *   var inset = mxUtils.getValue(this.style, mxConstants.STYLE_MARGIN, Math.min(3 + this.strokewidth, Math.min(w / 5, h / 5)));
+ *   x += inset;
+ *   y += inset;
+ *   w -= 2 * inset;
+ *   h -= 2 * inset;
+ *   
+ *   if (w > 0 && h > 0)
+ *   {
+ *     c.ellipse(x, y, w, h);
+ *   }
+ *   
+ *   c.fillAndStroke();
+ * };
+ * (end)
+ * 
+ * Constructor: mxDoubleEllipse
+ *
+ * Constructs a new ellipse shape.
+ *
+ * Parameters:
+ *
+ * bounds - <mxRectangle> that defines the bounds. This is stored in
+ * <mxShape.bounds>.
+ * fill - String that defines the fill color. This is stored in <fill>.
+ * stroke - String that defines the stroke color. This is stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ */
+function mxDoubleEllipse(bounds, fill, stroke, strokewidth)
+{
+	mxShape.call(this);
+	this.bounds = bounds;
+	this.fill = fill;
+	this.stroke = stroke;
+	this.strokewidth = (strokewidth != null) ? strokewidth : 1;
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxDoubleEllipse, mxShape);
+
+/**
+ * Variable: vmlScale
+ * 
+ * Scale for improving the precision of VML rendering. Default is 10.
+ */
+mxDoubleEllipse.prototype.vmlScale = 10;
+
+/**
+ * Function: paintBackground
+ * 
+ * Paints the background.
+ */
+mxDoubleEllipse.prototype.paintBackground = function(c, x, y, w, h)
+{
+	c.ellipse(x, y, w, h);
+	c.fillAndStroke();
+};
+
+/**
+ * Function: paintForeground
+ * 
+ * Paints the foreground.
+ */
+mxDoubleEllipse.prototype.paintForeground = function(c, x, y, w, h)
+{
+	if (!this.outline)
+	{
+		var margin = mxUtils.getValue(this.style, mxConstants.STYLE_MARGIN, Math.min(3 + this.strokewidth, Math.min(w / 5, h / 5)));
+		x += margin;
+		y += margin;
+		w -= 2 * margin;
+		h -= 2 * margin;
+		
+		// FIXME: Rounding issues in IE8 standards mode (not in 1.x)
+		if (w > 0 && h > 0)
+		{
+			c.ellipse(x, y, w, h);
+		}
+		
+		c.stroke();
+	}
+};
+
+/**
+ * Function: getLabelBounds
+ * 
+ * Returns the bounds for the label.
+ */
+mxDoubleEllipse.prototype.getLabelBounds = function(rect)
+{
+	var margin = (mxUtils.getValue(this.style, mxConstants.STYLE_MARGIN, Math.min(3 + this.strokewidth,
+			Math.min(rect.width / 5 / this.scale, rect.height / 5 / this.scale)))) * this.scale;
+
+	return new mxRectangle(rect.x + margin, rect.y + margin, rect.width - 2 * margin, rect.height - 2 * margin);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxRhombus
+ *
+ * Extends <mxShape> to implement a rhombus (aka diamond) shape.
+ * This shape is registered under <mxConstants.SHAPE_RHOMBUS>
+ * in <mxCellRenderer>.
+ * 
+ * Constructor: mxRhombus
+ *
+ * Constructs a new rhombus shape.
+ * 
+ * Parameters:
+ * 
+ * bounds - <mxRectangle> that defines the bounds. This is stored in
+ * <mxShape.bounds>.
+ * fill - String that defines the fill color. This is stored in <fill>.
+ * stroke - String that defines the stroke color. This is stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ */
+function mxRhombus(bounds, fill, stroke, strokewidth)
+{
+	mxShape.call(this);
+	this.bounds = bounds;
+	this.fill = fill;
+	this.stroke = stroke;
+	this.strokewidth = (strokewidth != null) ? strokewidth : 1;
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxRhombus, mxShape);
+
+/**
+ * Function: paintVertexShape
+ * 
+ * Generic painting implementation.
+ */
+mxRhombus.prototype.paintVertexShape = function(c, x, y, w, h)
+{
+	var hw = w / 2;
+	var hh = h / 2;
+	
+	var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2;
+	c.begin();
+	this.addPoints(c, [new mxPoint(x + hw, y), new mxPoint(x + w, y + hh), new mxPoint(x + hw, y + h),
+	     new mxPoint(x, y + hh)], this.isRounded, arcSize, true);
+	c.fillAndStroke();
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxPolyline
+ *
+ * Extends <mxShape> to implement a polyline (a line with multiple points).
+ * This shape is registered under <mxConstants.SHAPE_POLYLINE> in
+ * <mxCellRenderer>.
+ * 
+ * Constructor: mxPolyline
+ *
+ * Constructs a new polyline shape.
+ * 
+ * Parameters:
+ * 
+ * points - Array of <mxPoints> that define the points. This is stored in
+ * <mxShape.points>.
+ * stroke - String that defines the stroke color. Default is 'black'. This is
+ * stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ */
+function mxPolyline(points, stroke, strokewidth)
+{
+	mxShape.call(this);
+	this.points = points;
+	this.stroke = stroke;
+	this.strokewidth = (strokewidth != null) ? strokewidth : 1;
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxPolyline, mxShape);
+
+/**
+ * Function: getRotation
+ * 
+ * Returns 0.
+ */
+mxPolyline.prototype.getRotation = function()
+{
+	return 0;
+};
+
+/**
+ * Function: getShapeRotation
+ * 
+ * Returns 0.
+ */
+mxPolyline.prototype.getShapeRotation = function()
+{
+	return 0;
+};
+
+/**
+ * Function: isPaintBoundsInverted
+ * 
+ * Returns false.
+ */
+mxPolyline.prototype.isPaintBoundsInverted = function()
+{
+	return false;
+};
+
+/**
+ * Function: paintEdgeShape
+ * 
+ * Paints the line shape.
+ */
+mxPolyline.prototype.paintEdgeShape = function(c, pts)
+{
+	if (this.style == null || this.style[mxConstants.STYLE_CURVED] != 1)
+	{
+		this.paintLine(c, pts, this.isRounded);
+	}
+	else
+	{
+		this.paintCurvedLine(c, pts);
+	}
+};
+
+/**
+ * Function: paintLine
+ * 
+ * Paints the line shape.
+ */
+mxPolyline.prototype.paintLine = function(c, pts, rounded)
+{
+	var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2;
+	c.begin();
+	this.addPoints(c, pts, rounded, arcSize, false);
+	c.stroke();
+};
+
+/**
+ * Function: paintLine
+ * 
+ * Paints the line shape.
+ */
+mxPolyline.prototype.paintCurvedLine = function(c, pts)
+{
+	c.begin();
+	
+	var pt = pts[0];
+	var n = pts.length;
+	
+	c.moveTo(pt.x, pt.y);
+	
+	for (var i = 1; i < n - 2; i++)
+	{
+		var p0 = pts[i];
+		var p1 = pts[i + 1];
+		var ix = (p0.x + p1.x) / 2;
+		var iy = (p0.y + p1.y) / 2;
+		
+		c.quadTo(p0.x, p0.y, ix, iy);
+	}
+	
+	var p0 = pts[n - 2];
+	var p1 = pts[n - 1];
+	
+	c.quadTo(p0.x, p0.y, p1.x, p1.y);
+	c.stroke();
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxArrow
+ *
+ * Extends <mxShape> to implement an arrow shape. (The shape
+ * is used to represent edges, not vertices.)
+ * This shape is registered under <mxConstants.SHAPE_ARROW>
+ * in <mxCellRenderer>.
+ * 
+ * Constructor: mxArrow
+ *
+ * Constructs a new arrow shape.
+ * 
+ * Parameters:
+ * 
+ * points - Array of <mxPoints> that define the points. This is stored in
+ * <mxShape.points>.
+ * fill - String that defines the fill color. This is stored in <fill>.
+ * stroke - String that defines the stroke color. This is stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ * arrowWidth - Optional integer that defines the arrow width. Default is
+ * <mxConstants.ARROW_WIDTH>. This is stored in <arrowWidth>.
+ * spacing - Optional integer that defines the spacing between the arrow shape
+ * and its endpoints. Default is <mxConstants.ARROW_SPACING>. This is stored in
+ * <spacing>.
+ * endSize - Optional integer that defines the size of the arrowhead. Default
+ * is <mxConstants.ARROW_SIZE>. This is stored in <endSize>.
+ */
+function mxArrow(points, fill, stroke, strokewidth, arrowWidth, spacing, endSize)
+{
+	mxShape.call(this);
+	this.points = points;
+	this.fill = fill;
+	this.stroke = stroke;
+	this.strokewidth = (strokewidth != null) ? strokewidth : 1;
+	this.arrowWidth = (arrowWidth != null) ? arrowWidth : mxConstants.ARROW_WIDTH;
+	this.spacing = (spacing != null) ? spacing : mxConstants.ARROW_SPACING;
+	this.endSize = (endSize != null) ? endSize : mxConstants.ARROW_SIZE;
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxArrow, mxShape);
+
+/**
+ * Function: augmentBoundingBox
+ *
+ * Augments the bounding box with the edge width and markers.
+ */
+mxArrow.prototype.augmentBoundingBox = function(bbox)
+{
+	mxShape.prototype.augmentBoundingBox.apply(this, arguments);
+	
+	var w = Math.max(this.arrowWidth, this.endSize);
+	bbox.grow((w / 2 + this.strokewidth) * this.scale);
+};
+
+/**
+ * Function: paintEdgeShape
+ * 
+ * Paints the line shape.
+ */
+mxArrow.prototype.paintEdgeShape = function(c, pts)
+{
+	// Geometry of arrow
+	var spacing =  mxConstants.ARROW_SPACING;
+	var width = mxConstants.ARROW_WIDTH;
+	var arrow = mxConstants.ARROW_SIZE;
+
+	// Base vector (between end points)
+	var p0 = pts[0];
+	var pe = pts[pts.length - 1];
+	var dx = pe.x - p0.x;
+	var dy = pe.y - p0.y;
+	var dist = Math.sqrt(dx * dx + dy * dy);
+	var length = dist - 2 * spacing - arrow;
+	
+	// Computes the norm and the inverse norm
+	var nx = dx / dist;
+	var ny = dy / dist;
+	var basex = length * nx;
+	var basey = length * ny;
+	var floorx = width * ny/3;
+	var floory = -width * nx/3;
+	
+	// Computes points
+	var p0x = p0.x - floorx / 2 + spacing * nx;
+	var p0y = p0.y - floory / 2 + spacing * ny;
+	var p1x = p0x + floorx;
+	var p1y = p0y + floory;
+	var p2x = p1x + basex;
+	var p2y = p1y + basey;
+	var p3x = p2x + floorx;
+	var p3y = p2y + floory;
+	// p4 not necessary
+	var p5x = p3x - 3 * floorx;
+	var p5y = p3y - 3 * floory;
+	
+	c.begin();
+	c.moveTo(p0x, p0y);
+	c.lineTo(p1x, p1y);
+	c.lineTo(p2x, p2y);
+	c.lineTo(p3x, p3y);
+	c.lineTo(pe.x - spacing * nx, pe.y - spacing * ny);
+	c.lineTo(p5x, p5y);
+	c.lineTo(p5x + floorx, p5y + floory);
+	c.close();
+
+	c.fillAndStroke();
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxArrowConnector
+ *
+ * Extends <mxShape> to implement an new rounded arrow shape with support for
+ * waypoints and double arrows. (The shape is used to represent edges, not
+ * vertices.) This shape is registered under <mxConstants.SHAPE_ARROW_CONNECTOR>
+ * in <mxCellRenderer>.
+ * 
+ * Constructor: mxArrowConnector
+ *
+ * Constructs a new arrow shape.
+ * 
+ * Parameters:
+ * 
+ * points - Array of <mxPoints> that define the points. This is stored in
+ * <mxShape.points>.
+ * fill - String that defines the fill color. This is stored in <fill>.
+ * stroke - String that defines the stroke color. This is stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ * arrowWidth - Optional integer that defines the arrow width. Default is
+ * <mxConstants.ARROW_WIDTH>. This is stored in <arrowWidth>.
+ * spacing - Optional integer that defines the spacing between the arrow shape
+ * and its endpoints. Default is <mxConstants.ARROW_SPACING>. This is stored in
+ * <spacing>.
+ * endSize - Optional integer that defines the size of the arrowhead. Default
+ * is <mxConstants.ARROW_SIZE>. This is stored in <endSize>.
+ */
+function mxArrowConnector(points, fill, stroke, strokewidth, arrowWidth, spacing, endSize)
+{
+	mxShape.call(this);
+	this.points = points;
+	this.fill = fill;
+	this.stroke = stroke;
+	this.strokewidth = (strokewidth != null) ? strokewidth : 1;
+	this.arrowWidth = (arrowWidth != null) ? arrowWidth : mxConstants.ARROW_WIDTH;
+	this.arrowSpacing = (spacing != null) ? spacing : mxConstants.ARROW_SPACING;
+	this.startSize = mxConstants.ARROW_SIZE / 5;
+	this.endSize = mxConstants.ARROW_SIZE / 5;
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxArrowConnector, mxShape);
+
+/**
+ * Variable: useSvgBoundingBox
+ * 
+ * Allows to use the SVG bounding box in SVG. Default is false for performance
+ * reasons.
+ */
+mxArrowConnector.prototype.useSvgBoundingBox = true;
+
+/**
+ * Variable: resetStyles
+ * 
+ * Overrides mxShape to reset spacing.
+ */
+mxArrowConnector.prototype.resetStyles = function()
+{
+	mxShape.prototype.resetStyles.apply(this, arguments);
+	
+	this.arrowSpacing = mxConstants.ARROW_SPACING;
+};
+
+/**
+ * Overrides apply to get smooth transition from default start- and endsize.
+ */
+mxArrowConnector.prototype.apply = function(state)
+{
+	mxShape.prototype.apply.apply(this, arguments);
+
+	if (this.style != null)
+	{
+		this.startSize = mxUtils.getNumber(this.style, mxConstants.STYLE_STARTSIZE, mxConstants.ARROW_SIZE / 5) * 3;
+		this.endSize = mxUtils.getNumber(this.style, mxConstants.STYLE_ENDSIZE, mxConstants.ARROW_SIZE / 5) * 3;
+	}
+};
+
+/**
+ * Function: augmentBoundingBox
+ *
+ * Augments the bounding box with the edge width and markers.
+ */
+mxArrowConnector.prototype.augmentBoundingBox = function(bbox)
+{
+	mxShape.prototype.augmentBoundingBox.apply(this, arguments);
+	
+	var w = this.getEdgeWidth();
+	
+	if (this.isMarkerStart())
+	{
+		w = Math.max(w, this.getStartArrowWidth());
+	}
+	
+	if (this.isMarkerEnd())
+	{
+		w = Math.max(w, this.getEndArrowWidth());
+	}
+	
+	bbox.grow((w / 2 + this.strokewidth) * this.scale);
+};
+
+/**
+ * Function: paintEdgeShape
+ * 
+ * Paints the line shape.
+ */
+mxArrowConnector.prototype.paintEdgeShape = function(c, pts)
+{
+	// Geometry of arrow
+	var strokeWidth = this.strokewidth;
+	
+	if (this.outline)
+	{
+		strokeWidth = Math.max(1, mxUtils.getNumber(this.style, mxConstants.STYLE_STROKEWIDTH, this.strokewidth));
+	}
+	
+	var startWidth = this.getStartArrowWidth() + strokeWidth;
+	var endWidth = this.getEndArrowWidth() + strokeWidth;
+	var edgeWidth = this.outline ? this.getEdgeWidth() + strokeWidth : this.getEdgeWidth();
+	var openEnded = this.isOpenEnded();
+	var markerStart = this.isMarkerStart();
+	var markerEnd = this.isMarkerEnd();
+	var spacing = (openEnded) ? 0 : this.arrowSpacing + strokeWidth / 2;
+	var startSize = this.startSize + strokeWidth;
+	var endSize = this.endSize + strokeWidth;
+	var isRounded = this.isArrowRounded();
+	
+	// Base vector (between first points)
+	var pe = pts[pts.length - 1];
+
+	// Finds first non-overlapping point
+	var i0 = 1;
+	
+	while (i0 < pts.length - 1 && pts[i0].x == pts[0].x && pts[i0].y == pts[0].y)
+	{
+		i0++;
+	}
+	
+	var dx = pts[i0].x - pts[0].x;
+	var dy = pts[i0].y - pts[0].y;
+	var dist = Math.sqrt(dx * dx + dy * dy);
+	
+	if (dist == 0)
+	{
+		return;
+	}
+	
+	// Computes the norm and the inverse norm
+	var nx = dx / dist;
+	var nx2, nx1 = nx;
+	var ny = dy / dist;
+	var ny2, ny1 = ny;
+	var orthx = edgeWidth * ny;
+	var orthy = -edgeWidth * nx;
+	
+	// Stores the inbound function calls in reverse order in fns
+	var fns = [];
+	
+	if (isRounded)
+	{
+		c.setLineJoin('round');
+	}
+	else if (pts.length > 2)
+	{
+		// Only mitre if there are waypoints
+		c.setMiterLimit(1.42);
+	}
+
+	c.begin();
+
+	var startNx = nx;
+	var startNy = ny;
+
+	if (markerStart && !openEnded)
+	{
+		this.paintMarker(c, pts[0].x, pts[0].y, nx, ny, startSize, startWidth, edgeWidth, spacing, true);
+	}
+	else
+	{
+		var outStartX = pts[0].x + orthx / 2 + spacing * nx;
+		var outStartY = pts[0].y + orthy / 2 + spacing * ny;
+		var inEndX = pts[0].x - orthx / 2 + spacing * nx;
+		var inEndY = pts[0].y - orthy / 2 + spacing * ny;
+		
+		if (openEnded)
+		{
+			c.moveTo(outStartX, outStartY);
+			
+			fns.push(function()
+			{
+				c.lineTo(inEndX, inEndY);
+			});
+		}
+		else
+		{
+			c.moveTo(inEndX, inEndY);
+			c.lineTo(outStartX, outStartY);
+		}
+	}
+	
+	var dx1 = 0;
+	var dy1 = 0;
+	var dist1 = 0;
+
+	for (var i = 0; i < pts.length - 2; i++)
+	{
+		// Work out in which direction the line is bending
+		var pos = mxUtils.relativeCcw(pts[i].x, pts[i].y, pts[i+1].x, pts[i+1].y, pts[i+2].x, pts[i+2].y);
+
+		dx1 = pts[i+2].x - pts[i+1].x;
+		dy1 = pts[i+2].y - pts[i+1].y;
+
+		dist1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
+		
+		if (dist1 != 0)
+		{
+			nx1 = dx1 / dist1;
+			ny1 = dy1 / dist1;
+			
+			var tmp1 = nx * nx1 + ny * ny1;
+			tmp = Math.max(Math.sqrt((tmp1 + 1) / 2), 0.04);
+			
+			// Work out the normal orthogonal to the line through the control point and the edge sides intersection
+			nx2 = (nx + nx1);
+			ny2 = (ny + ny1);
+	
+			var dist2 = Math.sqrt(nx2 * nx2 + ny2 * ny2);
+			
+			if (dist2 != 0)
+			{
+				nx2 = nx2 / dist2;
+				ny2 = ny2 / dist2;
+				
+				// Higher strokewidths require a larger minimum bend, 0.35 covers all but the most extreme cases
+				var strokeWidthFactor = Math.max(tmp, Math.min(this.strokewidth / 200 + 0.04, 0.35));
+				var angleFactor = (pos != 0 && isRounded) ? Math.max(0.1, strokeWidthFactor) : Math.max(tmp, 0.06);
+
+				var outX = pts[i+1].x + ny2 * edgeWidth / 2 / angleFactor;
+				var outY = pts[i+1].y - nx2 * edgeWidth / 2 / angleFactor;
+				var inX = pts[i+1].x - ny2 * edgeWidth / 2 / angleFactor;
+				var inY = pts[i+1].y + nx2 * edgeWidth / 2 / angleFactor;
+				
+				if (pos == 0 || !isRounded)
+				{
+					// If the two segments are aligned, or if we're not drawing curved sections between segments
+					// just draw straight to the intersection point
+					c.lineTo(outX, outY);
+					
+					(function(x, y)
+					{
+						fns.push(function()
+						{
+							c.lineTo(x, y);
+						});
+					})(inX, inY);
+				}
+				else if (pos == -1)
+				{
+					var c1x = inX + ny * edgeWidth;
+					var c1y = inY - nx * edgeWidth;
+					var c2x = inX + ny1 * edgeWidth;
+					var c2y = inY - nx1 * edgeWidth;
+					c.lineTo(c1x, c1y);
+					c.quadTo(outX, outY, c2x, c2y);
+					
+					(function(x, y)
+					{
+						fns.push(function()
+						{
+							c.lineTo(x, y);
+						});
+					})(inX, inY);
+				}
+				else
+				{
+					c.lineTo(outX, outY);
+					
+					(function(x, y)
+					{
+						var c1x = outX - ny * edgeWidth;
+						var c1y = outY + nx * edgeWidth;
+						var c2x = outX - ny1 * edgeWidth;
+						var c2y = outY + nx1 * edgeWidth;
+						
+						fns.push(function()
+						{
+							c.quadTo(x, y, c1x, c1y);
+						});
+						fns.push(function()
+						{
+							c.lineTo(c2x, c2y);
+						});
+					})(inX, inY);
+				}
+				
+				nx = nx1;
+				ny = ny1;
+			}
+		}
+	}
+	
+	orthx = edgeWidth * ny1;
+	orthy = - edgeWidth * nx1;
+
+	if (markerEnd && !openEnded)
+	{
+		this.paintMarker(c, pe.x, pe.y, -nx, -ny, endSize, endWidth, edgeWidth, spacing, false);
+	}
+	else
+	{
+		c.lineTo(pe.x - spacing * nx1 + orthx / 2, pe.y - spacing * ny1 + orthy / 2);
+		
+		var inStartX = pe.x - spacing * nx1 - orthx / 2;
+		var inStartY = pe.y - spacing * ny1 - orthy / 2;
+
+		if (!openEnded)
+		{
+			c.lineTo(inStartX, inStartY);
+		}
+		else
+		{
+			c.moveTo(inStartX, inStartY);
+			
+			fns.splice(0, 0, function()
+			{
+				c.moveTo(inStartX, inStartY);
+			});
+		}
+	}
+	
+	for (var i = fns.length - 1; i >= 0; i--)
+	{
+		fns[i]();
+	}
+
+	if (openEnded)
+	{
+		c.end();
+		c.stroke();
+	}
+	else
+	{
+		c.close();
+		c.fillAndStroke();
+	}
+	
+	// Workaround for shadow on top of base arrow
+	c.setShadow(false);
+	
+	// Need to redraw the markers without the low miter limit
+	c.setMiterLimit(4);
+	
+	if (isRounded)
+	{
+		c.setLineJoin('flat');
+	}
+
+	if (pts.length > 2)
+	{
+		// Only to repaint markers if no waypoints
+		// Need to redraw the markers without the low miter limit
+		c.setMiterLimit(4);
+		if (markerStart && !openEnded)
+		{
+			c.begin();
+			this.paintMarker(c, pts[0].x, pts[0].y, startNx, startNy, startSize, startWidth, edgeWidth, spacing, true);
+			c.stroke();
+			c.end();
+		}
+		
+		if (markerEnd && !openEnded)
+		{
+			c.begin();
+			this.paintMarker(c, pe.x, pe.y, -nx, -ny, endSize, endWidth, edgeWidth, spacing, true);
+			c.stroke();
+			c.end();
+		}
+	}
+};
+
+/**
+ * Function: paintEdgeShape
+ * 
+ * Paints the line shape.
+ */
+mxArrowConnector.prototype.paintMarker = function(c, ptX, ptY, nx, ny, size, arrowWidth, edgeWidth, spacing, initialMove)
+{
+	var widthArrowRatio = edgeWidth / arrowWidth;
+	var orthx = edgeWidth * ny / 2;
+	var orthy = -edgeWidth * nx / 2;
+
+	var spaceX = (spacing + size) * nx;
+	var spaceY = (spacing + size) * ny;
+
+	if (initialMove)
+	{
+		c.moveTo(ptX - orthx + spaceX, ptY - orthy + spaceY);
+	}
+	else
+	{
+		c.lineTo(ptX - orthx + spaceX, ptY - orthy + spaceY);
+	}
+
+	c.lineTo(ptX - orthx / widthArrowRatio + spaceX, ptY - orthy / widthArrowRatio + spaceY);
+	c.lineTo(ptX + spacing * nx, ptY + spacing * ny);
+	c.lineTo(ptX + orthx / widthArrowRatio + spaceX, ptY + orthy / widthArrowRatio + spaceY);
+	c.lineTo(ptX + orthx + spaceX, ptY + orthy + spaceY);
+}
+
+/**
+ * Function: isArrowRounded
+ * 
+ * Returns wether the arrow is rounded
+ */
+mxArrowConnector.prototype.isArrowRounded = function()
+{
+	return this.isRounded;
+};
+
+/**
+ * Function: getStartArrowWidth
+ * 
+ * Returns the width of the start arrow
+ */
+mxArrowConnector.prototype.getStartArrowWidth = function()
+{
+	return mxConstants.ARROW_WIDTH;
+};
+
+/**
+ * Function: getEndArrowWidth
+ * 
+ * Returns the width of the end arrow
+ */
+mxArrowConnector.prototype.getEndArrowWidth = function()
+{
+	return mxConstants.ARROW_WIDTH;
+};
+
+/**
+ * Function: getEdgeWidth
+ * 
+ * Returns the width of the body of the edge
+ */
+mxArrowConnector.prototype.getEdgeWidth = function()
+{
+	return mxConstants.ARROW_WIDTH / 3;
+};
+
+/**
+ * Function: isOpenEnded
+ * 
+ * Returns whether the ends of the shape are drawn
+ */
+mxArrowConnector.prototype.isOpenEnded = function()
+{
+	return false;
+};
+
+/**
+ * Function: isMarkerStart
+ * 
+ * Returns whether the start marker is drawn
+ */
+mxArrowConnector.prototype.isMarkerStart = function()
+{
+	return (mxUtils.getValue(this.style, mxConstants.STYLE_STARTARROW, mxConstants.NONE) != mxConstants.NONE);
+};
+
+/**
+ * Function: isMarkerEnd
+ * 
+ * Returns whether the end marker is drawn
+ */
+mxArrowConnector.prototype.isMarkerEnd = function()
+{
+	return (mxUtils.getValue(this.style, mxConstants.STYLE_ENDARROW, mxConstants.NONE) != mxConstants.NONE);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxText
+ *
+ * Extends <mxShape> to implement a text shape. To change vertical text from
+ * bottom to top to top to bottom, the following code can be used:
+ * 
+ * (code)
+ * mxText.prototype.verticalTextRotation = 90;
+ * (end)
+ * 
+ * Constructor: mxText
+ *
+ * Constructs a new text shape.
+ * 
+ * Parameters:
+ * 
+ * value - String that represents the text to be displayed. This is stored in
+ * <value>.
+ * bounds - <mxRectangle> that defines the bounds. This is stored in
+ * <mxShape.bounds>.
+ * align - Specifies the horizontal alignment. Default is ''. This is stored in
+ * <align>.
+ * valign - Specifies the vertical alignment. Default is ''. This is stored in
+ * <valign>.
+ * color - String that specifies the text color. Default is 'black'. This is
+ * stored in <color>.
+ * family - String that specifies the font family. Default is
+ * <mxConstants.DEFAULT_FONTFAMILY>. This is stored in <family>.
+ * size - Integer that specifies the font size. Default is
+ * <mxConstants.DEFAULT_FONTSIZE>. This is stored in <size>.
+ * fontStyle - Specifies the font style. Default is 0. This is stored in
+ * <fontStyle>.
+ * spacing - Integer that specifies the global spacing. Default is 2. This is
+ * stored in <spacing>.
+ * spacingTop - Integer that specifies the top spacing. Default is 0. The
+ * sum of the spacing and this is stored in <spacingTop>.
+ * spacingRight - Integer that specifies the right spacing. Default is 0. The
+ * sum of the spacing and this is stored in <spacingRight>.
+ * spacingBottom - Integer that specifies the bottom spacing. Default is 0.The
+ * sum of the spacing and this is stored in <spacingBottom>.
+ * spacingLeft - Integer that specifies the left spacing. Default is 0. The
+ * sum of the spacing and this is stored in <spacingLeft>.
+ * horizontal - Boolean that specifies if the label is horizontal. Default is
+ * true. This is stored in <horizontal>.
+ * background - String that specifies the background color. Default is null.
+ * This is stored in <background>.
+ * border - String that specifies the label border color. Default is null.
+ * This is stored in <border>.
+ * wrap - Specifies if word-wrapping should be enabled. Default is false.
+ * This is stored in <wrap>.
+ * clipped - Specifies if the label should be clipped. Default is false.
+ * This is stored in <clipped>.
+ * overflow - Value of the overflow style. Default is 'visible'.
+ */
+function mxText(value, bounds, align, valign, color,
+	family,	size, fontStyle, spacing, spacingTop, spacingRight,
+	spacingBottom, spacingLeft, horizontal, background, border,
+	wrap, clipped, overflow, labelPadding, textDirection)
+{
+	mxShape.call(this);
+	this.value = value;
+	this.bounds = bounds;
+	this.color = (color != null) ? color : 'black';
+	this.align = (align != null) ? align : mxConstants.ALIGN_CENTER;
+	this.valign = (valign != null) ? valign : mxConstants.ALIGN_MIDDLE;
+	this.family = (family != null) ? family : mxConstants.DEFAULT_FONTFAMILY;
+	this.size = (size != null) ? size : mxConstants.DEFAULT_FONTSIZE;
+	this.fontStyle = (fontStyle != null) ? fontStyle : mxConstants.DEFAULT_FONTSTYLE;
+	this.spacing = parseInt(spacing || 2);
+	this.spacingTop = this.spacing + parseInt(spacingTop || 0);
+	this.spacingRight = this.spacing + parseInt(spacingRight || 0);
+	this.spacingBottom = this.spacing + parseInt(spacingBottom || 0);
+	this.spacingLeft = this.spacing + parseInt(spacingLeft || 0);
+	this.horizontal = (horizontal != null) ? horizontal : true;
+	this.background = background;
+	this.border = border;
+	this.wrap = (wrap != null) ? wrap : false;
+	this.clipped = (clipped != null) ? clipped : false;
+	this.overflow = (overflow != null) ? overflow : 'visible';
+	this.labelPadding = (labelPadding != null) ? labelPadding : 0;
+	this.textDirection = textDirection;
+	this.rotation = 0;
+	this.updateMargin();
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxText, mxShape);
+
+/**
+ * Variable: baseSpacingTop
+ * 
+ * Specifies the spacing to be added to the top spacing. Default is 0. Use the
+ * value 5 here to get the same label positions as in mxGraph 1.x.
+ */
+mxText.prototype.baseSpacingTop = 0;
+
+/**
+ * Variable: baseSpacingBottom
+ * 
+ * Specifies the spacing to be added to the bottom spacing. Default is 0. Use the
+ * value 1 here to get the same label positions as in mxGraph 1.x.
+ */
+mxText.prototype.baseSpacingBottom = 0;
+
+/**
+ * Variable: baseSpacingLeft
+ * 
+ * Specifies the spacing to be added to the left spacing. Default is 0.
+ */
+mxText.prototype.baseSpacingLeft = 0;
+
+/**
+ * Variable: baseSpacingRight
+ * 
+ * Specifies the spacing to be added to the right spacing. Default is 0.
+ */
+mxText.prototype.baseSpacingRight = 0;
+
+/**
+ * Variable: replaceLinefeeds
+ * 
+ * Specifies if linefeeds in HTML labels should be replaced with BR tags.
+ * Default is true.
+ */
+mxText.prototype.replaceLinefeeds = true;
+
+/**
+ * Variable: verticalTextRotation
+ * 
+ * Rotation for vertical text. Default is -90 (bottom to top).
+ */
+mxText.prototype.verticalTextRotation = -90;
+
+/**
+ * Variable: ignoreClippedStringSize
+ * 
+ * Specifies if the string size should be measured in <updateBoundingBox> if
+ * the label is clipped and the label position is center and middle. If this is
+ * true, then the bounding box will be set to <bounds>. Default is true.
+ * <ignoreStringSize> has precedence over this switch.
+ */
+mxText.prototype.ignoreClippedStringSize = true;
+
+/**
+ * Variable: ignoreStringSize
+ * 
+ * Specifies if the actual string size should be measured. If disabled the
+ * boundingBox will not ignore the actual size of the string, otherwise
+ * <bounds> will be used instead. Default is false.
+ */
+mxText.prototype.ignoreStringSize = false;
+
+/**
+ * Variable: textWidthPadding
+ * 
+ * Specifies the padding to be added to the text width for the bounding box.
+ * This is needed to make sure no clipping is applied to borders. Default is 4
+ * for IE 8 standards mode and 3 for all others.
+ */
+mxText.prototype.textWidthPadding = (document.documentMode == 8 && !mxClient.IS_EM) ? 4 : 3;
+
+/**
+ * Variable: lastValue
+ * 
+ * Contains the last rendered text value. Used for caching.
+ */
+mxText.prototype.lastValue = null;
+
+/**
+ * Variable: cacheEnabled
+ * 
+ * Specifies if caching for HTML labels should be enabled. Default is true.
+ */
+mxText.prototype.cacheEnabled = true;
+
+/**
+ * Function: isParseVml
+ * 
+ * Text shapes do not contain VML markup and do not need to be parsed. This
+ * method returns false to speed up rendering in IE8.
+ */
+mxText.prototype.isParseVml = function()
+{
+	return false;
+};
+
+/**
+ * Function: isHtmlAllowed
+ * 
+ * Returns true if HTML is allowed for this shape. This implementation returns
+ * true if the browser is not in IE8 standards mode.
+ */
+mxText.prototype.isHtmlAllowed = function()
+{
+	return document.documentMode != 8 || mxClient.IS_EM;
+};
+
+/**
+ * Function: getSvgScreenOffset
+ * 
+ * Disables offset in IE9 for crisper image output.
+ */
+mxText.prototype.getSvgScreenOffset = function()
+{
+	return 0;
+};
+
+/**
+ * Function: checkBounds
+ * 
+ * Returns true if the bounds are not null and all of its variables are numeric.
+ */
+mxText.prototype.checkBounds = function()
+{
+	return (!isNaN(this.scale) && isFinite(this.scale) && this.scale > 0 &&
+			this.bounds != null && !isNaN(this.bounds.x) && !isNaN(this.bounds.y) &&
+			!isNaN(this.bounds.width) && !isNaN(this.bounds.height));
+};
+
+/**
+ * Function: paint
+ * 
+ * Generic rendering code.
+ */
+mxText.prototype.paint = function(c, update)
+{
+	// Scale is passed-through to canvas
+	var s = this.scale;
+	var x = this.bounds.x / s;
+	var y = this.bounds.y / s;
+	var w = this.bounds.width / s;
+	var h = this.bounds.height / s;
+	
+	this.updateTransform(c, x, y, w, h);
+	this.configureCanvas(c, x, y, w, h);
+
+	var unscaledWidth = (this.state != null) ? this.state.unscaledWidth : null;
+
+	if (update)
+	{
+		if (this.node.firstChild != null && (unscaledWidth == null ||
+			this.lastUnscaledWidth != unscaledWidth))
+		{
+			c.invalidateCachedOffsetSize(this.node);
+		}
+
+		c.updateText(x, y, w, h, this.align, this.valign, this.wrap, this.overflow,
+				this.clipped, this.getTextRotation(), this.node);
+	}
+	else
+	{
+		// Checks if text contains HTML markup
+		var realHtml = mxUtils.isNode(this.value) || this.dialect == mxConstants.DIALECT_STRICTHTML;
+		
+		// Always renders labels as HTML in VML
+		var fmt = (realHtml || c instanceof mxVmlCanvas2D) ? 'html' : '';
+		var val = this.value;
+		
+		if (!realHtml && fmt == 'html')
+		{
+			val =  mxUtils.htmlEntities(val, false);
+		}
+		
+		if (fmt == 'html' && !mxUtils.isNode(this.value))
+		{
+			val = mxUtils.replaceTrailingNewlines(val, '<div><br></div>');			
+		}
+		
+		// Handles trailing newlines to make sure they are visible in rendering output
+		val = (!mxUtils.isNode(this.value) && this.replaceLinefeeds && fmt == 'html') ?
+			val.replace(/\n/g, '<br/>') : val;
+			
+		var dir = this.textDirection;
+	
+		if (dir == mxConstants.TEXT_DIRECTION_AUTO && !realHtml)
+		{
+			dir = this.getAutoDirection();
+		}
+		
+		if (dir != mxConstants.TEXT_DIRECTION_LTR && dir != mxConstants.TEXT_DIRECTION_RTL)
+		{
+			dir = null;
+		}
+	
+		c.text(x, y, w, h, val, this.align, this.valign, this.wrap, fmt, this.overflow,
+			this.clipped, this.getTextRotation(), dir);
+	}
+	
+	// Needs to invalidate the cached offset widths if the geometry changes
+	this.lastUnscaledWidth = unscaledWidth;
+};
+
+/**
+ * Function: redraw
+ * 
+ * Renders the text using the given DOM nodes.
+ */
+mxText.prototype.redraw = function()
+{
+	if (this.visible && this.checkBounds() && this.cacheEnabled && this.lastValue == this.value &&
+		(mxUtils.isNode(this.value) || this.dialect == mxConstants.DIALECT_STRICTHTML))
+	{
+		if (this.node.nodeName == 'DIV' && (this.isHtmlAllowed() || !mxClient.IS_VML))
+		{
+			this.updateSize(this.node, (this.state == null || this.state.view.textDiv == null));
+
+			if (mxClient.IS_IE && (document.documentMode == null || document.documentMode <= 8))
+			{
+				this.updateHtmlFilter();
+			}
+			else
+			{
+				this.updateHtmlTransform();
+			}
+			
+			this.updateBoundingBox();
+		}
+		else
+		{
+			var canvas = this.createCanvas();
+
+			if (canvas != null && canvas.updateText != null &&
+				canvas.invalidateCachedOffsetSize != null)
+			{
+				this.paint(canvas, true);
+				this.destroyCanvas(canvas);
+				this.updateBoundingBox();
+			}
+			else
+			{
+				// Fallback if canvas does not support updateText (VML)
+				mxShape.prototype.redraw.apply(this, arguments);
+			}
+		}
+	}
+	else
+	{
+		mxShape.prototype.redraw.apply(this, arguments);
+		
+		if (mxUtils.isNode(this.value) || this.dialect == mxConstants.DIALECT_STRICTHTML)
+		{
+			this.lastValue = this.value;
+		}
+		else
+		{
+			this.lastValue = null;
+		}
+	}
+};
+
+/**
+ * Function: resetStyles
+ * 
+ * Resets all styles.
+ */
+mxText.prototype.resetStyles = function()
+{
+	mxShape.prototype.resetStyles.apply(this, arguments);
+	
+	this.color = 'black';
+	this.align = mxConstants.ALIGN_CENTER;
+	this.valign = mxConstants.ALIGN_MIDDLE;
+	this.family = mxConstants.DEFAULT_FONTFAMILY;
+	this.size = mxConstants.DEFAULT_FONTSIZE;
+	this.fontStyle = mxConstants.DEFAULT_FONTSTYLE;
+	this.spacing = 2;
+	this.spacingTop = 2;
+	this.spacingRight = 2;
+	this.spacingBottom = 2;
+	this.spacingLeft = 2;
+	this.horizontal = true;
+	delete this.background;
+	delete this.border;
+	this.textDirection = mxConstants.DEFAULT_TEXT_DIRECTION;
+	delete this.margin;
+};
+
+/**
+ * Function: apply
+ * 
+ * Extends mxShape to update the text styles.
+ *
+ * Parameters:
+ *
+ * state - <mxCellState> of the corresponding cell.
+ */
+mxText.prototype.apply = function(state)
+{
+	var old = this.spacing;
+	mxShape.prototype.apply.apply(this, arguments);
+	
+	if (this.style != null)
+	{
+		this.fontStyle = mxUtils.getValue(this.style, mxConstants.STYLE_FONTSTYLE, this.fontStyle);
+		this.family = mxUtils.getValue(this.style, mxConstants.STYLE_FONTFAMILY, this.family);
+		this.size = mxUtils.getValue(this.style, mxConstants.STYLE_FONTSIZE, this.size);
+		this.color = mxUtils.getValue(this.style, mxConstants.STYLE_FONTCOLOR, this.color);
+		this.align = mxUtils.getValue(this.style, mxConstants.STYLE_ALIGN, this.align);
+		this.valign = mxUtils.getValue(this.style, mxConstants.STYLE_VERTICAL_ALIGN, this.valign);
+		this.spacing = parseInt(mxUtils.getValue(this.style, mxConstants.STYLE_SPACING, this.spacing));
+		this.spacingTop = parseInt(mxUtils.getValue(this.style, mxConstants.STYLE_SPACING_TOP, this.spacingTop - old)) + this.spacing;
+		this.spacingRight = parseInt(mxUtils.getValue(this.style, mxConstants.STYLE_SPACING_RIGHT, this.spacingRight - old)) + this.spacing;
+		this.spacingBottom = parseInt(mxUtils.getValue(this.style, mxConstants.STYLE_SPACING_BOTTOM, this.spacingBottom - old)) + this.spacing;
+		this.spacingLeft = parseInt(mxUtils.getValue(this.style, mxConstants.STYLE_SPACING_LEFT, this.spacingLeft - old)) + this.spacing;
+		this.horizontal = mxUtils.getValue(this.style, mxConstants.STYLE_HORIZONTAL, this.horizontal);
+		this.background = mxUtils.getValue(this.style, mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, this.background);
+		this.border = mxUtils.getValue(this.style, mxConstants.STYLE_LABEL_BORDERCOLOR, this.border);
+		this.textDirection = mxUtils.getValue(this.style, mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION);
+		this.opacity = mxUtils.getValue(this.style, mxConstants.STYLE_TEXT_OPACITY, 100);
+		this.updateMargin();
+	}
+	
+	this.flipV = null;
+	this.flipH = null;
+};
+
+/**
+ * Function: getAutoDirection
+ * 
+ * Used to determine the automatic text direction. Returns
+ * <mxConstants.TEXT_DIRECTION_LTR> or <mxConstants.TEXT_DIRECTION_RTL>
+ * depending on the contents of <value>. This is not invoked for HTML, wrapped
+ * content or if <value> is a DOM node.
+ */
+mxText.prototype.getAutoDirection = function()
+{
+	// Looks for strong (directional) characters
+	var tmp = /[A-Za-z\u05d0-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec(this.value);
+	
+	// Returns the direction defined by the character
+	return (tmp != null && tmp.length > 0 && tmp[0] > 'z') ?
+		mxConstants.TEXT_DIRECTION_RTL : mxConstants.TEXT_DIRECTION_LTR;
+};
+
+/**
+ * Function: updateBoundingBox
+ *
+ * Updates the <boundingBox> for this shape using the given node and position.
+ */
+mxText.prototype.updateBoundingBox = function()
+{
+	var node = this.node;
+	this.boundingBox = this.bounds.clone();
+	var rot = this.getTextRotation();
+	
+	var h = (this.style != null) ? mxUtils.getValue(this.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER) : null;
+	var v = (this.style != null) ? mxUtils.getValue(this.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE) : null;
+
+	if (!this.ignoreStringSize && node != null && this.overflow != 'fill' && (!this.clipped ||
+		!this.ignoreClippedStringSize || h != mxConstants.ALIGN_CENTER || v != mxConstants.ALIGN_MIDDLE))
+	{
+		var ow = null;
+		var oh = null;
+		
+		if (node.ownerSVGElement != null)
+		{
+			if (node.firstChild != null && node.firstChild.firstChild != null &&
+				node.firstChild.firstChild.nodeName == 'foreignObject')
+			{
+				node = node.firstChild.firstChild;
+				ow = parseInt(node.getAttribute('width')) * this.scale;
+				oh = parseInt(node.getAttribute('height')) * this.scale;
+			}
+			else
+			{
+				try
+				{
+					var b = node.getBBox();
+					
+					// Workaround for bounding box of empty string
+					if (typeof(this.value) == 'string' && mxUtils.trim(this.value) == 0)
+					{
+						this.boundingBox = null;
+					}
+					else if (b.width == 0 && b.height == 0)
+					{
+						this.boundingBox = null;
+					}
+					else
+					{
+						this.boundingBox = new mxRectangle(b.x, b.y, b.width, b.height);
+					}
+					
+					return;
+				}
+				catch (e)
+				{
+					// Ignores NS_ERROR_FAILURE in FF if container display is none.
+				}
+			}
+		}
+		else
+		{
+			var td = (this.state != null) ? this.state.view.textDiv : null;
+
+			// Use cached offset size
+			if (this.offsetWidth != null && this.offsetHeight != null)
+			{
+				ow = this.offsetWidth * this.scale;
+				oh = this.offsetHeight * this.scale;
+			}
+			else
+			{
+				// Cannot get node size while container hidden so a
+				// shared temporary DIV is used for text measuring
+				if (td != null)
+				{
+					this.updateFont(td);
+					this.updateSize(td, false);
+					this.updateInnerHtml(td);
+
+					node = td;
+				}
+				
+				var sizeDiv = node;
+
+				if (document.documentMode == 8 && !mxClient.IS_EM)
+				{
+					var w = Math.round(this.bounds.width / this.scale);
+	
+					if (this.wrap && w > 0)
+					{
+						node.style.wordWrap = mxConstants.WORD_WRAP;
+						node.style.whiteSpace = 'normal';
+
+						if (node.style.wordWrap != 'break-word')
+						{
+							// Innermost DIV is used for measuring text
+							var divs = sizeDiv.getElementsByTagName('div');
+							
+							if (divs.length > 0)
+							{
+								sizeDiv = divs[divs.length - 1];
+							}
+							
+							ow = sizeDiv.offsetWidth + 2;
+							divs = this.node.getElementsByTagName('div');
+							
+							if (this.clipped)
+							{
+								ow = Math.min(w, ow);
+							}
+							
+							// Second last DIV width must be updated in DOM tree
+							if (divs.length > 1)
+							{
+								divs[divs.length - 2].style.width = ow + 'px';
+							}
+						}
+					}
+					else
+					{
+						node.style.whiteSpace = 'nowrap';
+					}
+				}
+				else if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV')
+				{
+					sizeDiv = sizeDiv.firstChild;
+				}
+
+				this.offsetWidth = sizeDiv.offsetWidth + this.textWidthPadding;
+				this.offsetHeight = sizeDiv.offsetHeight;
+				
+				ow = this.offsetWidth * this.scale;
+				oh = this.offsetHeight * this.scale;
+			}
+		}
+
+		if (ow != null && oh != null)
+		{	
+			this.boundingBox = new mxRectangle(this.bounds.x,
+				this.bounds.y, ow, oh);
+		}
+	}
+
+	if (this.boundingBox != null)
+	{
+		if (rot != 0)
+		{
+			// Accounts for pre-rotated x and y
+			var bbox = mxUtils.getBoundingBox(new mxRectangle(
+				this.margin.x * this.boundingBox.width,
+				this.margin.y * this.boundingBox.height,
+				this.boundingBox.width, this.boundingBox.height),
+				rot, new mxPoint(0, 0));
+			
+			this.unrotatedBoundingBox = mxRectangle.fromRectangle(this.boundingBox);
+			this.unrotatedBoundingBox.x += this.margin.x * this.unrotatedBoundingBox.width;
+			this.unrotatedBoundingBox.y += this.margin.y * this.unrotatedBoundingBox.height;
+			
+			this.boundingBox.x += bbox.x;
+			this.boundingBox.y += bbox.y;
+			this.boundingBox.width = bbox.width;
+			this.boundingBox.height = bbox.height;
+		}
+		else
+		{
+			this.boundingBox.x += this.margin.x * this.boundingBox.width;
+			this.boundingBox.y += this.margin.y * this.boundingBox.height;
+			this.unrotatedBoundingBox = null;
+		}
+	}
+};
+
+/**
+ * Function: getShapeRotation
+ * 
+ * Returns 0 to avoid using rotation in the canvas via updateTransform.
+ */
+mxText.prototype.getShapeRotation = function()
+{
+	return 0;
+};
+
+/**
+ * Function: getTextRotation
+ * 
+ * Returns the rotation for the text label of the corresponding shape.
+ */
+mxText.prototype.getTextRotation = function()
+{
+	return (this.state != null && this.state.shape != null) ? this.state.shape.getTextRotation() : 0;
+};
+
+/**
+ * Function: isPaintBoundsInverted
+ * 
+ * Inverts the bounds if <mxShape.isBoundsInverted> returns true or if the
+ * horizontal style is false.
+ */
+mxText.prototype.isPaintBoundsInverted = function()
+{
+	return !this.horizontal && this.state != null && this.state.view.graph.model.isVertex(this.state.cell);
+};
+
+/**
+ * Function: configureCanvas
+ * 
+ * Sets the state of the canvas for drawing the shape.
+ */
+mxText.prototype.configureCanvas = function(c, x, y, w, h)
+{
+	mxShape.prototype.configureCanvas.apply(this, arguments);
+	
+	c.setFontColor(this.color);
+	c.setFontBackgroundColor(this.background);
+	c.setFontBorderColor(this.border);
+	c.setFontFamily(this.family);
+	c.setFontSize(this.size);
+	c.setFontStyle(this.fontStyle);
+};
+
+/**
+ * Function: updateVmlContainer
+ * 
+ * Sets the width and height of the container to 1px.
+ */
+mxText.prototype.updateVmlContainer = function()
+{
+	this.node.style.left = Math.round(this.bounds.x) + 'px';
+	this.node.style.top = Math.round(this.bounds.y) + 'px';
+	this.node.style.width = '1px';
+	this.node.style.height = '1px';
+	this.node.style.overflow = 'visible';
+};
+
+/**
+ * Function: redrawHtmlShape
+ *
+ * Updates the HTML node(s) to reflect the latest bounds and scale.
+ */
+mxText.prototype.redrawHtmlShape = function()
+{
+	var style = this.node.style;
+
+	// Resets CSS styles
+	style.whiteSpace = 'normal';
+	style.overflow = '';
+	style.width = '';
+	style.height = '';
+	
+	this.updateValue();
+	this.updateFont(this.node);
+	this.updateSize(this.node, (this.state == null || this.state.view.textDiv == null));
+	
+	this.offsetWidth = null;
+	this.offsetHeight = null;
+
+	if (mxClient.IS_IE && (document.documentMode == null || document.documentMode <= 8))
+	{
+		this.updateHtmlFilter();
+	}
+	else
+	{
+		this.updateHtmlTransform();
+	}
+};
+
+/**
+ * Function: updateHtmlTransform
+ *
+ * Returns the spacing as an <mxPoint>.
+ */
+mxText.prototype.updateHtmlTransform = function()
+{
+	var theta = this.getTextRotation();
+	var style = this.node.style;
+	var dx = this.margin.x;
+	var dy = this.margin.y;
+	
+	if (theta != 0)
+	{
+		mxUtils.setPrefixedStyle(style, 'transformOrigin', (-dx * 100) + '%' + ' ' + (-dy * 100) + '%');
+		mxUtils.setPrefixedStyle(style, 'transform', 'translate(' + (dx * 100) + '%' + ',' + (dy * 100) + '%)' +
+			'scale(' + this.scale + ') rotate(' + theta + 'deg)');
+	}
+	else
+	{
+		mxUtils.setPrefixedStyle(style, 'transformOrigin', '0% 0%');
+		mxUtils.setPrefixedStyle(style, 'transform', 'scale(' + this.scale + ')' +
+			'translate(' + (dx * 100) + '%' + ',' + (dy * 100) + '%)');
+	}
+
+	style.left = Math.round(this.bounds.x - Math.ceil(dx * ((this.overflow != 'fill' &&
+		this.overflow != 'width') ? 3 : 1))) + 'px';
+	style.top = Math.round(this.bounds.y - dy * ((this.overflow != 'fill') ? 3 : 1)) + 'px';
+	
+	if (this.opacity < 100)
+	{
+		style.opacity = this.opacity / 100;
+	}
+	else
+	{
+		style.opacity = '';
+	}
+};
+
+/**
+ * Function: setInnerHtml
+ * 
+ * Sets the inner HTML of the given element to the <value>.
+ */
+mxText.prototype.updateInnerHtml = function(elt)
+{
+	if (mxUtils.isNode(this.value))
+	{
+		elt.innerHTML = this.value.outerHTML;
+	}
+	else
+	{
+		var val = this.value;
+		
+		if (this.dialect != mxConstants.DIALECT_STRICTHTML)
+		{
+			// LATER: Can be cached in updateValue
+			val = mxUtils.htmlEntities(val, false);
+		}
+		
+		// Handles trailing newlines to make sure they are visible in rendering output
+		val = mxUtils.replaceTrailingNewlines(val, '<div>&nbsp;</div>');
+		val = (this.replaceLinefeeds) ? val.replace(/\n/g, '<br/>') : val;
+		val = '<div style="display:inline-block;_display:inline;">' + val + '</div>';
+		
+		elt.innerHTML = val;
+	}
+};
+
+/**
+ * Function: updateHtmlFilter
+ *
+ * Rotated text rendering quality is bad for IE9 quirks/IE8 standards
+ */
+mxText.prototype.updateHtmlFilter = function()
+{
+	var style = this.node.style;
+	var dx = this.margin.x;
+	var dy = this.margin.y;
+	var s = this.scale;
+	
+	// Resets filter before getting offsetWidth
+	mxUtils.setOpacity(this.node, this.opacity);
+	
+	// Adds 1 to match table height in 1.x
+	var ow = 0;
+	var oh = 0;
+	var td = (this.state != null) ? this.state.view.textDiv : null;
+	var sizeDiv = this.node;
+	
+	// Fallback for hidden text rendering in IE quirks mode
+	if (td != null)
+	{
+		td.style.overflow = '';
+		td.style.height = '';
+		td.style.width = '';
+		
+		this.updateFont(td);
+		this.updateSize(td, false);
+		this.updateInnerHtml(td);
+		
+		var w = Math.round(this.bounds.width / this.scale);
+
+		if (this.wrap && w > 0)
+		{
+			td.style.whiteSpace = 'normal';
+			td.style.wordWrap = mxConstants.WORD_WRAP;
+			ow = w;
+			
+			if (this.clipped)
+			{
+				ow = Math.min(ow, this.bounds.width);
+			}
+
+			td.style.width = ow + 'px';
+		}
+		else
+		{
+			td.style.whiteSpace = 'nowrap';
+		}
+		
+		sizeDiv = td;
+		
+		if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV')
+		{
+			sizeDiv = sizeDiv.firstChild;
+			
+			if (this.wrap && td.style.wordWrap == 'break-word')
+			{
+				sizeDiv.style.width = '100%';
+			}
+		}
+
+		// Required to update the height of the text box after wrapping width is known 
+		if (!this.clipped && this.wrap && w > 0)
+		{
+			ow = sizeDiv.offsetWidth + this.textWidthPadding;
+			td.style.width = ow + 'px';
+		}
+		
+		oh = sizeDiv.offsetHeight + 2;
+		
+		if (mxClient.IS_QUIRKS && this.border != null && this.border != mxConstants.NONE)
+		{
+			oh += 3;
+		}
+	}
+	else if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV')
+	{
+		sizeDiv = sizeDiv.firstChild;
+		oh = sizeDiv.offsetHeight;
+	}
+
+	ow = sizeDiv.offsetWidth + this.textWidthPadding;
+	
+	if (this.clipped)
+	{
+		oh = Math.min(oh, this.bounds.height);
+	}
+
+	var w = this.bounds.width / s;
+	var h = this.bounds.height / s;
+
+	// Handles special case for live preview with no wrapper DIV and no textDiv
+	if (this.overflow == 'fill')
+	{
+		oh = h;
+		ow = w;
+	}
+	else if (this.overflow == 'width')
+	{
+		oh = sizeDiv.scrollHeight;
+		ow = w;
+	}
+	
+	// Stores for later use
+	this.offsetWidth = ow;
+	this.offsetHeight = oh;
+	
+	// Simulates max-height CSS in quirks mode
+	if (mxClient.IS_QUIRKS && (this.clipped || (this.overflow == 'width' && h > 0)))
+	{
+		h = Math.min(h, oh);
+		style.height = Math.round(h) + 'px';
+	}
+	else
+	{
+		h = oh;
+	}
+
+	if (this.overflow != 'fill' && this.overflow != 'width')
+	{
+		if (this.clipped)
+		{
+			ow = Math.min(w, ow);
+		}
+		
+		w = ow;
+
+		// Simulates max-width CSS in quirks mode
+		if ((mxClient.IS_QUIRKS && this.clipped) || this.wrap)
+		{
+			style.width = Math.round(w) + 'px';
+		}
+	}
+
+	h *= s;
+	w *= s;
+	
+	// Rotation case is handled via VML canvas
+	var rad = this.getTextRotation() * (Math.PI / 180);
+	
+	// Precalculate cos and sin for the rotation
+	var real_cos = parseFloat(parseFloat(Math.cos(rad)).toFixed(8));
+	var real_sin = parseFloat(parseFloat(Math.sin(-rad)).toFixed(8));
+
+	rad %= 2 * Math.PI;
+	
+	if (rad < 0)
+	{
+		rad += 2 * Math.PI;
+	}
+	
+	rad %= Math.PI;
+	
+	if (rad > Math.PI / 2)
+	{
+		rad = Math.PI - rad;
+	}
+	
+	var cos = Math.cos(rad);
+	var sin = Math.sin(-rad);
+
+	var tx = w * -(dx + 0.5);
+	var ty = h * -(dy + 0.5);
+
+	var top_fix = (h - h * cos + w * sin) / 2 + real_sin * tx - real_cos * ty;
+	var left_fix = (w - w * cos + h * sin) / 2 - real_cos * tx - real_sin * ty;
+	
+	if (rad != 0)
+	{
+		var f = 'progid:DXImageTransform.Microsoft.Matrix(M11=' + real_cos + ', M12='+
+			real_sin + ', M21=' + (-real_sin) + ', M22=' + real_cos + ', sizingMethod=\'auto expand\')';
+		
+		if (style.filter != null && style.filter.length > 0)
+		{
+			style.filter += ' ' + f;
+		}
+		else
+		{
+			style.filter = f;
+		}
+	}
+	
+	// Workaround for rendering offsets
+	var dy = 0;
+	
+	if (this.overflow != 'fill' && mxClient.IS_QUIRKS)
+	{
+		if (this.valign == mxConstants.ALIGN_TOP)
+		{
+			dy -= 1;
+		}
+		else if (this.valign == mxConstants.ALIGN_BOTTOM)
+		{
+			dy += 2;
+		}
+		else
+		{
+			dy += 1;
+		}
+	}
+
+	style.zoom = s;
+	style.left = Math.round(this.bounds.x + left_fix - w / 2) + 'px';
+	style.top = Math.round(this.bounds.y + top_fix - h / 2 + dy) + 'px';
+};
+
+/**
+ * Function: updateValue
+ *
+ * Updates the HTML node(s) to reflect the latest bounds and scale.
+ */
+mxText.prototype.updateValue = function()
+{
+	if (mxUtils.isNode(this.value))
+	{
+		this.node.innerHTML = '';
+		this.node.appendChild(this.value);
+	}
+	else
+	{
+		var val = this.value;
+		
+		if (this.dialect != mxConstants.DIALECT_STRICTHTML)
+		{
+			val = mxUtils.htmlEntities(val, false);
+		}
+		
+		// Handles trailing newlines to make sure they are visible in rendering output
+		val = mxUtils.replaceTrailingNewlines(val, '<div><br></div>');
+		val = (this.replaceLinefeeds) ? val.replace(/\n/g, '<br/>') : val;
+		var bg = (this.background != null && this.background != mxConstants.NONE) ? this.background : null;
+		var bd = (this.border != null && this.border != mxConstants.NONE) ? this.border : null;
+
+		if (this.overflow == 'fill' || this.overflow == 'width')
+		{
+			if (bg != null)
+			{
+				this.node.style.backgroundColor = bg;
+			}
+			
+			if (bd != null)
+			{
+				this.node.style.border = '1px solid ' + bd;
+			}
+		}
+		else
+		{
+			var css = '';
+			
+			if (bg != null)
+			{
+				css += 'background-color:' + bg + ';';
+			}
+			
+			if (bd != null)
+			{
+				css += 'border:1px solid ' + bd + ';';
+			}
+			
+			// Wrapper DIV for background, zoom needed for inline in quirks
+			// and to measure wrapped font sizes in all browsers
+			// FIXME: Background size in quirks mode for wrapped text
+			var lh = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (this.size * mxConstants.LINE_HEIGHT) + 'px' :
+				mxConstants.LINE_HEIGHT;
+			val = '<div style="zoom:1;' + css + 'display:inline-block;_display:inline;text-decoration:inherit;' +
+				'padding-bottom:1px;padding-right:1px;line-height:' + lh + '">' + val + '</div>';
+		}
+
+		this.node.innerHTML = val;
+		
+		// Sets text direction
+		var divs = this.node.getElementsByTagName('div');
+		
+		if (divs.length > 0)
+		{
+			var dir = this.textDirection;
+
+			if (dir == mxConstants.TEXT_DIRECTION_AUTO && this.dialect != mxConstants.DIALECT_STRICTHTML)
+			{
+				dir = this.getAutoDirection();
+			}
+			
+			if (dir == mxConstants.TEXT_DIRECTION_LTR || dir == mxConstants.TEXT_DIRECTION_RTL)
+			{
+				divs[divs.length - 1].setAttribute('dir', dir);
+			}
+			else
+			{
+				divs[divs.length - 1].removeAttribute('dir');
+			}
+		}
+	}
+};
+
+/**
+ * Function: updateFont
+ *
+ * Updates the HTML node(s) to reflect the latest bounds and scale.
+ */
+mxText.prototype.updateFont = function(node)
+{
+	var style = node.style;
+	
+	style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (this.size * mxConstants.LINE_HEIGHT) + 'px' : mxConstants.LINE_HEIGHT;
+	style.fontSize = this.size + 'px';
+	style.fontFamily = this.family;
+	style.verticalAlign = 'top';
+	style.color = this.color;
+	
+	if ((this.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
+	{
+		style.fontWeight = 'bold';
+	}
+	else
+	{
+		style.fontWeight = '';
+	}
+
+	if ((this.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
+	{
+		style.fontStyle = 'italic';
+	}
+	else
+	{
+		style.fontStyle = '';
+	}
+	
+	if ((this.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
+	{
+		style.textDecoration = 'underline';
+	}
+	else
+	{
+		style.textDecoration = '';
+	}
+	
+	if (this.align == mxConstants.ALIGN_CENTER)
+	{
+		style.textAlign = 'center';
+	}
+	else if (this.align == mxConstants.ALIGN_RIGHT)
+	{
+		style.textAlign = 'right';
+	}
+	else
+	{
+		style.textAlign = 'left';
+	}
+};
+
+/**
+ * Function: updateSize
+ *
+ * Updates the HTML node(s) to reflect the latest bounds and scale.
+ */
+mxText.prototype.updateSize = function(node, enableWrap)
+{
+	var w = Math.max(0, Math.round(this.bounds.width / this.scale));
+	var h = Math.max(0, Math.round(this.bounds.height / this.scale));
+	var style = node.style;
+	
+	// NOTE: Do not use maxWidth here because wrapping will
+	// go wrong if the cell is outside of the viewable area
+	if (this.clipped)
+	{
+		style.overflow = 'hidden';
+		
+		if (!mxClient.IS_QUIRKS)
+		{
+			style.maxHeight = h + 'px';
+			style.maxWidth = w + 'px';
+		}
+		else
+		{
+			style.width = w + 'px';
+		}
+	}
+	else if (this.overflow == 'fill')
+	{
+		style.width = (w + 1) + 'px';
+		style.height = (h + 1) + 'px';
+		style.overflow = 'hidden';
+	}
+	else if (this.overflow == 'width')
+	{
+		style.width = (w + 1) + 'px';
+		style.maxHeight = (h + 1) + 'px';
+		style.overflow = 'hidden';
+	}
+	
+	if (this.wrap && w > 0)
+	{
+		style.wordWrap = mxConstants.WORD_WRAP;
+		style.whiteSpace = 'normal';
+		style.width = w + 'px';
+
+		if (enableWrap && this.overflow != 'fill' && this.overflow != 'width')
+		{
+			var sizeDiv = node;
+			
+			if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV')
+			{
+				sizeDiv = sizeDiv.firstChild;
+				
+				if (node.style.wordWrap == 'break-word')
+				{
+					sizeDiv.style.width = '100%';
+				}
+			}
+			
+			var tmp = sizeDiv.offsetWidth;
+			
+			// Workaround for text measuring in hidden containers
+			if (tmp == 0)
+			{
+				var prev = node.parentNode;
+				node.style.visibility = 'hidden';
+				document.body.appendChild(node);
+				tmp = sizeDiv.offsetWidth;
+				node.style.visibility = '';
+				prev.appendChild(node);
+			}
+
+			tmp += 3;
+			
+			if (this.clipped)
+			{
+				tmp = Math.min(tmp, w);
+			}
+			
+			style.width = tmp + 'px';
+		}
+	}
+	else
+	{
+		style.whiteSpace = 'nowrap';
+	}
+};
+
+/**
+ * Function: getMargin
+ *
+ * Returns the spacing as an <mxPoint>.
+ */
+mxText.prototype.updateMargin = function()
+{
+	this.margin = mxUtils.getAlignmentAsPoint(this.align, this.valign);
+};
+
+/**
+ * Function: getSpacing
+ *
+ * Returns the spacing as an <mxPoint>.
+ */
+mxText.prototype.getSpacing = function()
+{
+	var dx = 0;
+	var dy = 0;
+
+	if (this.align == mxConstants.ALIGN_CENTER)
+	{
+		dx = (this.spacingLeft - this.spacingRight) / 2;
+	}
+	else if (this.align == mxConstants.ALIGN_RIGHT)
+	{
+		dx = -this.spacingRight - this.baseSpacingRight;
+	}
+	else
+	{
+		dx = this.spacingLeft + this.baseSpacingLeft;
+	}
+
+	if (this.valign == mxConstants.ALIGN_MIDDLE)
+	{
+		dy = (this.spacingTop - this.spacingBottom) / 2;
+	}
+	else if (this.valign == mxConstants.ALIGN_BOTTOM)
+	{
+		dy = -this.spacingBottom - this.baseSpacingBottom;;
+	}
+	else
+	{
+		dy = this.spacingTop + this.baseSpacingTop;
+	}
+	
+	return new mxPoint(dx, dy);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxTriangle
+ * 
+ * Implementation of the triangle shape.
+ * 
+ * Constructor: mxTriangle
+ *
+ * Constructs a new triangle shape.
+ */
+function mxTriangle()
+{
+	mxActor.call(this);
+};
+
+/**
+ * Extends mxActor.
+ */
+mxUtils.extend(mxTriangle, mxActor);
+
+/**
+ * Function: redrawPath
+ *
+ * Draws the path for this shape.
+ */
+mxTriangle.prototype.redrawPath = function(c, x, y, w, h)
+{
+	var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2;
+	this.addPoints(c, [new mxPoint(0, 0), new mxPoint(w, 0.5 * h), new mxPoint(0, h)], this.isRounded, arcSize, true);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxHexagon
+ * 
+ * Implementation of the hexagon shape.
+ * 
+ * Constructor: mxHexagon
+ *
+ * Constructs a new hexagon shape.
+ */
+function mxHexagon()
+{
+	mxActor.call(this);
+};
+
+/**
+ * Extends mxActor.
+ */
+mxUtils.extend(mxHexagon, mxActor);
+
+/**
+ * Function: redrawPath
+ *
+ * Draws the path for this shape.
+ */
+mxHexagon.prototype.redrawPath = function(c, x, y, w, h)
+{
+	var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2;
+	this.addPoints(c, [new mxPoint(0.25 * w, 0), new mxPoint(0.75 * w, 0), new mxPoint(w, 0.5 * h), new mxPoint(0.75 * w, h),
+	                   new mxPoint(0.25 * w, h), new mxPoint(0, 0.5 * h)], this.isRounded, arcSize, true);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxLine
+ *
+ * Extends <mxShape> to implement a horizontal line shape.
+ * This shape is registered under <mxConstants.SHAPE_LINE> in
+ * <mxCellRenderer>.
+ * 
+ * Constructor: mxLine
+ *
+ * Constructs a new line shape.
+ * 
+ * Parameters:
+ * 
+ * bounds - <mxRectangle> that defines the bounds. This is stored in
+ * <mxShape.bounds>.
+ * stroke - String that defines the stroke color. Default is 'black'. This is
+ * stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ */
+function mxLine(bounds, stroke, strokewidth)
+{
+	mxShape.call(this);
+	this.bounds = bounds;
+	this.stroke = stroke;
+	this.strokewidth = (strokewidth != null) ? strokewidth : 1;
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxLine, mxShape);
+
+/**
+ * Function: paintVertexShape
+ * 
+ * Redirects to redrawPath for subclasses to work.
+ */
+mxLine.prototype.paintVertexShape = function(c, x, y, w, h)
+{
+	var mid = y + h / 2;
+
+	c.begin();
+	c.moveTo(x, mid);
+	c.lineTo(x + w, mid);
+	c.stroke();
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxImageShape
+ *
+ * Extends <mxShape> to implement an image shape. This shape is registered
+ * under <mxConstants.SHAPE_IMAGE> in <mxCellRenderer>.
+ * 
+ * Constructor: mxImageShape
+ * 
+ * Constructs a new image shape.
+ * 
+ * Parameters:
+ * 
+ * bounds - <mxRectangle> that defines the bounds. This is stored in
+ * <mxShape.bounds>.
+ * image - String that specifies the URL of the image. This is stored in
+ * <image>.
+ * fill - String that defines the fill color. This is stored in <fill>.
+ * stroke - String that defines the stroke color. This is stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 0. This is stored in <strokewidth>.
+ */
+function mxImageShape(bounds, image, fill, stroke, strokewidth)
+{
+	mxShape.call(this);
+	this.bounds = bounds;
+	this.image = image;
+	this.fill = fill;
+	this.stroke = stroke;
+	this.strokewidth = (strokewidth != null) ? strokewidth : 1;
+	this.shadow = false;
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxImageShape, mxRectangleShape);
+
+/**
+ * Variable: preserveImageAspect
+ *
+ * Switch to preserve image aspect. Default is true.
+ */
+mxImageShape.prototype.preserveImageAspect = true;
+
+/**
+ * Function: getSvgScreenOffset
+ * 
+ * Disables offset in IE9 for crisper image output.
+ */
+mxImageShape.prototype.getSvgScreenOffset = function()
+{
+	return 0;
+};
+
+/**
+ * Function: apply
+ * 
+ * Overrides <mxShape.apply> to replace the fill and stroke colors with the
+ * respective values from <mxConstants.STYLE_IMAGE_BACKGROUND> and
+ * <mxConstants.STYLE_IMAGE_BORDER>.
+ * 
+ * Applies the style of the given <mxCellState> to the shape. This
+ * implementation assigns the following styles to local fields:
+ * 
+ * - <mxConstants.STYLE_IMAGE_BACKGROUND> => fill
+ * - <mxConstants.STYLE_IMAGE_BORDER> => stroke
+ *
+ * Parameters:
+ *
+ * state - <mxCellState> of the corresponding cell.
+ */
+mxImageShape.prototype.apply = function(state)
+{
+	mxShape.prototype.apply.apply(this, arguments);
+	
+	this.fill = null;
+	this.stroke = null;
+	this.gradient = null;
+	
+	if (this.style != null)
+	{
+		this.preserveImageAspect = mxUtils.getNumber(this.style, mxConstants.STYLE_IMAGE_ASPECT, 1) == 1;
+		
+		// Legacy support for imageFlipH/V
+		this.flipH = this.flipH || mxUtils.getValue(this.style, 'imageFlipH', 0) == 1;
+		this.flipV = this.flipV || mxUtils.getValue(this.style, 'imageFlipV', 0) == 1;
+	}
+};
+
+/**
+ * Function: isHtmlAllowed
+ * 
+ * Returns true if HTML is allowed for this shape. This implementation always
+ * returns false.
+ */
+mxImageShape.prototype.isHtmlAllowed = function()
+{
+	return !this.preserveImageAspect;
+};
+
+/**
+ * Function: createHtml
+ *
+ * Creates and returns the HTML DOM node(s) to represent
+ * this shape. This implementation falls back to <createVml>
+ * so that the HTML creation is optional.
+ */
+mxImageShape.prototype.createHtml = function()
+{
+	var node = document.createElement('div');
+	node.style.position = 'absolute';
+
+	return node;
+};
+
+/**
+ * Function: paintVertexShape
+ * 
+ * Generic background painting implementation.
+ */
+mxImageShape.prototype.paintVertexShape = function(c, x, y, w, h)
+{
+	if (this.image != null)
+	{
+		var fill = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_BACKGROUND, null);
+		var stroke = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_BORDER, null);
+		
+		if (fill != null)
+		{
+			// Stroke rendering required for shadow
+			c.setFillColor(fill);
+			c.setStrokeColor(stroke);
+			c.rect(x, y, w, h);
+			c.fillAndStroke();
+		}
+
+		// FlipH/V are implicit via mxShape.updateTransform
+		c.image(x, y, w, h, this.image, this.preserveImageAspect, false, false);
+		
+		var stroke = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_BORDER, null);
+		
+		if (stroke != null)
+		{
+			c.setShadow(false);
+			c.setStrokeColor(stroke);
+			c.rect(x, y, w, h);
+			c.stroke();
+		}
+	}
+	else
+	{
+		mxRectangleShape.prototype.paintBackground.apply(this, arguments);
+	}
+};
+
+/**
+ * Function: redraw
+ * 
+ * Overrides <mxShape.redraw> to preserve the aspect ratio of images.
+ */
+mxImageShape.prototype.redrawHtmlShape = function()
+{
+	this.node.style.left = Math.round(this.bounds.x) + 'px';
+	this.node.style.top = Math.round(this.bounds.y) + 'px';
+	this.node.style.width = Math.max(0, Math.round(this.bounds.width)) + 'px';
+	this.node.style.height = Math.max(0, Math.round(this.bounds.height)) + 'px';
+	this.node.innerHTML = '';
+
+	if (this.image != null)
+	{
+		var fill = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_BACKGROUND, '');
+		var stroke = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_BORDER, '');
+		this.node.style.backgroundColor = fill;
+		this.node.style.borderColor = stroke;
+		
+		// VML image supports PNG in IE6
+		var useVml = mxClient.IS_IE6 || ((document.documentMode == null || document.documentMode <= 8) && this.rotation != 0);
+		var img = document.createElement((useVml) ? mxClient.VML_PREFIX + ':image' : 'img');
+		img.setAttribute('border', '0');
+		img.style.position = 'absolute';
+		img.src = this.image;
+
+		var filter = (this.opacity < 100) ? 'alpha(opacity=' + this.opacity + ')' : '';
+		this.node.style.filter = filter;
+		
+		if (this.flipH && this.flipV)
+		{
+			filter += 'progid:DXImageTransform.Microsoft.BasicImage(rotation=2)';
+		}
+		else if (this.flipH)
+		{
+			filter += 'progid:DXImageTransform.Microsoft.BasicImage(mirror=1)';
+		}
+		else if (this.flipV)
+		{
+			filter += 'progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)';
+		}
+
+		if (img.style.filter != filter)
+		{
+			img.style.filter = filter;
+		}
+
+		if (img.nodeName == 'image')
+		{
+			img.style.rotation = this.rotation;
+		}
+		else if (this.rotation != 0)
+		{
+			// LATER: Add flipV/H support
+			mxUtils.setPrefixedStyle(img.style, 'transform', 'rotate(' + this.rotation + 'deg)');
+		}
+		else
+		{
+			mxUtils.setPrefixedStyle(img.style, 'transform', '');
+		}
+
+		// Known problem: IE clips top line of image for certain angles
+		img.style.width = this.node.style.width;
+		img.style.height = this.node.style.height;
+		
+		this.node.style.backgroundImage = '';
+		this.node.appendChild(img);
+	}
+	else
+	{
+		this.setTransparentBackgroundImage(this.node);
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxLabel
+ *
+ * Extends <mxShape> to implement an image shape with a label.
+ * This shape is registered under <mxConstants.SHAPE_LABEL> in
+ * <mxCellRenderer>.
+ * 
+ * Constructor: mxLabel
+ *
+ * Constructs a new label shape.
+ * 
+ * Parameters:
+ * 
+ * bounds - <mxRectangle> that defines the bounds. This is stored in
+ * <mxShape.bounds>.
+ * fill - String that defines the fill color. This is stored in <fill>.
+ * stroke - String that defines the stroke color. This is stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ */
+function mxLabel(bounds, fill, stroke, strokewidth)
+{
+	mxRectangleShape.call(this, bounds, fill, stroke, strokewidth);
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxLabel, mxRectangleShape);
+
+/**
+ * Variable: imageSize
+ *
+ * Default width and height for the image. Default is
+ * <mxConstants.DEFAULT_IMAGESIZE>.
+ */
+mxLabel.prototype.imageSize = mxConstants.DEFAULT_IMAGESIZE;
+
+/**
+ * Variable: spacing
+ *
+ * Default value for image spacing. Default is 2.
+ */
+mxLabel.prototype.spacing = 2;
+
+/**
+ * Variable: indicatorSize
+ *
+ * Default width and height for the indicicator. Default is 10.
+ */
+mxLabel.prototype.indicatorSize = 10;
+
+/**
+ * Variable: indicatorSpacing
+ *
+ * Default spacing between image and indicator. Default is 2.
+ */
+mxLabel.prototype.indicatorSpacing = 2;
+
+/**
+ * Function: init
+ *
+ * Initializes the shape and the <indicator>.
+ */
+mxLabel.prototype.init = function(container)
+{
+	mxShape.prototype.init.apply(this, arguments);
+
+	if (this.indicatorShape != null)
+	{
+		this.indicator = new this.indicatorShape();
+		this.indicator.dialect = this.dialect;
+		this.indicator.init(this.node);
+	}
+};
+
+/**
+ * Function: redraw
+ *
+ * Reconfigures this shape. This will update the colors of the indicator
+ * and reconfigure it if required.
+ */
+mxLabel.prototype.redraw = function()
+{
+	if (this.indicator != null)
+	{
+		this.indicator.fill = this.indicatorColor;
+		this.indicator.stroke = this.indicatorStrokeColor;
+		this.indicator.gradient = this.indicatorGradientColor;
+		this.indicator.direction = this.indicatorDirection;
+	}
+	
+	mxShape.prototype.redraw.apply(this, arguments);
+};
+
+/**
+ * Function: isHtmlAllowed
+ *
+ * Returns true for non-rounded, non-rotated shapes with no glass gradient and
+ * no indicator shape.
+ */
+mxLabel.prototype.isHtmlAllowed = function()
+{
+	return mxRectangleShape.prototype.isHtmlAllowed.apply(this, arguments) &&
+		this.indicatorColor == null && this.indicatorShape == null;
+};
+
+/**
+ * Function: paintForeground
+ * 
+ * Generic background painting implementation.
+ */
+mxLabel.prototype.paintForeground = function(c, x, y, w, h)
+{
+	this.paintImage(c, x, y, w, h);
+	this.paintIndicator(c, x, y, w, h);
+	
+	mxRectangleShape.prototype.paintForeground.apply(this, arguments);
+};
+
+/**
+ * Function: paintImage
+ * 
+ * Generic background painting implementation.
+ */
+mxLabel.prototype.paintImage = function(c, x, y, w, h)
+{
+	if (this.image != null)
+	{
+		var bounds = this.getImageBounds(x, y, w, h);
+		c.image(bounds.x, bounds.y, bounds.width, bounds.height, this.image, false, false, false);
+	}
+};
+
+/**
+ * Function: getImageBounds
+ * 
+ * Generic background painting implementation.
+ */
+mxLabel.prototype.getImageBounds = function(x, y, w, h)
+{
+	var align = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_ALIGN, mxConstants.ALIGN_LEFT);
+	var valign = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE);
+	var width = mxUtils.getNumber(this.style, mxConstants.STYLE_IMAGE_WIDTH, mxConstants.DEFAULT_IMAGESIZE);
+	var height = mxUtils.getNumber(this.style, mxConstants.STYLE_IMAGE_HEIGHT, mxConstants.DEFAULT_IMAGESIZE);
+	var spacing = mxUtils.getNumber(this.style, mxConstants.STYLE_SPACING, this.spacing) + 5;
+
+	if (align == mxConstants.ALIGN_CENTER)
+	{
+		x += (w - width) / 2;
+	}
+	else if (align == mxConstants.ALIGN_RIGHT)
+	{
+		x += w - width - spacing;
+	}
+	else // default is left
+	{
+		x += spacing;
+	}
+
+	if (valign == mxConstants.ALIGN_TOP)
+	{
+		y += spacing;
+	}
+	else if (valign == mxConstants.ALIGN_BOTTOM)
+	{
+		y += h - height - spacing;
+	}
+	else // default is middle
+	{
+		y += (h - height) / 2;
+	}
+	
+	return new mxRectangle(x, y, width, height);
+};
+
+/**
+ * Function: paintIndicator
+ * 
+ * Generic background painting implementation.
+ */
+mxLabel.prototype.paintIndicator = function(c, x, y, w, h)
+{
+	if (this.indicator != null)
+	{
+		this.indicator.bounds = this.getIndicatorBounds(x, y, w, h);
+		this.indicator.paint(c);
+	}
+	else if (this.indicatorImage != null)
+	{
+		var bounds = this.getIndicatorBounds(x, y, w, h);
+		c.image(bounds.x, bounds.y, bounds.width, bounds.height, this.indicatorImage, false, false, false);
+	}
+};
+
+/**
+ * Function: getIndicatorBounds
+ * 
+ * Generic background painting implementation.
+ */
+mxLabel.prototype.getIndicatorBounds = function(x, y, w, h)
+{
+	var align = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_ALIGN, mxConstants.ALIGN_LEFT);
+	var valign = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE);
+	var width = mxUtils.getNumber(this.style, mxConstants.STYLE_INDICATOR_WIDTH, this.indicatorSize);
+	var height = mxUtils.getNumber(this.style, mxConstants.STYLE_INDICATOR_HEIGHT, this.indicatorSize);
+	var spacing = this.spacing + 5;		
+	
+	if (align == mxConstants.ALIGN_RIGHT)
+	{
+		x += w - width - spacing;
+	}
+	else if (align == mxConstants.ALIGN_CENTER)
+	{
+		x += (w - width) / 2;
+	}
+	else // default is left
+	{
+		x += spacing;
+	}
+	
+	if (valign == mxConstants.ALIGN_BOTTOM)
+	{
+		y += h - height - spacing;
+	}
+	else if (valign == mxConstants.ALIGN_TOP)
+	{
+		y += spacing;
+	}
+	else // default is middle
+	{
+		y += (h - height) / 2;
+	}
+	
+	return new mxRectangle(x, y, width, height);
+};
+/**
+ * Function: redrawHtmlShape
+ * 
+ * Generic background painting implementation.
+ */
+mxLabel.prototype.redrawHtmlShape = function()
+{
+	mxRectangleShape.prototype.redrawHtmlShape.apply(this, arguments);
+	
+	// Removes all children
+	while(this.node.hasChildNodes())
+	{
+		this.node.removeChild(this.node.lastChild);
+	}
+	
+	if (this.image != null)
+	{
+		var node = document.createElement('img');
+		node.style.position = 'relative';
+		node.setAttribute('border', '0');
+		
+		var bounds = this.getImageBounds(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height);
+		bounds.x -= this.bounds.x;
+		bounds.y -= this.bounds.y;
+
+		node.style.left = Math.round(bounds.x) + 'px';
+		node.style.top = Math.round(bounds.y) + 'px';
+		node.style.width = Math.round(bounds.width) + 'px';
+		node.style.height = Math.round(bounds.height) + 'px';
+		
+		node.src = this.image;
+		
+		this.node.appendChild(node);
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCylinder
+ *
+ * Extends <mxShape> to implement an cylinder shape. If a
+ * custom shape with one filled area and an overlay path is
+ * needed, then this shape's <redrawPath> should be overridden.
+ * This shape is registered under <mxConstants.SHAPE_CYLINDER>
+ * in <mxCellRenderer>.
+ * 
+ * Constructor: mxCylinder
+ *
+ * Constructs a new cylinder shape.
+ * 
+ * Parameters:
+ * 
+ * bounds - <mxRectangle> that defines the bounds. This is stored in
+ * <mxShape.bounds>.
+ * fill - String that defines the fill color. This is stored in <fill>.
+ * stroke - String that defines the stroke color. This is stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ */
+function mxCylinder(bounds, fill, stroke, strokewidth)
+{
+	mxShape.call(this);
+	this.bounds = bounds;
+	this.fill = fill;
+	this.stroke = stroke;
+	this.strokewidth = (strokewidth != null) ? strokewidth : 1;
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxCylinder, mxShape);
+
+/**
+ * Variable: maxHeight
+ *
+ * Defines the maximum height of the top and bottom part
+ * of the cylinder shape.
+ */
+mxCylinder.prototype.maxHeight = 40;
+
+/**
+ * Variable: svgStrokeTolerance
+ *
+ * Sets stroke tolerance to 0 for SVG.
+ */
+mxCylinder.prototype.svgStrokeTolerance = 0;
+
+/**
+ * Function: paintVertexShape
+ * 
+ * Redirects to redrawPath for subclasses to work.
+ */
+mxCylinder.prototype.paintVertexShape = function(c, x, y, w, h)
+{
+	c.translate(x, y);
+	c.begin();
+	this.redrawPath(c, x, y, w, h, false);
+	c.fillAndStroke();
+	
+	c.setShadow(false);
+	
+	c.begin();
+	this.redrawPath(c, x, y, w, h, true);
+	c.stroke();
+};
+
+/**
+ * Function: redrawPath
+ *
+ * Draws the path for this shape.
+ */
+mxCylinder.prototype.redrawPath = function(c, x, y, w, h, isForeground)
+{
+	var dy = Math.min(this.maxHeight, Math.round(h / 5));
+	
+	if ((isForeground && this.fill != null) || (!isForeground && this.fill == null))
+	{
+		c.moveTo(0, dy);
+		c.curveTo(0, 2 * dy, w, 2 * dy, w, dy);
+		
+		// Needs separate shapes for correct hit-detection
+		if (!isForeground)
+		{
+			c.stroke();
+			c.begin();
+		}
+	}
+	
+	if (!isForeground)
+	{
+		c.moveTo(0, dy);
+		c.curveTo(0, -dy / 3, w, -dy / 3, w, dy);
+		c.lineTo(w, h - dy);
+		c.curveTo(w, h + dy / 3, 0, h + dy / 3, 0, h - dy);
+		c.close();
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxConnector
+ * 
+ * Extends <mxShape> to implement a connector shape. The connector
+ * shape allows for arrow heads on either side.
+ * 
+ * This shape is registered under <mxConstants.SHAPE_CONNECTOR> in
+ * <mxCellRenderer>.
+ * 
+ * Constructor: mxConnector
+ * 
+ * Constructs a new connector shape.
+ * 
+ * Parameters:
+ * 
+ * points - Array of <mxPoints> that define the points. This is stored in
+ * <mxShape.points>.
+ * stroke - String that defines the stroke color. This is stored in <stroke>.
+ * Default is 'black'.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ */
+function mxConnector(points, stroke, strokewidth)
+{
+	mxPolyline.call(this, points, stroke, strokewidth);
+};
+
+/**
+ * Extends mxPolyline.
+ */
+mxUtils.extend(mxConnector, mxPolyline);
+
+/**
+ * Function: updateBoundingBox
+ *
+ * Updates the <boundingBox> for this shape using <createBoundingBox> and
+ * <augmentBoundingBox> and stores the result in <boundingBox>.
+ */
+mxConnector.prototype.updateBoundingBox = function()
+{
+	this.useSvgBoundingBox = this.style != null && this.style[mxConstants.STYLE_CURVED] == 1;
+	mxShape.prototype.updateBoundingBox.apply(this, arguments);
+};
+
+/**
+ * Function: paintEdgeShape
+ * 
+ * Paints the line shape.
+ */
+mxConnector.prototype.paintEdgeShape = function(c, pts)
+{
+	// The indirection via functions for markers is needed in
+	// order to apply the offsets before painting the line and
+	// paint the markers after painting the line.
+	var sourceMarker = this.createMarker(c, pts, true);
+	var targetMarker = this.createMarker(c, pts, false);
+
+	mxPolyline.prototype.paintEdgeShape.apply(this, arguments);
+	
+	// Disables shadows, dashed styles and fixes fill color for markers
+	c.setFillColor(this.stroke);
+	c.setShadow(false);
+	c.setDashed(false);
+	
+	if (sourceMarker != null)
+	{
+		sourceMarker();
+	}
+	
+	if (targetMarker != null)
+	{
+		targetMarker();
+	}
+};
+
+/**
+ * Function: createMarker
+ * 
+ * Prepares the marker by adding offsets in pts and returning a function to
+ * paint the marker.
+ */
+mxConnector.prototype.createMarker = function(c, pts, source)
+{
+	var result = null;
+	var n = pts.length;
+	var type = mxUtils.getValue(this.style, (source) ? mxConstants.STYLE_STARTARROW : mxConstants.STYLE_ENDARROW);
+	var p0 = (source) ? pts[1] : pts[n - 2];
+	var pe = (source) ? pts[0] : pts[n - 1];
+	
+	if (type != null && p0 != null && pe != null)
+	{
+		var count = 1;
+		
+		// Uses next non-overlapping point
+		while (count < n - 1 && Math.round(p0.x - pe.x) == 0 && Math.round(p0.y - pe.y) == 0)
+		{
+			p0 = (source) ? pts[1 + count] : pts[n - 2 - count];
+			count++;
+		}
+	
+		// Computes the norm and the inverse norm
+		var dx = pe.x - p0.x;
+		var dy = pe.y - p0.y;
+	
+		var dist = Math.max(1, Math.sqrt(dx * dx + dy * dy));
+		
+		var unitX = dx / dist;
+		var unitY = dy / dist;
+	
+		var size = mxUtils.getNumber(this.style, (source) ? mxConstants.STYLE_STARTSIZE : mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE);
+		
+		// Allow for stroke width in the end point used and the 
+		// orthogonal vectors describing the direction of the marker
+		var filled = this.style[(source) ? mxConstants.STYLE_STARTFILL : mxConstants.STYLE_ENDFILL] != 0;
+		
+		result = mxMarker.createMarker(c, this, type, pe, unitX, unitY, size, source, this.strokewidth, filled);
+	}
+	
+	return result;
+};
+
+/**
+ * Function: augmentBoundingBox
+ *
+ * Augments the bounding box with the strokewidth and shadow offsets.
+ */
+mxConnector.prototype.augmentBoundingBox = function(bbox)
+{
+	mxShape.prototype.augmentBoundingBox.apply(this, arguments);
+	
+	// Adds marker sizes
+	var size = 0;
+	
+	if (mxUtils.getValue(this.style, mxConstants.STYLE_STARTARROW, mxConstants.NONE) != mxConstants.NONE)
+	{
+		size = mxUtils.getNumber(this.style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_MARKERSIZE) + 1;
+	}
+	
+	if (mxUtils.getValue(this.style, mxConstants.STYLE_ENDARROW, mxConstants.NONE) != mxConstants.NONE)
+	{
+		size = Math.max(size, mxUtils.getNumber(this.style, mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE)) + 1;
+	}
+	
+	bbox.grow(size * this.scale);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxSwimlane
+ *
+ * Extends <mxShape> to implement a swimlane shape. This shape is registered
+ * under <mxConstants.SHAPE_SWIMLANE> in <mxCellRenderer>. Use the
+ * <mxConstants.STYLE_STYLE_STARTSIZE> to define the size of the title
+ * region, <mxConstants.STYLE_SWIMLANE_FILLCOLOR> for the content area fill,
+ * <mxConstants.STYLE_SEPARATORCOLOR> to draw an additional vertical separator
+ * and <mxConstants.STYLE_SWIMLANE_LINE> to hide the line between the title
+ * region and the content area. The <mxConstants.STYLE_HORIZONTAL> affects
+ * the orientation of this shape, not only its label.
+ * 
+ * Constructor: mxSwimlane
+ *
+ * Constructs a new swimlane shape.
+ * 
+ * Parameters:
+ * 
+ * bounds - <mxRectangle> that defines the bounds. This is stored in
+ * <mxShape.bounds>.
+ * fill - String that defines the fill color. This is stored in <fill>.
+ * stroke - String that defines the stroke color. This is stored in <stroke>.
+ * strokewidth - Optional integer that defines the stroke width. Default is
+ * 1. This is stored in <strokewidth>.
+ */
+function mxSwimlane(bounds, fill, stroke, strokewidth)
+{
+	mxShape.call(this);
+	this.bounds = bounds;
+	this.fill = fill;
+	this.stroke = stroke;
+	this.strokewidth = (strokewidth != null) ? strokewidth : 1;
+};
+
+/**
+ * Extends mxShape.
+ */
+mxUtils.extend(mxSwimlane, mxShape);
+
+/**
+ * Variable: imageSize
+ *
+ * Default imagewidth and imageheight if an image but no imagewidth
+ * and imageheight are defined in the style. Value is 16.
+ */
+mxSwimlane.prototype.imageSize = 16;
+
+/**
+ * Function: getGradientBounds
+ * 
+ * Returns the bounding box for the gradient box for this shape.
+ */
+mxSwimlane.prototype.getTitleSize = function()
+{
+	return Math.max(0, mxUtils.getValue(this.style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE));
+};
+
+/**
+ * Function: getGradientBounds
+ * 
+ * Returns the bounding box for the gradient box for this shape.
+ */
+mxSwimlane.prototype.getLabelBounds = function(rect)
+{
+	var start = this.getTitleSize();
+	var bounds = new mxRectangle(rect.x, rect.y, rect.width, rect.height);
+	var horizontal = this.isHorizontal();
+	
+	var flipH = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPH, 0) == 1;
+	var flipV = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPV, 0) == 1;	
+	
+	// East is default
+	var shapeVertical = (this.direction == mxConstants.DIRECTION_NORTH ||
+			this.direction == mxConstants.DIRECTION_SOUTH);
+	var realHorizontal = horizontal == !shapeVertical;
+	
+	var realFlipH = !realHorizontal && flipH != (this.direction == mxConstants.DIRECTION_SOUTH ||
+			this.direction == mxConstants.DIRECTION_WEST);
+	var realFlipV = realHorizontal && flipV != (this.direction == mxConstants.DIRECTION_SOUTH ||
+			this.direction == mxConstants.DIRECTION_WEST);
+
+	// Shape is horizontal
+	if (!shapeVertical)
+	{
+		var tmp = Math.min(bounds.height, start * this.scale);
+
+		if (realFlipH || realFlipV)
+		{
+			bounds.y += bounds.height - tmp;
+		}
+
+		bounds.height = tmp;
+	}
+	else
+	{
+		var tmp = Math.min(bounds.width, start * this.scale);
+		
+		if (realFlipH || realFlipV)
+		{
+			bounds.x += bounds.width - tmp;	
+		}
+
+		bounds.width = tmp;
+	}
+	
+	return bounds;
+};
+
+/**
+ * Function: getGradientBounds
+ * 
+ * Returns the bounding box for the gradient box for this shape.
+ */
+mxSwimlane.prototype.getGradientBounds = function(c, x, y, w, h)
+{
+	var start = this.getTitleSize();
+	
+	if (this.isHorizontal())
+	{
+		start = Math.min(start, h);
+		return new mxRectangle(x, y, w, start);
+	}
+	else
+	{
+		start = Math.min(start, w);
+		return new mxRectangle(x, y, start, h);
+	}
+};
+
+/**
+ * Function: getArcSize
+ * 
+ * Returns the arcsize for the swimlane.
+ */
+mxSwimlane.prototype.getArcSize = function(w, h, start)
+{
+	var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100;
+
+	return start * f * 3; 
+};
+
+/**
+ * Function: paintVertexShape
+ *
+ * Paints the swimlane vertex shape.
+ */
+mxSwimlane.prototype.isHorizontal = function()
+{
+	return mxUtils.getValue(this.style, mxConstants.STYLE_HORIZONTAL, 1) == 1;
+};
+
+/**
+ * Function: paintVertexShape
+ *
+ * Paints the swimlane vertex shape.
+ */
+mxSwimlane.prototype.paintVertexShape = function(c, x, y, w, h)
+{
+	var start = this.getTitleSize();
+	var fill = mxUtils.getValue(this.style, mxConstants.STYLE_SWIMLANE_FILLCOLOR, mxConstants.NONE);
+	var swimlaneLine = mxUtils.getValue(this.style, mxConstants.STYLE_SWIMLANE_LINE, 1) == 1;
+	var r = 0;
+	
+	if (this.isHorizontal())
+	{
+		start = Math.min(start, h);
+	}
+	else
+	{
+		start = Math.min(start, w);
+	}
+	
+	c.translate(x, y);
+	
+	if (!this.isRounded)
+	{
+		this.paintSwimlane(c, x, y, w, h, start, fill, swimlaneLine);
+	}
+	else
+	{
+		r = this.getArcSize(w, h, start);
+		this.paintRoundedSwimlane(c, x, y, w, h, start, r, fill, swimlaneLine);
+	}
+	
+	var sep = mxUtils.getValue(this.style, mxConstants.STYLE_SEPARATORCOLOR, mxConstants.NONE);
+	this.paintSeparator(c, x, y, w, h, start, sep);
+
+	if (this.image != null)
+	{
+		var bounds = this.getImageBounds(x, y, w, h);
+		c.image(bounds.x - x, bounds.y - y, bounds.width, bounds.height,
+				this.image, false, false, false);
+	}
+	
+	if (this.glass)
+	{
+		c.setShadow(false);
+		this.paintGlassEffect(c, 0, 0, w, start, r);
+	}
+};
+
+/**
+ * Function: paintSwimlane
+ *
+ * Paints the swimlane vertex shape.
+ */
+mxSwimlane.prototype.paintSwimlane = function(c, x, y, w, h, start, fill, swimlaneLine)
+{
+	if (fill != mxConstants.NONE)
+	{
+		c.save();
+		c.setFillColor(fill);
+		c.rect(0, 0, w, h);
+		c.fillAndStroke();
+		c.restore();
+		c.setShadow(false);
+	}
+
+	c.begin();
+	
+	if (this.isHorizontal())
+	{
+		c.moveTo(0, start);
+		c.lineTo(0, 0);
+		c.lineTo(w, 0);
+		c.lineTo(w, start);
+		
+		if (swimlaneLine || start >= h)
+		{
+			c.close();
+		}
+		
+		c.fillAndStroke();
+		
+		// Transparent content area
+		if (start < h && fill == mxConstants.NONE)
+		{
+			c.pointerEvents = false;
+			
+			c.begin();
+			c.moveTo(0, start);
+			c.lineTo(0, h);
+			c.lineTo(w, h);
+			c.lineTo(w, start);
+			c.stroke();
+		}
+	}
+	else
+	{
+		c.moveTo(start, 0);
+		c.lineTo(0, 0);
+		c.lineTo(0, h);
+		c.lineTo(start, h);
+		
+		if (swimlaneLine || start >= w)
+		{
+			c.close();
+		}
+		
+		c.fillAndStroke();
+		
+		// Transparent content area
+		if (start < w && fill == mxConstants.NONE)
+		{
+			c.pointerEvents = false;
+			
+			c.begin();
+			c.moveTo(start, 0);
+			c.lineTo(w, 0);
+			c.lineTo(w, h);
+			c.lineTo(start, h);
+			c.stroke();
+		}
+	}
+};
+
+/**
+ * Function: paintRoundedSwimlane
+ *
+ * Paints the swimlane vertex shape.
+ */
+mxSwimlane.prototype.paintRoundedSwimlane = function(c, x, y, w, h, start, r, fill, swimlaneLine)
+{
+	r = Math.min(h - start, Math.min(start, r));
+	
+	if (fill != mxConstants.NONE)
+	{
+		c.save();
+		c.setFillColor(fill);
+		c.roundrect(0, 0, w, h, r, r);
+		c.fillAndStroke();
+		c.restore();
+		c.setShadow(false);
+	}
+	
+	c.begin();
+	
+	if (this.isHorizontal())
+	{
+		c.moveTo(w, start);
+		c.lineTo(w, r);
+		c.quadTo(w, 0, w - Math.min(w / 2, r), 0);
+		c.lineTo(Math.min(w / 2, r), 0);
+		c.quadTo(0, 0, 0, r);
+		c.lineTo(0, start);
+		
+		if (swimlaneLine || start >= h)
+		{
+			c.close();
+		}
+	
+		c.fillAndStroke();
+		
+		// Transparent content area
+		if (start < h && fill == mxConstants.NONE)
+		{
+			c.pointerEvents = false;
+			
+			c.begin();
+			c.moveTo(0, start);
+			c.lineTo(0, h - r);
+			c.quadTo(0, h, Math.min(w / 2, r), h);
+			c.lineTo(w - Math.min(w / 2, r), h);
+			c.quadTo(w, h, w, h - r);
+			c.lineTo(w, start);
+			c.stroke();
+		}
+	}
+	else
+	{
+		c.moveTo(start, 0);
+		c.lineTo(r, 0);
+		c.quadTo(0, 0, 0, Math.min(h / 2, r));
+		c.lineTo(0, h - Math.min(h / 2, r));
+		c.quadTo(0, h, r, h);
+		c.lineTo(start, h);
+		
+		if (swimlaneLine || start >= w)
+		{
+			c.close();
+		}
+	
+		c.fillAndStroke();
+		
+		// Transparent content area
+		if (start < w && fill == mxConstants.NONE)
+		{
+			c.pointerEvents = false;
+			
+			c.begin();
+			c.moveTo(start, h);
+			c.lineTo(w - r, h);
+			c.quadTo(w, h, w, h - Math.min(h / 2, r));
+			c.lineTo(w, Math.min(h / 2, r));
+			c.quadTo(w, 0, w - r, 0);
+			c.lineTo(start, 0);
+			c.stroke();
+		}
+	}
+};
+
+/**
+ * Function: paintSwimlane
+ *
+ * Paints the swimlane vertex shape.
+ */
+mxSwimlane.prototype.paintSeparator = function(c, x, y, w, h, start, color)
+{
+	if (color != mxConstants.NONE)
+	{
+		c.setStrokeColor(color);
+		c.setDashed(true);
+		c.begin();
+		
+		if (this.isHorizontal())
+		{
+			c.moveTo(w, start);
+			c.lineTo(w, h);
+		}
+		else
+		{
+			c.moveTo(start, 0);
+			c.lineTo(w, 0);
+		}
+		
+		c.stroke();
+		c.setDashed(false);
+	}
+};
+
+/**
+ * Function: getImageBounds
+ *
+ * Paints the swimlane vertex shape.
+ */
+mxSwimlane.prototype.getImageBounds = function(x, y, w, h)
+{
+	if (this.isHorizontal())
+	{
+		return new mxRectangle(x + w - this.imageSize, y, this.imageSize, this.imageSize);
+	}
+	else
+	{
+		return new mxRectangle(x, y, this.imageSize, this.imageSize);
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphLayout
+ * 
+ * Base class for all layout algorithms in mxGraph. Main public functions are
+ * <move> for handling a moved cell within a layouted parent, and <execute> for
+ * running the layout on a given parent cell.
+ *
+ * Known Subclasses:
+ *
+ * <mxCircleLayout>, <mxCompactTreeLayout>, <mxCompositeLayout>,
+ * <mxFastOrganicLayout>, <mxParallelEdgeLayout>, <mxPartitionLayout>,
+ * <mxStackLayout>
+ * 
+ * Constructor: mxGraphLayout
+ *
+ * Constructs a new layout using the given layouts.
+ *
+ * Arguments:
+ * 
+ * graph - Enclosing 
+ */
+function mxGraphLayout(graph)
+{
+	this.graph = graph;
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxGraphLayout.prototype.graph = null;
+
+/**
+ * Variable: useBoundingBox
+ *
+ * Boolean indicating if the bounding box of the label should be used if
+ * its available. Default is true.
+ */
+mxGraphLayout.prototype.useBoundingBox = true;
+
+/**
+ * Variable: parent
+ *
+ * The parent cell of the layout, if any
+ */
+mxGraphLayout.prototype.parent = null;
+
+/**
+ * Function: moveCell
+ * 
+ * Notified when a cell is being moved in a parent that has automatic
+ * layout to update the cell state (eg. index) so that the outcome of the
+ * layout will position the vertex as close to the point (x, y) as
+ * possible.
+ * 
+ * Empty implementation.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> which has been moved.
+ * x - X-coordinate of the new cell location.
+ * y - Y-coordinate of the new cell location.
+ */
+mxGraphLayout.prototype.moveCell = function(cell, x, y) { };
+
+/**
+ * Function: execute
+ * 
+ * Executes the layout algorithm for the children of the given parent.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose children should be layed out.
+ */
+mxGraphLayout.prototype.execute = function(parent) { };
+
+/**
+ * Function: getGraph
+ * 
+ * Returns the graph that this layout operates on.
+ */
+mxGraphLayout.prototype.getGraph = function()
+{
+	return this.graph;
+};
+
+/**
+ * Function: getConstraint
+ * 
+ * Returns the constraint for the given key and cell. The optional edge and
+ * source arguments are used to return inbound and outgoing routing-
+ * constraints for the given edge and vertex. This implementation always
+ * returns the value for the given key in the style of the given cell.
+ * 
+ * Parameters:
+ * 
+ * key - Key of the constraint to be returned.
+ * cell - <mxCell> whose constraint should be returned.
+ * edge - Optional <mxCell> that represents the connection whose constraint
+ * should be returned. Default is null.
+ * source - Optional boolean that specifies if the connection is incoming
+ * or outgoing. Default is null.
+ */
+mxGraphLayout.prototype.getConstraint = function(key, cell, edge, source)
+{
+	var state = this.graph.view.getState(cell);
+	var style = (state != null) ? state.style : this.graph.getCellStyle(cell);
+	
+	return (style != null) ? style[key] : null;
+};
+
+/**
+ * Function: traverse
+ * 
+ * Traverses the (directed) graph invoking the given function for each
+ * visited vertex and edge. The function is invoked with the current vertex
+ * and the incoming edge as a parameter. This implementation makes sure
+ * each vertex is only visited once. The function may return false if the
+ * traversal should stop at the given vertex.
+ * 
+ * Example:
+ * 
+ * (code)
+ * mxLog.show();
+ * var cell = graph.getSelectionCell();
+ * graph.traverse(cell, false, function(vertex, edge)
+ * {
+ *   mxLog.debug(graph.getLabel(vertex));
+ * });
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> that represents the vertex where the traversal starts.
+ * directed - Optional boolean indicating if edges should only be traversed
+ * from source to target. Default is true.
+ * func - Visitor function that takes the current vertex and the incoming
+ * edge as arguments. The traversal stops if the function returns false.
+ * edge - Optional <mxCell> that represents the incoming edge. This is
+ * null for the first step of the traversal.
+ * visited - Optional <mxDictionary> of cell paths for the visited cells.
+ */
+mxGraphLayout.traverse = function(vertex, directed, func, edge, visited)
+{
+	if (func != null && vertex != null)
+	{
+		directed = (directed != null) ? directed : true;
+		visited = visited || new mxDictionary();
+		
+		if (!visited.get(vertex))
+		{
+			visited.put(vertex, true);
+			var result = func(vertex, edge);
+			
+			if (result == null || result)
+			{
+				var edgeCount = this.graph.model.getEdgeCount(vertex);
+				
+				if (edgeCount > 0)
+				{
+					for (var i = 0; i < edgeCount; i++)
+					{
+						var e = this.graph.model.getEdgeAt(vertex, i);
+						var isSource = this.graph.model.getTerminal(e, true) == vertex;
+												
+						if (!directed || isSource)
+						{
+							var next = this.graph.view.getVisibleTerminal(e, !isSource);
+							this.traverse(next, directed, func, e, visited);
+						}
+					}
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: isVertexMovable
+ * 
+ * Returns a boolean indicating if the given <mxCell> is movable or
+ * bendable by the algorithm. This implementation returns true if the given
+ * cell is movable in the graph.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose movable state should be returned.
+ */
+mxGraphLayout.prototype.isVertexMovable = function(cell)
+{
+	return this.graph.isCellMovable(cell);
+};
+
+/**
+ * Function: isVertexIgnored
+ * 
+ * Returns a boolean indicating if the given <mxCell> should be ignored by
+ * the algorithm. This implementation returns false for all vertices.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> whose ignored state should be returned.
+ */
+mxGraphLayout.prototype.isVertexIgnored = function(vertex)
+{
+	return !this.graph.getModel().isVertex(vertex) ||
+		!this.graph.isCellVisible(vertex);
+};
+
+/**
+ * Function: isEdgeIgnored
+ * 
+ * Returns a boolean indicating if the given <mxCell> should be ignored by
+ * the algorithm. This implementation returns false for all vertices.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose ignored state should be returned.
+ */
+mxGraphLayout.prototype.isEdgeIgnored = function(edge)
+{
+	var model = this.graph.getModel();
+	
+	return !model.isEdge(edge) ||
+		!this.graph.isCellVisible(edge) ||
+		model.getTerminal(edge, true) == null ||
+		model.getTerminal(edge, false) == null;
+};
+
+/**
+ * Function: setEdgeStyleEnabled
+ * 
+ * Disables or enables the edge style of the given edge.
+ */
+mxGraphLayout.prototype.setEdgeStyleEnabled = function(edge, value)
+{
+	this.graph.setCellStyles(mxConstants.STYLE_NOEDGESTYLE,
+			(value) ? '0' : '1', [edge]);
+};
+
+/**
+ * Function: setOrthogonalEdge
+ * 
+ * Disables or enables orthogonal end segments of the given edge.
+ */
+mxGraphLayout.prototype.setOrthogonalEdge = function(edge, value)
+{
+	this.graph.setCellStyles(mxConstants.STYLE_ORTHOGONAL,
+			(value) ? '1' : '0', [edge]);
+};
+
+/**
+ * Function: getParentOffset
+ * 
+ * Determines the offset of the given parent to the parent
+ * of the layout
+ */
+mxGraphLayout.prototype.getParentOffset = function(parent)
+{
+	var result = new mxPoint();
+
+	if (parent != null && parent != this.parent)
+	{
+		var model = this.graph.getModel();
+
+		if (model.isAncestor(this.parent, parent))
+		{
+			var parentGeo = model.getGeometry(parent);
+
+			while (parent != this.parent)
+			{
+				result.x = result.x + parentGeo.x;
+				result.y = result.y + parentGeo.y;
+
+				parent = model.getParent(parent);;
+				parentGeo = model.getGeometry(parent);
+			}
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Function: setEdgePoints
+ * 
+ * Replaces the array of mxPoints in the geometry of the given edge
+ * with the given array of mxPoints.
+ */
+mxGraphLayout.prototype.setEdgePoints = function(edge, points)
+{
+	if (edge != null)
+	{
+		var model = this.graph.model;
+		var geometry = model.getGeometry(edge);
+
+		if (geometry == null)
+		{
+			geometry = new mxGeometry();
+			geometry.setRelative(true);
+		}
+		else
+		{
+			geometry = geometry.clone();
+		}
+
+		if (this.parent != null && points != null)
+		{
+			var parent = model.getParent(edge);
+
+			var parentOffset = this.getParentOffset(parent);
+
+			for (var i = 0; i < points.length; i++)
+			{
+				points[i].x = points[i].x - parentOffset.x;
+				points[i].y = points[i].y - parentOffset.y;
+			}
+		}
+
+		geometry.points = points;
+		model.setGeometry(edge, geometry);
+	}
+};
+
+/**
+ * Function: setVertexLocation
+ * 
+ * Sets the new position of the given cell taking into account the size of
+ * the bounding box if <useBoundingBox> is true. The change is only carried
+ * out if the new location is not equal to the existing location, otherwise
+ * the geometry is not replaced with an updated instance. The new or old
+ * bounds are returned (including overlapping labels).
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose geometry is to be set.
+ * x - Integer that defines the x-coordinate of the new location.
+ * y - Integer that defines the y-coordinate of the new location.
+ */
+mxGraphLayout.prototype.setVertexLocation = function(cell, x, y)
+{
+	var model = this.graph.getModel();
+	var geometry = model.getGeometry(cell);
+	var result = null;
+	
+	if (geometry != null)
+	{
+		result = new mxRectangle(x, y, geometry.width, geometry.height);
+		
+		// Checks for oversize labels and shifts the result
+		// TODO: Use mxUtils.getStringSize for label bounds
+		if (this.useBoundingBox)
+		{
+			var state = this.graph.getView().getState(cell);
+			
+			if (state != null && state.text != null && state.text.boundingBox != null)
+			{
+				var scale = this.graph.getView().scale;
+				var box = state.text.boundingBox;
+				
+				if (state.text.boundingBox.x < state.x)
+				{
+					x += (state.x - box.x) / scale;
+					result.width = box.width;
+				}
+				
+				if (state.text.boundingBox.y < state.y)
+				{
+					y += (state.y - box.y) / scale;
+					result.height = box.height;
+				}
+			}
+		}
+
+		if (this.parent != null)
+		{
+			var parent = model.getParent(cell);
+
+			if (parent != null && parent != this.parent)
+			{
+				var parentOffset = this.getParentOffset(parent);
+
+				x = x - parentOffset.x;
+				y = y - parentOffset.y;
+			}
+		}
+
+		if (geometry.x != x || geometry.y != y)
+		{
+			geometry = geometry.clone();
+			geometry.x = x;
+			geometry.y = y;
+			
+			model.setGeometry(cell, geometry);
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getVertexBounds
+ * 
+ * Returns an <mxRectangle> that defines the bounds of the given cell or
+ * the bounding box if <useBoundingBox> is true.
+ */
+mxGraphLayout.prototype.getVertexBounds = function(cell)
+{
+	var geo = this.graph.getModel().getGeometry(cell);
+
+	// Checks for oversize label bounding box and corrects
+	// the return value accordingly
+	// TODO: Use mxUtils.getStringSize for label bounds
+	if (this.useBoundingBox)
+	{
+		var state = this.graph.getView().getState(cell);
+
+		if (state != null && state.text != null && state.text.boundingBox != null)
+		{
+			var scale = this.graph.getView().scale;
+			var tmp = state.text.boundingBox;
+
+			var dx0 = Math.max(state.x - tmp.x, 0) / scale;
+			var dy0 = Math.max(state.y - tmp.y, 0) / scale;
+			var dx1 = Math.max((tmp.x + tmp.width) - (state.x + state.width), 0) / scale;
+  			var dy1 = Math.max((tmp.y + tmp.height) - (state.y + state.height), 0) / scale;
+
+			geo = new mxRectangle(geo.x - dx0, geo.y - dy0, geo.width + dx0 + dx1, geo.height + dy0 + dy1);
+		}
+	}
+
+	if (this.parent != null)
+	{
+		var parent = this.graph.getModel().getParent(cell);
+		geo = geo.clone();
+
+		if (parent != null && parent != this.parent)
+		{
+			var parentOffset = this.getParentOffset(parent);
+			geo.x = geo.x + parentOffset.x;
+			geo.y = geo.y + parentOffset.y;
+		}
+	}
+
+	return new mxRectangle(geo.x, geo.y, geo.width, geo.height);
+};
+
+/**
+ * Function: arrangeGroups
+ * 
+ * Shortcut to <mxGraph.updateGroupBounds> with moveGroup set to true.
+ */
+mxGraphLayout.prototype.arrangeGroups = function(cells, border, topBorder, rightBorder, bottomBorder, leftBorder)
+{
+	return this.graph.updateGroupBounds(cells, border, true, topBorder, rightBorder, bottomBorder, leftBorder);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxStackLayout
+ * 
+ * Extends <mxGraphLayout> to create a horizontal or vertical stack of the
+ * child vertices. The children do not need to be connected for this layout
+ * to work.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var layout = new mxStackLayout(graph, true);
+ * layout.execute(graph.getDefaultParent());
+ * (end)
+ * 
+ * Constructor: mxStackLayout
+ * 
+ * Constructs a new stack layout layout for the specified graph,
+ * spacing, orientation and offset.
+ */
+function mxStackLayout(graph, horizontal, spacing, x0, y0, border)
+{
+	mxGraphLayout.call(this, graph);
+	this.horizontal = (horizontal != null) ? horizontal : true;
+	this.spacing = (spacing != null) ? spacing : 0;
+	this.x0 = (x0 != null) ? x0 : 0;
+	this.y0 = (y0 != null) ? y0 : 0;
+	this.border = (border != null) ? border : 0;
+};
+
+/**
+ * Extends mxGraphLayout.
+ */
+mxStackLayout.prototype = new mxGraphLayout();
+mxStackLayout.prototype.constructor = mxStackLayout;
+
+/**
+ * Variable: horizontal
+ *
+ * Specifies the orientation of the layout. Default is true.
+ */
+mxStackLayout.prototype.horizontal = null;
+
+/**
+ * Variable: spacing
+ *
+ * Specifies the spacing between the cells. Default is 0.
+ */
+mxStackLayout.prototype.spacing = null;
+
+/**
+ * Variable: x0
+ *
+ * Specifies the horizontal origin of the layout. Default is 0.
+ */
+mxStackLayout.prototype.x0 = null;
+
+/**
+ * Variable: y0
+ *
+ * Specifies the vertical origin of the layout. Default is 0.
+ */
+mxStackLayout.prototype.y0 = null;
+
+/**
+ * Variable: border
+ *
+ * Border to be added if fill is true. Default is 0.
+ */
+mxStackLayout.prototype.border = 0;
+
+/**
+ * Variable: marginTop
+ * 
+ * Top margin for the child area. Default is 0.
+ */
+mxStackLayout.prototype.marginTop = 0;
+
+/**
+ * Variable: marginLeft
+ * 
+ * Top margin for the child area. Default is 0.
+ */
+mxStackLayout.prototype.marginLeft = 0;
+
+/**
+ * Variable: marginRight
+ * 
+ * Top margin for the child area. Default is 0.
+ */
+mxStackLayout.prototype.marginRight = 0;
+
+/**
+ * Variable: marginBottom
+ * 
+ * Top margin for the child area. Default is 0.
+ */
+mxStackLayout.prototype.marginBottom = 0;
+
+/**
+ * Variable: keepFirstLocation
+ * 
+ * Boolean indicating if the location of the first cell should be
+ * kept, that is, it will not be moved to x0 or y0.
+ */
+mxStackLayout.prototype.keepFirstLocation = false;
+
+/**
+ * Variable: fill
+ * 
+ * Boolean indicating if dimension should be changed to fill out the parent
+ * cell. Default is false.
+ */
+mxStackLayout.prototype.fill = false;
+	
+/**
+ * Variable: resizeParent
+ * 
+ * If the parent should be resized to match the width/height of the
+ * stack. Default is false.
+ */
+mxStackLayout.prototype.resizeParent = false;
+
+/**
+ * Variable: resizeParentMax
+ * 
+ * Use maximum of existing value and new value for resize of parent.
+ * Default is false.
+ */
+mxStackLayout.prototype.resizeParentMax = false;
+
+/**
+ * Variable: resizeLast
+ * 
+ * If the last element should be resized to fill out the parent. Default is
+ * false. If <resizeParent> is true then this is ignored.
+ */
+mxStackLayout.prototype.resizeLast = false;
+
+/**
+ * Variable: wrap
+ * 
+ * Value at which a new column or row should be created. Default is null.
+ */
+mxStackLayout.prototype.wrap = null;
+
+/**
+ * Variable: borderCollapse
+ * 
+ * If the strokeWidth should be ignored. Default is true.
+ */
+mxStackLayout.prototype.borderCollapse = true;
+
+/**
+ * Function: isHorizontal
+ * 
+ * Returns <horizontal>.
+ */
+mxStackLayout.prototype.isHorizontal = function()
+{
+	return this.horizontal;
+};
+
+/**
+ * Function: moveCell
+ * 
+ * Implements <mxGraphLayout.moveCell>.
+ */
+mxStackLayout.prototype.moveCell = function(cell, x, y)
+{
+	var model = this.graph.getModel();
+	var parent = model.getParent(cell);
+	var horizontal = this.isHorizontal();
+	
+	if (cell != null && parent != null)
+	{
+		var i = 0;
+		var last = 0;
+		var childCount = model.getChildCount(parent);
+		var value = (horizontal) ? x : y;
+		var pstate = this.graph.getView().getState(parent);
+
+		if (pstate != null)
+		{
+			value -= (horizontal) ? pstate.x : pstate.y;
+		}
+		
+		value /= this.graph.view.scale;
+		
+		for (i = 0; i < childCount; i++)
+		{
+			var child = model.getChildAt(parent, i);
+			
+			if (child != cell)
+			{
+				var bounds = model.getGeometry(child);
+				
+				if (bounds != null)
+				{
+					var tmp = (horizontal) ?
+						bounds.x + bounds.width / 2 :
+						bounds.y + bounds.height / 2;
+					
+					if (last <= value && tmp > value)
+					{
+						break;
+					}
+					
+					last = tmp;
+				}
+			}
+		}
+
+		// Changes child order in parent
+		var idx = parent.getIndex(cell);
+		idx = Math.max(0, i - ((i > idx) ? 1 : 0));
+
+		model.add(parent, cell, idx);
+	}
+};
+
+/**
+ * Function: getParentSize
+ * 
+ * Returns the size for the parent container or the size of the graph
+ * container if the parent is a layer or the root of the model.
+ */
+mxStackLayout.prototype.getParentSize = function(parent)
+{
+	var model = this.graph.getModel();			
+	var pgeo = model.getGeometry(parent);
+	
+	// Handles special case where the parent is either a layer with no
+	// geometry or the current root of the view in which case the size
+	// of the graph's container will be used.
+	if (this.graph.container != null && ((pgeo == null &&
+		model.isLayer(parent)) || parent == this.graph.getView().currentRoot))
+	{
+		var width = this.graph.container.offsetWidth - 1;
+		var height = this.graph.container.offsetHeight - 1;
+		pgeo = new mxRectangle(0, 0, width, height);
+	}
+	
+	return pgeo;
+};
+
+/**
+ * Function: execute
+ * 
+ * Implements <mxGraphLayout.execute>.
+ * 
+ * Only children where <isVertexIgnored> returns false are taken into
+ * account.
+ */
+mxStackLayout.prototype.execute = function(parent)
+{
+	if (parent != null)
+	{
+		var pgeo = this.getParentSize(parent);
+		var horizontal = this.isHorizontal();
+		var model = this.graph.getModel();	
+		var fillValue = null;
+		
+		if (pgeo != null)
+		{
+			fillValue = (horizontal) ? pgeo.height - this.marginTop - this.marginBottom :
+				pgeo.width - this.marginLeft - this.marginRight;
+		}
+		
+		fillValue -= 2 * this.spacing + 2 * this.border;
+		var x0 = this.x0 + this.border + this.marginLeft;
+		var y0 = this.y0 + this.border + this.marginTop;
+		
+		// Handles swimlane start size
+		if (this.graph.isSwimlane(parent))
+		{
+			// Uses computed style to get latest 
+			var style = this.graph.getCellStyle(parent);
+			var start = mxUtils.getNumber(style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE);
+			var horz = mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true) == 1;
+
+			if (pgeo != null)
+			{
+				if (horz)
+				{
+					start = Math.min(start, pgeo.height);
+				}
+				else
+				{
+					start = Math.min(start, pgeo.width);
+				}
+			}
+			
+			if (horizontal == horz)
+			{
+				fillValue -= start;
+			}
+
+			if (horz)
+			{
+				y0 += start;
+			}
+			else
+			{
+				x0 += start;
+			}
+		}
+
+		model.beginUpdate();
+		try
+		{
+			var tmp = 0;
+			var last = null;
+			var lastValue = 0;
+			var lastChild = null;
+			var childCount = model.getChildCount(parent);
+			
+			for (var i = 0; i < childCount; i++)
+			{
+				var child = model.getChildAt(parent, i);
+				
+				if (!this.isVertexIgnored(child) && this.isVertexMovable(child))
+				{
+					var geo = model.getGeometry(child);
+					
+					if (geo != null)
+					{
+						geo = geo.clone();
+						
+						if (this.wrap != null && last != null)
+						{
+							if ((horizontal && last.x + last.width +
+								geo.width + 2 * this.spacing > this.wrap) ||
+								(!horizontal && last.y + last.height +
+								geo.height + 2 * this.spacing > this.wrap))
+							{
+								last = null;
+								
+								if (horizontal)
+								{
+									y0 += tmp + this.spacing;
+								}
+								else
+								{
+									x0 += tmp + this.spacing;
+								}
+								
+								tmp = 0;
+							}	
+						}
+						
+						tmp = Math.max(tmp, (horizontal) ? geo.height : geo.width);
+						var sw = 0;
+						
+						if (!this.borderCollapse)
+						{
+							var childStyle = this.graph.getCellStyle(child);
+							sw = mxUtils.getNumber(childStyle, mxConstants.STYLE_STROKEWIDTH, 1);
+						}
+						
+						if (last != null)
+						{
+							if (horizontal)
+							{
+								geo.x = lastValue + this.spacing + Math.floor(sw / 2);
+							}
+							else
+							{
+								geo.y = lastValue + this.spacing + Math.floor(sw / 2);
+							}
+						}
+						else if (!this.keepFirstLocation)
+						{
+							if (horizontal)
+							{
+								geo.x = x0;
+							}
+							else
+							{
+								geo.y = y0;
+							}
+						}
+						
+						if (horizontal)
+						{
+							geo.y = y0;
+						}
+						else
+						{
+							geo.x = x0;
+						}
+						
+						if (this.fill && fillValue != null)
+						{
+							if (horizontal)
+							{
+								geo.height = fillValue;
+							}
+							else
+							{
+								geo.width = fillValue;									
+							}
+						}
+						
+						this.setChildGeometry(child, geo);
+						lastChild = child;
+						last = geo;
+						
+						if (horizontal)
+						{
+							lastValue = last.x + last.width + Math.floor(sw / 2);
+						}
+						else
+						{
+							lastValue = last.y + last.height + Math.floor(sw / 2);
+						}
+					}
+				}
+			}
+
+			if (this.resizeParent && pgeo != null && last != null && !this.graph.isCellCollapsed(parent))
+			{
+				this.updateParentGeometry(parent, pgeo, last);
+			}
+			else if (this.resizeLast && pgeo != null && last != null && lastChild != null)
+			{
+				if (horizontal)
+				{
+					last.width = pgeo.width - last.x - this.spacing - this.marginRight - this.marginLeft;
+				}
+				else
+				{
+					last.height = pgeo.height - last.y - this.spacing - this.marginBottom;
+				}
+				
+				this.setChildGeometry(lastChild, last);
+			}
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: execute
+ * 
+ * Implements <mxGraphLayout.execute>.
+ * 
+ * Only children where <isVertexIgnored> returns false are taken into
+ * account.
+ */
+mxStackLayout.prototype.setChildGeometry = function(child, geo)
+{
+	var geo2 = this.graph.getCellGeometry(child);
+	
+	if (geo2 == null || geo.x != geo2.x || geo.y != geo2.y ||
+		geo.width != geo2.width || geo.height != geo2.height)
+	{
+		this.graph.getModel().setGeometry(child, geo);
+	}
+};
+
+/**
+ * Function: execute
+ * 
+ * Implements <mxGraphLayout.execute>.
+ * 
+ * Only children where <isVertexIgnored> returns false are taken into
+ * account.
+ */
+mxStackLayout.prototype.updateParentGeometry = function(parent, pgeo, last)
+{
+	var horizontal = this.isHorizontal();
+	var model = this.graph.getModel();	
+
+	var pgeo2 = pgeo.clone();
+	
+	if (horizontal)
+	{
+		var tmp = last.x + last.width + this.spacing + this.marginRight;
+		
+		if (this.resizeParentMax)
+		{
+			pgeo2.width = Math.max(pgeo2.width, tmp);
+		}
+		else
+		{
+			pgeo2.width = tmp;
+		}
+	}
+	else
+	{
+		var tmp = last.y + last.height + this.spacing + this.marginBottom;
+		
+		if (this.resizeParentMax)
+		{
+			pgeo2.height = Math.max(pgeo2.height, tmp);
+		}
+		else
+		{
+			pgeo2.height = tmp;
+		}
+	}
+	
+	if (pgeo.x != pgeo2.x || pgeo.y != pgeo2.y ||
+		pgeo.width != pgeo2.width || pgeo.height != pgeo2.height)
+	{
+		model.setGeometry(parent, pgeo2);
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxPartitionLayout
+ * 
+ * Extends <mxGraphLayout> for partitioning the parent cell vertically or
+ * horizontally by filling the complete area with the child cells. A horizontal
+ * layout partitions the height of the given parent whereas a a non-horizontal
+ * layout partitions the width. If the parent is a layer (that is, a child of
+ * the root node), then the current graph size is partitioned. The children do
+ * not need to be connected for this layout to work.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var layout = new mxPartitionLayout(graph, true, 10, 20);
+ * layout.execute(graph.getDefaultParent());
+ * (end)
+ * 
+ * Constructor: mxPartitionLayout
+ * 
+ * Constructs a new stack layout layout for the specified graph,
+ * spacing, orientation and offset.
+ */
+function mxPartitionLayout(graph, horizontal, spacing, border)
+{
+	mxGraphLayout.call(this, graph);
+	this.horizontal = (horizontal != null) ? horizontal : true;
+	this.spacing = spacing || 0;
+	this.border = border || 0;
+};
+
+/**
+ * Extends mxGraphLayout.
+ */
+mxPartitionLayout.prototype = new mxGraphLayout();
+mxPartitionLayout.prototype.constructor = mxPartitionLayout;
+
+/**
+ * Variable: horizontal
+ * 
+ * Boolean indicating the direction in which the space is partitioned.
+ * Default is true.
+ */
+mxPartitionLayout.prototype.horizontal = null;
+
+/**
+ * Variable: spacing
+ * 
+ * Integer that specifies the absolute spacing in pixels between the
+ * children. Default is 0.
+ */
+mxPartitionLayout.prototype.spacing = null;
+
+/**
+ * Variable: border
+ * 
+ * Integer that specifies the absolute inset in pixels for the parent that
+ * contains the children. Default is 0.
+ */
+mxPartitionLayout.prototype.border = null;
+
+/**
+ * Variable: resizeVertices
+ * 
+ * Boolean that specifies if vertices should be resized. Default is true.
+ */
+mxPartitionLayout.prototype.resizeVertices = true;
+
+/**
+ * Function: isHorizontal
+ * 
+ * Returns <horizontal>.
+ */
+mxPartitionLayout.prototype.isHorizontal = function()
+{
+	return this.horizontal;
+};
+
+/**
+ * Function: moveCell
+ * 
+ * Implements <mxGraphLayout.moveCell>.
+ */
+mxPartitionLayout.prototype.moveCell = function(cell, x, y)
+{
+	var model = this.graph.getModel();
+	var parent = model.getParent(cell);
+	
+	if (cell != null &&
+		parent != null)
+	{
+		var i = 0;
+		var last = 0;
+		var childCount = model.getChildCount(parent);
+		
+		// Finds index of the closest swimlane
+		// TODO: Take into account the orientation
+		for (i = 0; i < childCount; i++)
+		{
+			var child = model.getChildAt(parent, i);
+			var bounds = this.getVertexBounds(child);
+			
+			if (bounds != null)
+			{
+				var tmp = bounds.x + bounds.width / 2;
+				
+				if (last < x && tmp > x)
+				{
+					break;
+				}
+				
+				last = tmp;
+			}
+		}
+		
+		// Changes child order in parent
+		var idx = parent.getIndex(cell);
+		idx = Math.max(0, i - ((i > idx) ? 1 : 0));
+		
+		model.add(parent, cell, idx);
+	}
+};
+
+/**
+ * Function: execute
+ * 
+ * Implements <mxGraphLayout.execute>. All children where <isVertexIgnored>
+ * returns false and <isVertexMovable> returns true are modified.
+ */
+mxPartitionLayout.prototype.execute = function(parent)
+{
+	var horizontal = this.isHorizontal();
+	var model = this.graph.getModel();
+	var pgeo = model.getGeometry(parent);
+	
+	// Handles special case where the parent is either a layer with no
+	// geometry or the current root of the view in which case the size
+	// of the graph's container will be used.
+	if (this.graph.container != null &&
+		((pgeo == null &&
+		model.isLayer(parent)) ||
+		parent == this.graph.getView().currentRoot))
+	{
+		var width = this.graph.container.offsetWidth - 1;
+		var height = this.graph.container.offsetHeight - 1;
+		pgeo = new mxRectangle(0, 0, width, height);
+	}
+
+	if (pgeo != null)
+	{
+		var children = [];
+		var childCount = model.getChildCount(parent);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			var child = model.getChildAt(parent, i);
+			
+			if (!this.isVertexIgnored(child) &&
+				this.isVertexMovable(child))
+			{
+				children.push(child);
+			}
+		}
+		
+		var n = children.length;
+
+		if (n > 0)
+		{
+			var x0 = this.border;
+			var y0 = this.border;
+			var other = (horizontal) ? pgeo.height : pgeo.width;
+			other -= 2 * this.border;
+
+			var size = (this.graph.isSwimlane(parent)) ?
+				this.graph.getStartSize(parent) :
+				new mxRectangle();
+
+			other -= (horizontal) ? size.height : size.width;
+			x0 = x0 + size.width;
+			y0 = y0 + size.height;
+
+			var tmp = this.border + (n - 1) * this.spacing;
+			var value = (horizontal) ?
+				((pgeo.width - x0 - tmp) / n) :
+				((pgeo.height - y0 - tmp) / n);
+			
+			// Avoids negative values, that is values where the sum of the
+			// spacing plus the border is larger then the available space
+			if (value > 0)
+			{
+				model.beginUpdate();
+				try
+				{
+					for (var i = 0; i < n; i++)
+					{
+						var child = children[i];
+						var geo = model.getGeometry(child);
+					
+						if (geo != null)
+						{
+							geo = geo.clone();
+							geo.x = x0;
+							geo.y = y0;
+
+							if (horizontal)
+							{
+								if (this.resizeVertices)
+								{
+									geo.width = value;
+									geo.height = other;
+								}
+								
+								x0 += value + this.spacing;
+							}
+							else
+							{
+								if (this.resizeVertices)
+								{
+									geo.height = value;
+									geo.width = other;
+								}
+								
+								y0 += value + this.spacing;
+							}
+
+							model.setGeometry(child, geo);
+						}
+					}
+				}
+				finally
+				{
+					model.endUpdate();
+				}
+			}
+		}
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCompactTreeLayout
+ * 
+ * Extends <mxGraphLayout> to implement a compact tree (Moen) algorithm. This
+ * layout is suitable for graphs that have no cycles (trees). Vertices that are
+ * not connected to the tree will be ignored by this layout.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var layout = new mxCompactTreeLayout(graph);
+ * layout.execute(graph.getDefaultParent());
+ * (end)
+ * 
+ * Constructor: mxCompactTreeLayout
+ * 
+ * Constructs a new compact tree layout for the specified graph
+ * and orientation.
+ */
+function mxCompactTreeLayout(graph, horizontal, invert)
+{
+	mxGraphLayout.call(this, graph);
+	this.horizontal = (horizontal != null) ? horizontal : true;
+	this.invert = (invert != null) ? invert : false;
+};
+
+/**
+ * Extends mxGraphLayout.
+ */
+mxCompactTreeLayout.prototype = new mxGraphLayout();
+mxCompactTreeLayout.prototype.constructor = mxCompactTreeLayout;
+
+/**
+ * Variable: horizontal
+ *
+ * Specifies the orientation of the layout. Default is true.
+ */
+mxCompactTreeLayout.prototype.horizontal = null;	 
+
+/**
+ * Variable: invert
+ *
+ * Specifies if edge directions should be inverted. Default is false.
+ */
+mxCompactTreeLayout.prototype.invert = null;	 
+
+/**
+ * Variable: resizeParent
+ * 
+ * If the parents should be resized to match the width/height of the
+ * children. Default is true.
+ */
+mxCompactTreeLayout.prototype.resizeParent = true;
+
+/**
+ * Variable: maintainParentLocation
+ * 
+ * Specifies if the parent location should be maintained, so that the
+ * top, left corner stays the same before and after execution of
+ * the layout. Default is false for backwards compatibility.
+ */
+mxCompactTreeLayout.prototype.maintainParentLocation = false;
+
+/**
+ * Variable: groupPadding
+ * 
+ * Padding added to resized parents. Default is 10.
+ */
+mxCompactTreeLayout.prototype.groupPadding = 10;
+
+/**
+ * Variable: groupPaddingTop
+ * 
+ * Top padding added to resized parents. Default is 0.
+ */
+mxCompactTreeLayout.prototype.groupPaddingTop = 0;
+
+/**
+ * Variable: groupPaddingRight
+ * 
+ * Right padding added to resized parents. Default is 0.
+ */
+mxCompactTreeLayout.prototype.groupPaddingRight = 0;
+
+/**
+ * Variable: groupPaddingBottom
+ * 
+ * Bottom padding added to resized parents. Default is 0.
+ */
+mxCompactTreeLayout.prototype.groupPaddingBottom = 0;
+
+/**
+ * Variable: groupPaddingLeft
+ * 
+ * Left padding added to resized parents. Default is 0.
+ */
+mxCompactTreeLayout.prototype.groupPaddingLeft = 0;
+
+/**
+ * Variable: parentsChanged
+ *
+ * A set of the parents that need updating based on children
+ * process as part of the layout.
+ */
+mxCompactTreeLayout.prototype.parentsChanged = null;
+
+/**
+ * Variable: moveTree
+ * 
+ * Specifies if the tree should be moved to the top, left corner
+ * if it is inside a top-level layer. Default is false.
+ */
+mxCompactTreeLayout.prototype.moveTree = false;
+
+/**
+ * Variable: visited
+ * 
+ * Specifies if the tree should be moved to the top, left corner
+ * if it is inside a top-level layer. Default is false.
+ */
+mxCompactTreeLayout.prototype.visited = null;
+
+/**
+ * Variable: levelDistance
+ *
+ * Holds the levelDistance. Default is 10.
+ */
+mxCompactTreeLayout.prototype.levelDistance = 10;
+
+/**
+ * Variable: nodeDistance
+ *
+ * Holds the nodeDistance. Default is 20.
+ */
+mxCompactTreeLayout.prototype.nodeDistance = 20;
+
+/**
+ * Variable: resetEdges
+ * 
+ * Specifies if all edge points of traversed edges should be removed.
+ * Default is true.
+ */
+mxCompactTreeLayout.prototype.resetEdges = true;
+
+/**
+ * Variable: prefHozEdgeSep
+ * 
+ * The preferred horizontal distance between edges exiting a vertex.
+ */
+mxCompactTreeLayout.prototype.prefHozEdgeSep = 5;
+
+/**
+ * Variable: prefVertEdgeOff
+ * 
+ * The preferred vertical offset between edges exiting a vertex.
+ */
+mxCompactTreeLayout.prototype.prefVertEdgeOff = 4;
+
+/**
+ * Variable: minEdgeJetty
+ * 
+ * The minimum distance for an edge jetty from a vertex.
+ */
+mxCompactTreeLayout.prototype.minEdgeJetty = 8;
+
+/**
+ * Variable: channelBuffer
+ * 
+ * The size of the vertical buffer in the center of inter-rank channels
+ * where edge control points should not be placed.
+ */
+mxCompactTreeLayout.prototype.channelBuffer = 4;
+
+/**
+ * Variable: edgeRouting
+ * 
+ * Whether or not to apply the internal tree edge routing.
+ */
+mxCompactTreeLayout.prototype.edgeRouting = true;
+
+/**
+ * Variable: sortEdges
+ * 
+ * Specifies if edges should be sorted according to the order of their
+ * opposite terminal cell in the model.
+ */
+mxCompactTreeLayout.prototype.sortEdges = false;
+
+/**
+ * Variable: alignRanks
+ * 
+ * Whether or not the tops of cells in each rank should be aligned
+ * across the rank
+ */
+mxCompactTreeLayout.prototype.alignRanks = false;
+
+/**
+ * Variable: maxRankHeight
+ * 
+ * An array of the maximum height of cells (relative to the layout direction)
+ * per rank
+ */
+mxCompactTreeLayout.prototype.maxRankHeight = null;
+
+/**
+ * Variable: root
+ * 
+ * The cell to use as the root of the tree
+ */
+mxCompactTreeLayout.prototype.root = null;
+
+/**
+ * Variable: node
+ * 
+ * The internal node representation of the root cell. Do not set directly
+ * , this value is only exposed to assist with post-processing functionality
+ */
+mxCompactTreeLayout.prototype.node = null;
+
+/**
+ * Function: isVertexIgnored
+ * 
+ * Returns a boolean indicating if the given <mxCell> should be ignored as a
+ * vertex. This returns true if the cell has no connections.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> whose ignored state should be returned.
+ */
+mxCompactTreeLayout.prototype.isVertexIgnored = function(vertex)
+{
+	return mxGraphLayout.prototype.isVertexIgnored.apply(this, arguments) ||
+		this.graph.getConnections(vertex).length == 0;
+};
+
+/**
+ * Function: isHorizontal
+ * 
+ * Returns <horizontal>.
+ */
+mxCompactTreeLayout.prototype.isHorizontal = function()
+{
+	return this.horizontal;
+};
+
+/**
+ * Function: execute
+ * 
+ * Implements <mxGraphLayout.execute>.
+ * 
+ * If the parent has any connected edges, then it is used as the root of
+ * the tree. Else, <mxGraph.findTreeRoots> will be used to find a suitable
+ * root node within the set of children of the given parent.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose children should be laid out.
+ * root - Optional <mxCell> that will be used as the root of the tree.
+ * Overrides <root> if specified.
+ */
+mxCompactTreeLayout.prototype.execute = function(parent, root)
+{
+	this.parent = parent;
+	var model = this.graph.getModel();
+
+	if (root == null)
+	{
+		// Takes the parent as the root if it has outgoing edges
+		if (this.graph.getEdges(parent, model.getParent(parent),
+			this.invert, !this.invert, false).length > 0)
+		{
+			this.root = parent;
+		}
+		
+		// Tries to find a suitable root in the parent's
+		// children
+		else
+		{
+			var roots = this.graph.findTreeRoots(parent, true, this.invert);
+			
+			if (roots.length > 0)
+			{
+				for (var i = 0; i < roots.length; i++)
+				{
+					if (!this.isVertexIgnored(roots[i]) &&
+						this.graph.getEdges(roots[i], null,
+							this.invert, !this.invert, false).length > 0)
+					{
+						this.root = roots[i];
+						break;
+					}
+				}
+			}
+		}
+	}
+	else
+	{
+		this.root = root;
+	}
+	
+	if (this.root != null)
+	{
+		if (this.resizeParent)
+		{
+			this.parentsChanged = new Object();
+		}
+		else
+		{
+			this.parentsChanged = null;
+		}
+
+		//  Maintaining parent location
+		this.parentX = null;
+		this.parentY = null;
+		
+		if (parent != this.root && model.isVertex(parent) != null && this.maintainParentLocation)
+		{
+			var geo = this.graph.getCellGeometry(parent);
+			
+			if (geo != null)
+			{
+				this.parentX = geo.x;
+				this.parentY = geo.y;
+			}
+		}
+		
+		model.beginUpdate();
+		
+		try
+		{
+			this.visited = new Object();
+			this.node = this.dfs(this.root, parent);
+			
+			if (this.alignRanks)
+			{
+				this.maxRankHeight = [];
+				this.findRankHeights(this.node, 0);
+				this.setCellHeights(this.node, 0);
+			}
+			
+			if (this.node != null)
+			{
+				this.layout(this.node);
+				var x0 = this.graph.gridSize;
+				var y0 = x0;
+				
+				if (!this.moveTree)
+				{
+					var g = this.getVertexBounds(this.root);
+					
+					if (g != null)
+					{
+						x0 = g.x;
+						y0 = g.y;
+					}
+				}
+				
+				var bounds = null;
+				
+				if (this.isHorizontal())
+				{
+					bounds = this.horizontalLayout(this.node, x0, y0);
+				}
+				else
+				{
+					bounds = this.verticalLayout(this.node, null, x0, y0);
+				}
+
+				if (bounds != null)
+				{
+					var dx = 0;
+					var dy = 0;
+
+					if (bounds.x < 0)
+					{
+						dx = Math.abs(x0 - bounds.x);
+					}
+
+					if (bounds.y < 0)
+					{
+						dy = Math.abs(y0 - bounds.y);	
+					}
+
+					if (dx != 0 || dy != 0)
+					{
+						this.moveNode(this.node, dx, dy);
+					}
+					
+					if (this.resizeParent)
+					{
+						this.adjustParents();
+					}
+
+					if (this.edgeRouting)
+					{
+						// Iterate through all edges setting their positions
+						this.localEdgeProcessing(this.node);
+					}
+				}
+				
+				// Maintaining parent location
+				if (this.parentX != null && this.parentY != null)
+				{
+					var geo = this.graph.getCellGeometry(parent);
+					
+					if (geo != null)
+					{
+						geo = geo.clone();
+						geo.x = this.parentX;
+						geo.y = this.parentY;
+						model.setGeometry(parent, geo);
+					}
+				}
+			}
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: moveNode
+ * 
+ * Moves the specified node and all of its children by the given amount.
+ */
+mxCompactTreeLayout.prototype.moveNode = function(node, dx, dy)
+{
+	node.x += dx;
+	node.y += dy;
+	this.apply(node);
+	
+	var child = node.child;
+	
+	while (child != null)
+	{
+		this.moveNode(child, dx, dy);
+		child = child.next;
+	}
+};
+
+
+/**
+ * Function: sortOutgoingEdges
+ * 
+ * Called if <sortEdges> is true to sort the array of outgoing edges in place.
+ */
+mxCompactTreeLayout.prototype.sortOutgoingEdges = function(source, edges)
+{
+	var lookup = new mxDictionary();
+	
+	edges.sort(function(e1, e2)
+	{
+		var end1 = e1.getTerminal(e1.getTerminal(false) == source);
+		var p1 = lookup.get(end1);
+		
+		if (p1 == null)
+		{
+			p1 = mxCellPath.create(end1).split(mxCellPath.PATH_SEPARATOR);
+			lookup.put(end1, p1);
+		}
+
+		var end2 = e2.getTerminal(e2.getTerminal(false) == source);
+		var p2 = lookup.get(end2);
+		
+		if (p2 == null)
+		{
+			p2 = mxCellPath.create(end2).split(mxCellPath.PATH_SEPARATOR);
+			lookup.put(end2, p2);
+		}
+
+		return mxCellPath.compare(p1, p2);
+	});
+};
+
+/**
+ * Function: findRankHeights
+ * 
+ * Stores the maximum height (relative to the layout
+ * direction) of cells in each rank
+ */
+mxCompactTreeLayout.prototype.findRankHeights = function(node, rank)
+{
+	if (this.maxRankHeight[rank] == null || this.maxRankHeight[rank] < node.height)
+	{
+		this.maxRankHeight[rank] = node.height;
+	}
+
+	var child = node.child;
+	
+	while (child != null)
+	{
+		this.findRankHeights(child, rank + 1);
+		child = child.next;
+	}
+};
+
+/**
+ * Function: setCellHeights
+ * 
+ * Set the cells heights (relative to the layout
+ * direction) when the tops of each rank are to be aligned
+ */
+mxCompactTreeLayout.prototype.setCellHeights = function(node, rank)
+{
+	if (this.maxRankHeight[rank] != null && this.maxRankHeight[rank] > node.height)
+	{
+		node.height = this.maxRankHeight[rank];
+	}
+
+	var child = node.child;
+	
+	while (child != null)
+	{
+		this.setCellHeights(child, rank + 1);
+		child = child.next;
+	}
+};
+
+/**
+ * Function: dfs
+ * 
+ * Does a depth first search starting at the specified cell.
+ * Makes sure the specified parent is never left by the
+ * algorithm.
+ */
+mxCompactTreeLayout.prototype.dfs = function(cell, parent)
+{
+	var id = mxCellPath.create(cell);
+	var node = null;
+	
+	if (cell != null && this.visited[id] == null && !this.isVertexIgnored(cell))
+	{
+		this.visited[id] = cell;
+		node = this.createNode(cell);
+
+		var model = this.graph.getModel();
+		var prev = null;
+		var out = this.graph.getEdges(cell, parent, this.invert, !this.invert, false, true);
+		var view = this.graph.getView();
+		
+		if (this.sortEdges)
+		{
+			this.sortOutgoingEdges(cell, out);
+		}
+
+		for (var i = 0; i < out.length; i++)
+		{
+			var edge = out[i];
+			
+			if (!this.isEdgeIgnored(edge))
+			{
+				// Resets the points on the traversed edge
+				if (this.resetEdges)
+				{
+					this.setEdgePoints(edge, null);
+				}
+				
+				if (this.edgeRouting)
+				{
+					this.setEdgeStyleEnabled(edge, false);
+					this.setEdgePoints(edge, null);
+				}
+				
+				// Checks if terminal in same swimlane
+				var state = view.getState(edge);
+				var target = (state != null) ? state.getVisibleTerminal(this.invert) : view.getVisibleTerminal(edge, this.invert);
+				var tmp = this.dfs(target, parent);
+				
+				if (tmp != null && model.getGeometry(target) != null)
+				{
+					if (prev == null)
+					{
+						node.child = tmp;
+					}
+					else
+					{
+						prev.next = tmp;
+					}
+					
+					prev = tmp;
+				}
+			}
+		}
+	}
+	
+	return node;
+};
+
+/**
+ * Function: layout
+ * 
+ * Starts the actual compact tree layout algorithm
+ * at the given node.
+ */
+mxCompactTreeLayout.prototype.layout = function(node)
+{
+	if (node != null)
+	{
+		var child = node.child;
+		
+		while (child != null)
+		{
+			this.layout(child);
+			child = child.next;
+		}
+		
+		if (node.child != null)
+		{
+			this.attachParent(node, this.join(node));
+		}
+		else
+		{
+			this.layoutLeaf(node);
+		}
+	}
+};
+
+/**
+ * Function: horizontalLayout
+ */
+mxCompactTreeLayout.prototype.horizontalLayout = function(node, x0, y0, bounds)
+{
+	node.x += x0 + node.offsetX;
+	node.y += y0 + node.offsetY;
+	bounds = this.apply(node, bounds);
+	var child = node.child;
+	
+	if (child != null)
+	{
+		bounds = this.horizontalLayout(child, node.x, node.y, bounds);
+		var siblingOffset = node.y + child.offsetY;
+		var s = child.next;
+		
+		while (s != null)
+		{
+			bounds = this.horizontalLayout(s, node.x + child.offsetX, siblingOffset, bounds);
+			siblingOffset += s.offsetY;
+			s = s.next;
+		}
+	}
+	
+	return bounds;
+};
+	
+/**
+ * Function: verticalLayout
+ */
+mxCompactTreeLayout.prototype.verticalLayout = function(node, parent, x0, y0, bounds)
+{
+	node.x += x0 + node.offsetY;
+	node.y += y0 + node.offsetX;
+	bounds = this.apply(node, bounds);
+	var child = node.child;
+	
+	if (child != null)
+	{
+		bounds = this.verticalLayout(child, node, node.x, node.y, bounds);
+		var siblingOffset = node.x + child.offsetY;
+		var s = child.next;
+		
+		while (s != null)
+		{
+			bounds = this.verticalLayout(s, node, siblingOffset, node.y + child.offsetX, bounds);
+			siblingOffset += s.offsetY;
+			s = s.next;
+		}
+	}
+	
+	return bounds;
+};
+
+/**
+ * Function: attachParent
+ */
+mxCompactTreeLayout.prototype.attachParent = function(node, height)
+{
+	var x = this.nodeDistance + this.levelDistance;
+	var y2 = (height - node.width) / 2 - this.nodeDistance;
+	var 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));
+};
+
+/**
+ * Function: layoutLeaf
+ */
+mxCompactTreeLayout.prototype.layoutLeaf = function(node)
+{
+	var dist = 2 * this.nodeDistance;
+	
+	node.contour.upperTail = this.createLine(
+		node.height + dist, 0);
+	node.contour.upperHead = node.contour.upperTail;
+	node.contour.lowerTail = this.createLine(
+		0, -node.width - dist);
+	node.contour.lowerHead = this.createLine(
+		node.height + dist, 0, node.contour.lowerTail);
+};
+
+/**
+ * Function: join
+ */
+mxCompactTreeLayout.prototype.join = function(node)
+{
+	var dist = 2 * this.nodeDistance;
+	
+	var child = node.child;
+	node.contour = child.contour;
+	var h = child.width + dist;
+	var sum = h;
+	child = child.next;
+	
+	while (child != null)
+	{
+		var d = this.merge(node.contour, child.contour);
+		child.offsetY = d + h;
+		child.offsetX = 0;
+		h = child.width + dist;
+		sum += d + h;
+		child = child.next;
+	}
+	
+	return sum;
+};
+
+/**
+ * Function: merge
+ */
+mxCompactTreeLayout.prototype.merge = function(p1, p2)
+{
+	var x = 0;
+	var y = 0;
+	var total = 0;
+	
+	var upper = p1.lowerHead;
+	var lower = p2.upperHead;
+	
+	while (lower != null && upper != null)
+	{
+		var d = this.offset(x, y, lower.dx, lower.dy,
+			upper.dx, upper.dy);
+		y += d;
+		total += d;
+		
+		if (x + lower.dx <= upper.dx)
+		{
+			x += lower.dx;
+			y += lower.dy;
+			lower = lower.next;
+		}
+		else
+		{				
+			x -= upper.dx;
+			y -= upper.dy;
+			upper = upper.next;
+		}
+	}
+	
+	if (lower != null)
+	{
+		var b = this.bridge(p1.upperTail, 0, 0, lower, x, y);
+		p1.upperTail = (b.next != null) ? p2.upperTail : b;
+		p1.lowerTail = p2.lowerTail;
+	}
+	else
+	{
+		var b = this.bridge(p2.lowerTail, x, y, upper, 0, 0);
+		
+		if (b.next == null)
+		{
+			p1.lowerTail = b;
+		}
+	}
+	
+	p1.lowerHead = p2.lowerHead;
+	
+	return total;
+};
+
+/**
+ * Function: offset
+ */
+mxCompactTreeLayout.prototype.offset = function(p1, p2, a1, a2, b1, b2)
+{
+	var d = 0;
+	
+	if (b1 <= p1 || p1 + a1 <= 0)
+	{
+		return 0;
+	}
+
+	var t = b1 * a2 - a1 * b2;
+	
+	if (t > 0)
+	{
+		if (p1 < 0)
+		{
+			var s = p1 * a2;
+			d = s / a1 - p2;
+		}
+		else if (p1 > 0)
+		{
+			var s = p1 * b2;
+			d = s / b1 - p2;
+		}
+		else
+		{
+			d = -p2;
+		}
+	}
+	else if (b1 < p1 + a1)
+	{
+		var s = (b1 - p1) * a2;
+		d = b2 - (p2 + s / a1);
+	}
+	else if (b1 > p1 + a1)
+	{
+		var s = (a1 + p1) * b2;
+		d = s / b1 - (p2 + a2);
+	}
+	else
+	{
+		d = b2 - (p2 + a2);
+	}
+
+	if (d > 0)
+	{
+		return d;
+	}
+	else
+	{
+		return 0;
+	}
+};
+
+/**
+ * Function: bridge
+ */
+mxCompactTreeLayout.prototype.bridge = function(line1, x1, y1, line2, x2, y2)
+{
+	var dx = x2 + line2.dx - x1;
+	var dy = 0;
+	var s = 0;
+	
+	if (line2.dx == 0)
+	{
+		dy = line2.dy;
+	}
+	else
+	{
+		s = dx * line2.dy;
+		dy = s / line2.dx;
+	}
+	
+	var r = this.createLine(dx, dy, line2.next);
+	line1.next = this.createLine(0, y2 + line2.dy - dy - y1, r);
+	
+	return r;
+};
+
+/**
+ * Function: createNode
+ */
+mxCompactTreeLayout.prototype.createNode = function(cell)
+{
+	var node = new Object();
+	node.cell = cell;
+	node.x = 0;
+	node.y = 0;
+	node.width = 0;
+	node.height = 0;
+	
+	var geo = this.getVertexBounds(cell);
+	
+	if (geo != null)
+	{
+		if (this.isHorizontal())
+		{
+			node.width = geo.height;
+			node.height = geo.width;			
+		}
+		else
+		{
+			node.width = geo.width;
+			node.height = geo.height;
+		}
+	}
+	
+	node.offsetX = 0;
+	node.offsetY = 0;
+	node.contour = new Object();
+	
+	return node;
+};
+
+/**
+ * Function: apply
+ */
+mxCompactTreeLayout.prototype.apply = function(node, bounds)
+{
+	var model = this.graph.getModel();
+	var cell = node.cell;
+	var g = model.getGeometry(cell);
+
+	if (cell != null && g != null)
+	{
+		if (this.isVertexMovable(cell))
+		{
+			g = this.setVertexLocation(cell, node.x, node.y);
+			
+			if (this.resizeParent)
+			{
+				var parent = model.getParent(cell);
+				var id = mxCellPath.create(parent);
+				
+				// Implements set semantic
+				if (this.parentsChanged[id] == null)
+				{
+					this.parentsChanged[id] = parent;					
+				}
+			}
+		}
+		
+		if (bounds == null)
+		{
+			bounds = new mxRectangle(g.x, g.y, g.width, g.height);
+		}
+		else
+		{
+			bounds = new mxRectangle(Math.min(bounds.x, g.x),
+				Math.min(bounds.y, g.y),
+				Math.max(bounds.x + bounds.width, g.x + g.width),
+				Math.max(bounds.y + bounds.height, g.y + g.height));
+		}
+	}
+	
+	return bounds;
+};
+
+/**
+ * Function: createLine
+ */
+mxCompactTreeLayout.prototype.createLine = function(dx, dy, next)
+{
+	var line = new Object();
+	line.dx = dx;
+	line.dy = dy;
+	line.next = next;
+	
+	return line;
+};
+
+/**
+ * Function: adjustParents
+ * 
+ * Adjust parent cells whose child geometries have changed. The default 
+ * implementation adjusts the group to just fit around the children with 
+ * a padding.
+ */
+mxCompactTreeLayout.prototype.adjustParents = function()
+{
+	var tmp = [];
+	
+	for (var id in this.parentsChanged)
+	{
+		tmp.push(this.parentsChanged[id]);
+	}
+	
+	this.arrangeGroups(mxUtils.sortCells(tmp, true), this.groupPadding, this.groupPaddingTop,
+		this.groupPaddingRight, this.groupPaddingBottom, this.groupPaddingLeft);
+};
+
+/**
+ * Function: localEdgeProcessing
+ *
+ * Moves the specified node and all of its children by the given amount.
+ */
+mxCompactTreeLayout.prototype.localEdgeProcessing = function(node)
+{
+	this.processNodeOutgoing(node);
+	var child = node.child;
+
+	while (child != null)
+	{
+		this.localEdgeProcessing(child);
+		child = child.next;
+	}
+};
+
+/**
+ * Function: localEdgeProcessing
+ *
+ * Separates the x position of edges as they connect to vertices
+ */
+mxCompactTreeLayout.prototype.processNodeOutgoing = function(node)
+{
+	var child = node.child;
+	var parentCell = node.cell;
+
+	var childCount = 0;
+	var sortedCells = [];
+
+	while (child != null)
+	{
+		childCount++;
+
+		var sortingCriterion = child.x;
+
+		if (this.horizontal)
+		{
+			sortingCriterion = child.y;
+		}
+
+		sortedCells.push(new WeightedCellSorter(child, sortingCriterion));
+		child = child.next;
+	}
+
+	sortedCells.sort(WeightedCellSorter.prototype.compare);
+
+	var availableWidth = node.width;
+
+	var requiredWidth = (childCount + 1) * this.prefHozEdgeSep;
+
+	// Add a buffer on the edges of the vertex if the edge count allows
+	if (availableWidth > requiredWidth + (2 * this.prefHozEdgeSep))
+	{
+		availableWidth -= 2 * this.prefHozEdgeSep;
+	}
+
+	var edgeSpacing = availableWidth / childCount;
+
+	var currentXOffset = edgeSpacing / 2.0;
+
+	if (availableWidth > requiredWidth + (2 * this.prefHozEdgeSep))
+	{
+		currentXOffset += this.prefHozEdgeSep;
+	}
+
+	var currentYOffset = this.minEdgeJetty - this.prefVertEdgeOff;
+	var maxYOffset = 0;
+
+	var parentBounds = this.getVertexBounds(parentCell);
+	child = node.child;
+
+	for (var j = 0; j < sortedCells.length; j++)
+	{
+		var childCell = sortedCells[j].cell.cell;
+		var childBounds = this.getVertexBounds(childCell);
+
+		var edges = this.graph.getEdgesBetween(parentCell,
+				childCell, false);
+		
+		var newPoints = [];
+		var x = 0;
+		var y = 0;
+
+		for (var i = 0; i < edges.length; i++)
+		{
+			if (this.horizontal)
+			{
+				// Use opposite co-ords, calculation was done for 
+				// 
+				x = parentBounds.x + parentBounds.width;
+				y = parentBounds.y + currentXOffset;
+				newPoints.push(new mxPoint(x, y));
+				x = parentBounds.x + parentBounds.width
+						+ currentYOffset;
+				newPoints.push(new mxPoint(x, y));
+				y = childBounds.y + childBounds.height / 2.0;
+				newPoints.push(new mxPoint(x, y));
+				this.setEdgePoints(edges[i], newPoints);
+			}
+			else
+			{
+				x = parentBounds.x + currentXOffset;
+				y = parentBounds.y + parentBounds.height;
+				newPoints.push(new mxPoint(x, y));
+				y = parentBounds.y + parentBounds.height
+						+ currentYOffset;
+				newPoints.push(new mxPoint(x, y));
+				x = childBounds.x + childBounds.width / 2.0;
+				newPoints.push(new mxPoint(x, y));
+				this.setEdgePoints(edges[i], newPoints);
+			}
+		}
+
+		if (j < childCount / 2)
+		{
+			currentYOffset += this.prefVertEdgeOff;
+		}
+		else if (j > childCount / 2)
+		{
+			currentYOffset -= this.prefVertEdgeOff;
+		}
+		// Ignore the case if equals, this means the second of 2
+		// jettys with the same y (even number of edges)
+
+		//								pos[k * 2] = currentX;
+		currentXOffset += edgeSpacing;
+		//								pos[k * 2 + 1] = currentYOffset;
+
+		maxYOffset = Math.max(maxYOffset, currentYOffset);
+	}
+};
+
+/**
+ * Class: WeightedCellSorter
+ * 
+ * A utility class used to track cells whilst sorting occurs on the weighted
+ * sum of their connected edges. Does not violate (x.compareTo(y)==0) ==
+ * (x.equals(y))
+ *
+ * Constructor: WeightedCellSorter
+ * 
+ * Constructs a new weighted cell sorted for the given cell and weight.
+ */
+function WeightedCellSorter(cell, weightedValue)
+{
+	this.cell = cell;
+	this.weightedValue = weightedValue;
+};
+
+/**
+ * Variable: weightedValue
+ * 
+ * The weighted value of the cell stored.
+ */
+WeightedCellSorter.prototype.weightedValue = 0;
+
+/**
+ * Variable: nudge
+ * 
+ * Whether or not to flip equal weight values.
+ */
+WeightedCellSorter.prototype.nudge = false;
+
+/**
+ * Variable: visited
+ * 
+ * Whether or not this cell has been visited in the current assignment.
+ */
+WeightedCellSorter.prototype.visited = false;
+
+/**
+ * Variable: rankIndex
+ * 
+ * The index this cell is in the model rank.
+ */
+WeightedCellSorter.prototype.rankIndex = null;
+
+/**
+ * Variable: cell
+ * 
+ * The cell whose median value is being calculated.
+ */
+WeightedCellSorter.prototype.cell = null;
+
+/**
+ * Function: compare
+ * 
+ * Compares two WeightedCellSorters.
+ */
+WeightedCellSorter.prototype.compare = function(a, b)
+{
+	if (a != null && b != null)
+	{
+		if (b.weightedValue > a.weightedValue)
+		{
+			return 1;
+		}
+		else if (b.weightedValue < a.weightedValue)
+		{
+			return -1;
+		}
+		else
+		{
+			if (b.nudge)
+			{
+				return 1;
+			}
+			else
+			{
+				return -1;
+			}
+		}
+	}
+	else
+	{
+		return 0;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxRadialTreeLayout
+ * 
+ * Extends <mxGraphLayout> to implement a radial tree algorithm. This
+ * layout is suitable for graphs that have no cycles (trees). Vertices that are
+ * not connected to the tree will be ignored by this layout.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var layout = new mxRadialTreeLayout(graph);
+ * layout.execute(graph.getDefaultParent());
+ * (end)
+ * 
+ * Constructor: mxRadialTreeLayout
+ * 
+ * Constructs a new radial tree layout for the specified graph
+ */
+function mxRadialTreeLayout(graph)
+{
+	mxCompactTreeLayout.call(this, graph , false);
+};
+
+/**
+ * Extends mxGraphLayout.
+ */
+mxUtils.extend(mxRadialTreeLayout, mxCompactTreeLayout);
+
+/**
+ * Variable: angleOffset
+ *
+ * The initial offset to compute the angle position.
+ */
+mxRadialTreeLayout.prototype.angleOffset = 0.5;
+
+/**
+ * Variable: rootx
+ *
+ * The X co-ordinate of the root cell
+ */
+mxRadialTreeLayout.prototype.rootx = 0;
+
+/**
+ * Variable: rooty
+ *
+ * The Y co-ordinate of the root cell
+ */
+mxRadialTreeLayout.prototype.rooty = 0;
+
+/**
+ * Variable: levelDistance
+ *
+ * Holds the levelDistance. Default is 120.
+ */
+mxRadialTreeLayout.prototype.levelDistance = 120;
+
+/**
+ * Variable: nodeDistance
+ *
+ * Holds the nodeDistance. Default is 10.
+ */
+mxRadialTreeLayout.prototype.nodeDistance = 10;
+
+/**
+ * Variable: autoRadius
+ * 
+ * Specifies if the radios should be computed automatically
+ */
+mxRadialTreeLayout.prototype.autoRadius = false;
+
+/**
+ * Variable: sortEdges
+ * 
+ * Specifies if edges should be sorted according to the order of their
+ * opposite terminal cell in the model.
+ */
+mxRadialTreeLayout.prototype.sortEdges = false;
+
+/**
+ * Variable: rowMinX
+ * 
+ * Array of leftmost x coordinate of each row
+ */
+mxRadialTreeLayout.prototype.rowMinX = [];
+
+/**
+ * Variable: rowMaxX
+ * 
+ * Array of rightmost x coordinate of each row
+ */
+mxRadialTreeLayout.prototype.rowMaxX = [];
+
+/**
+ * Variable: rowMinCenX
+ * 
+ * Array of x coordinate of leftmost vertex of each row
+ */
+mxRadialTreeLayout.prototype.rowMinCenX = [];
+
+/**
+ * Variable: rowMaxCenX
+ * 
+ * Array of x coordinate of rightmost vertex of each row
+ */
+mxRadialTreeLayout.prototype.rowMaxCenX = [];
+
+/**
+ * Variable: rowRadi
+ * 
+ * Array of y deltas of each row behind root vertex, also the radius in the tree
+ */
+mxRadialTreeLayout.prototype.rowRadi = [];
+
+/**
+ * Variable: row
+ * 
+ * Array of vertices on each row
+ */
+mxRadialTreeLayout.prototype.row = [];
+
+/**
+ * Function: isVertexIgnored
+ * 
+ * Returns a boolean indicating if the given <mxCell> should be ignored as a
+ * vertex. This returns true if the cell has no connections.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> whose ignored state should be returned.
+ */
+mxRadialTreeLayout.prototype.isVertexIgnored = function(vertex)
+{
+	return mxGraphLayout.prototype.isVertexIgnored.apply(this, arguments) ||
+		this.graph.getConnections(vertex).length == 0;
+};
+
+/**
+ * Function: execute
+ * 
+ * Implements <mxGraphLayout.execute>.
+ * 
+ * If the parent has any connected edges, then it is used as the root of
+ * the tree. Else, <mxGraph.findTreeRoots> will be used to find a suitable
+ * root node within the set of children of the given parent.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose children should be laid out.
+ * root - Optional <mxCell> that will be used as the root of the tree.
+ */
+mxRadialTreeLayout.prototype.execute = function(parent, root)
+{
+	this.parent = parent;
+	
+	this.useBoundingBox = false;
+	this.edgeRouting = false;
+	//this.horizontal = false;
+
+	mxCompactTreeLayout.prototype.execute.apply(this, arguments);
+	
+	var bounds = null;
+	var rootBounds = this.getVertexBounds(this.root);
+	this.centerX = rootBounds.x + rootBounds.width / 2;
+	this.centerY = rootBounds.y + rootBounds.height / 2;
+
+	// Calculate the bounds of the involved vertices directly from the values set in the compact tree
+	for (var vertex in this.visited)
+	{
+		var vertexBounds = this.getVertexBounds(this.visited[vertex]);
+		bounds = (bounds != null) ? bounds : vertexBounds.clone();
+		bounds.add(vertexBounds);
+	}
+	
+	this.calcRowDims([this.node], 0);
+	
+	var maxLeftGrad = 0;
+	var maxRightGrad = 0;
+
+	// Find the steepest left and right gradients
+	for (var i = 0; i < this.row.length; i++)
+	{
+		var leftGrad = (this.centerX - this.rowMinX[i] - this.nodeDistance) / this.rowRadi[i];
+		var rightGrad = (this.rowMaxX[i] - this.centerX - this.nodeDistance) / this.rowRadi[i];
+		
+		maxLeftGrad = Math.max (maxLeftGrad, leftGrad);
+		maxRightGrad = Math.max (maxRightGrad, rightGrad);
+	}
+	
+	// Extend out row so they meet the maximum gradient and convert to polar co-ords
+	for (var i = 0; i < this.row.length; i++)
+	{
+		var xLeftLimit = this.centerX - this.nodeDistance - maxLeftGrad * this.rowRadi[i];
+		var xRightLimit = this.centerX + this.nodeDistance + maxRightGrad * this.rowRadi[i];
+		var fullWidth = xRightLimit - xLeftLimit;
+		
+		for (var j = 0; j < this.row[i].length; j ++)
+		{
+			var row = this.row[i];
+			var node = row[j];
+			var vertexBounds = this.getVertexBounds(node.cell);
+			var xProportion = (vertexBounds.x + vertexBounds.width / 2 - xLeftLimit) / (fullWidth);
+			var theta =  2 * Math.PI * xProportion;
+			node.theta = theta;
+		}
+	}
+
+	// Post-process from outside inwards to try to align parents with children
+	for (var i = this.row.length - 2; i >= 0; i--)
+	{
+		var row = this.row[i];
+		
+		for (var j = 0; j < row.length; j++)
+		{
+			var node = row[j];
+			var child = node.child;
+			var counter = 0;
+			var totalTheta = 0;
+			
+			while (child != null)
+			{
+				totalTheta += child.theta;
+				counter++;
+				child = child.next;
+			}
+			
+			if (counter > 0)
+			{
+				var averTheta = totalTheta / counter;
+				
+				if (averTheta > node.theta && j < row.length - 1)
+				{
+					var nextTheta = row[j+1].theta;
+					node.theta = Math.min (averTheta, nextTheta - Math.PI/10);
+				}
+				else if (averTheta < node.theta && j > 0 )
+				{
+					var lastTheta = row[j-1].theta;
+					node.theta = Math.max (averTheta, lastTheta + Math.PI/10);
+				}
+			}
+		}
+	}
+	
+	// Set locations
+	for (var i = 0; i < this.row.length; i++)
+	{
+		for (var j = 0; j < this.row[i].length; j ++)
+		{
+			var row = this.row[i];
+			var node = row[j];
+			var vertexBounds = this.getVertexBounds(node.cell);
+			this.setVertexLocation(node.cell,
+									this.centerX - vertexBounds.width / 2 + this.rowRadi[i] * Math.cos(node.theta),
+									this.centerY - vertexBounds.height / 2 + this.rowRadi[i] * Math.sin(node.theta));
+		}
+	}
+};
+
+/**
+ * Function: calcRowDims
+ * 
+ * Recursive function to calculate the dimensions of each row
+ * 
+ * Parameters:
+ * 
+ * row - Array of internal nodes, the children of which are to be processed.
+ * rowNum - Integer indicating which row is being processed.
+ */
+mxRadialTreeLayout.prototype.calcRowDims = function(row, rowNum)
+{
+	if (row == null || row.length == 0)
+	{
+		return;
+	}
+
+	// Place root's children proportionally around the first level
+	this.rowMinX[rowNum] = this.centerX;
+	this.rowMaxX[rowNum] = this.centerX;
+	this.rowMinCenX[rowNum] = this.centerX;
+	this.rowMaxCenX[rowNum] = this.centerX;
+	this.row[rowNum] = [];
+
+	var rowHasChildren = false;
+
+	for (var i = 0; i < row.length; i++)
+	{
+		var child = row[i] != null ? row[i].child : null;
+
+		while (child != null)
+		{
+			var cell = child.cell;
+			vertexBounds = this.getVertexBounds(cell);
+			
+			this.rowMinX[rowNum] = Math.min(vertexBounds.x, this.rowMinX[rowNum]);
+			this.rowMaxX[rowNum] = Math.max(vertexBounds.x + vertexBounds.width, this.rowMaxX[rowNum]);
+			this.rowMinCenX[rowNum] = Math.min(vertexBounds.x + vertexBounds.width / 2, this.rowMinCenX[rowNum]);
+			this.rowMaxCenX[rowNum] = Math.max(vertexBounds.x + vertexBounds.width / 2, this.rowMaxCenX[rowNum]);
+			this.rowRadi[rowNum] = vertexBounds.y - this.getVertexBounds(this.root).y;
+	
+			if (child.child != null)
+			{
+				rowHasChildren = true;
+			}
+			this.row[rowNum].push(child);
+			child = child.next;
+		}
+	}
+	
+	if (rowHasChildren)
+	{
+		this.calcRowDims(this.row[rowNum], rowNum + 1);
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxFastOrganicLayout
+ * 
+ * Extends <mxGraphLayout> to implement a fast organic layout algorithm.
+ * The vertices need to be connected for this layout to work, vertices
+ * with no connections are ignored.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var layout = new mxFastOrganicLayout(graph);
+ * layout.execute(graph.getDefaultParent());
+ * (end)
+ * 
+ * Constructor: mxCompactTreeLayout
+ * 
+ * Constructs a new fast organic layout for the specified graph.
+ */
+function mxFastOrganicLayout(graph)
+{
+	mxGraphLayout.call(this, graph);
+};
+
+/**
+ * Extends mxGraphLayout.
+ */
+mxFastOrganicLayout.prototype = new mxGraphLayout();
+mxFastOrganicLayout.prototype.constructor = mxFastOrganicLayout;
+
+/**
+ * Variable: useInputOrigin
+ * 
+ * Specifies if the top left corner of the input cells should be the origin
+ * of the layout result. Default is true.
+ */
+mxFastOrganicLayout.prototype.useInputOrigin = true;
+
+/**
+ * Variable: resetEdges
+ * 
+ * Specifies if all edge points of traversed edges should be removed.
+ * Default is true.
+ */
+mxFastOrganicLayout.prototype.resetEdges = true;
+
+/**
+ * Variable: disableEdgeStyle
+ * 
+ * Specifies if the STYLE_NOEDGESTYLE flag should be set on edges that are
+ * modified by the result. Default is true.
+ */
+mxFastOrganicLayout.prototype.disableEdgeStyle = true;
+
+/**
+ * Variable: forceConstant
+ * 
+ * The force constant by which the attractive forces are divided and the
+ * replusive forces are multiple by the square of. The value equates to the
+ * average radius there is of free space around each node. Default is 50.
+ */
+mxFastOrganicLayout.prototype.forceConstant = 50;
+
+/**
+ * Variable: forceConstantSquared
+ * 
+ * Cache of <forceConstant>^2 for performance.
+ */
+mxFastOrganicLayout.prototype.forceConstantSquared = 0;
+
+/**
+ * Variable: minDistanceLimit
+ * 
+ * Minimal distance limit. Default is 2. Prevents of
+ * dividing by zero.
+ */
+mxFastOrganicLayout.prototype.minDistanceLimit = 2;
+
+/**
+ * Variable: minDistanceLimit
+ * 
+ * Minimal distance limit. Default is 2. Prevents of
+ * dividing by zero.
+ */
+mxFastOrganicLayout.prototype.maxDistanceLimit = 500;
+
+/**
+ * Variable: minDistanceLimitSquared
+ * 
+ * Cached version of <minDistanceLimit> squared.
+ */
+mxFastOrganicLayout.prototype.minDistanceLimitSquared = 4;
+
+/**
+ * Variable: initialTemp
+ * 
+ * Start value of temperature. Default is 200.
+ */
+mxFastOrganicLayout.prototype.initialTemp = 200;
+
+/**
+ * Variable: temperature
+ * 
+ * Temperature to limit displacement at later stages of layout.
+ */
+mxFastOrganicLayout.prototype.temperature = 0;
+
+/**
+ * Variable: maxIterations
+ * 
+ * Total number of iterations to run the layout though.
+ */
+mxFastOrganicLayout.prototype.maxIterations = 0;
+
+/**
+ * Variable: iteration
+ * 
+ * Current iteration count.
+ */
+mxFastOrganicLayout.prototype.iteration = 0;
+
+/**
+ * Variable: vertexArray
+ * 
+ * An array of all vertices to be laid out.
+ */
+mxFastOrganicLayout.prototype.vertexArray;
+
+/**
+ * Variable: dispX
+ * 
+ * An array of locally stored X co-ordinate displacements for the vertices.
+ */
+mxFastOrganicLayout.prototype.dispX;
+
+/**
+ * Variable: dispY
+ * 
+ * An array of locally stored Y co-ordinate displacements for the vertices.
+ */
+mxFastOrganicLayout.prototype.dispY;
+
+/**
+ * Variable: cellLocation
+ * 
+ * An array of locally stored co-ordinate positions for the vertices.
+ */
+mxFastOrganicLayout.prototype.cellLocation;
+
+/**
+ * Variable: radius
+ * 
+ * The approximate radius of each cell, nodes only.
+ */
+mxFastOrganicLayout.prototype.radius;
+
+/**
+ * Variable: radiusSquared
+ * 
+ * The approximate radius squared of each cell, nodes only.
+ */
+mxFastOrganicLayout.prototype.radiusSquared;
+
+/**
+ * Variable: isMoveable
+ * 
+ * Array of booleans representing the movable states of the vertices.
+ */
+mxFastOrganicLayout.prototype.isMoveable;
+
+/**
+ * Variable: neighbours
+ * 
+ * Local copy of cell neighbours.
+ */
+mxFastOrganicLayout.prototype.neighbours;
+
+/**
+ * Variable: indices
+ * 
+ * Hashtable from cells to local indices.
+ */
+mxFastOrganicLayout.prototype.indices;
+
+/**
+ * Variable: allowedToRun
+ * 
+ * Boolean flag that specifies if the layout is allowed to run. If this is
+ * set to false, then the layout exits in the following iteration.
+ */
+mxFastOrganicLayout.prototype.allowedToRun = true;
+
+/**
+ * Function: isVertexIgnored
+ * 
+ * Returns a boolean indicating if the given <mxCell> should be ignored as a
+ * vertex. This returns true if the cell has no connections.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> whose ignored state should be returned.
+ */
+mxFastOrganicLayout.prototype.isVertexIgnored = function(vertex)
+{
+	return mxGraphLayout.prototype.isVertexIgnored.apply(this, arguments) ||
+		this.graph.getConnections(vertex).length == 0;
+};
+
+/**
+ * Function: execute
+ * 
+ * Implements <mxGraphLayout.execute>. This operates on all children of the
+ * given parent where <isVertexIgnored> returns false.
+ */
+mxFastOrganicLayout.prototype.execute = function(parent)
+{
+	var model = this.graph.getModel();
+	this.vertexArray = [];
+	var cells = this.graph.getChildVertices(parent);
+	
+	for (var i = 0; i < cells.length; i++)
+	{
+		if (!this.isVertexIgnored(cells[i]))
+		{
+			this.vertexArray.push(cells[i]);
+		}
+	}
+	
+	var initialBounds = (this.useInputOrigin) ?
+			this.graph.getBoundingBoxFromGeometry(this.vertexArray) :
+				null;
+	var n = this.vertexArray.length;
+
+	this.indices = [];
+	this.dispX = [];
+	this.dispY = [];
+	this.cellLocation = [];
+	this.isMoveable = [];
+	this.neighbours = [];
+	this.radius = [];
+	this.radiusSquared = [];
+
+	if (this.forceConstant < 0.001)
+	{
+		this.forceConstant = 0.001;
+	}
+
+	this.forceConstantSquared = this.forceConstant * this.forceConstant;
+
+	// Create a map of vertices first. This is required for the array of
+	// arrays called neighbours which holds, for each vertex, a list of
+	// ints which represents the neighbours cells to that vertex as
+	// the indices into vertexArray
+	for (var i = 0; i < this.vertexArray.length; i++)
+	{
+		var vertex = this.vertexArray[i];
+		this.cellLocation[i] = [];
+		
+		// Set up the mapping from array indices to cells
+		var id = mxObjectIdentity.get(vertex);
+		this.indices[id] = i;
+		var bounds = this.getVertexBounds(vertex);
+
+		// Set the X,Y value of the internal version of the cell to
+		// the center point of the vertex for better positioning
+		var width = bounds.width;
+		var height = bounds.height;
+		
+		// Randomize (0, 0) locations
+		var x = bounds.x;
+		var y = bounds.y;
+		
+		this.cellLocation[i][0] = x + width / 2.0;
+		this.cellLocation[i][1] = y + height / 2.0;
+		this.radius[i] = Math.min(width, height);
+		this.radiusSquared[i] = this.radius[i] * this.radius[i];
+	}
+
+	// Moves cell location back to top-left from center locations used in
+	// algorithm, resetting the edge points is part of the transaction
+	model.beginUpdate();
+	try
+	{
+		for (var i = 0; i < n; i++)
+		{
+			this.dispX[i] = 0;
+			this.dispY[i] = 0;
+			this.isMoveable[i] = this.isVertexMovable(this.vertexArray[i]);
+
+			// Get lists of neighbours to all vertices, translate the cells
+			// obtained in indices into vertexArray and store as an array
+			// against the orginial cell index
+			var edges = this.graph.getConnections(this.vertexArray[i], parent);
+			var cells = this.graph.getOpposites(edges, this.vertexArray[i]);
+			this.neighbours[i] = [];
+
+			for (var j = 0; j < cells.length; j++)
+			{
+				// Resets the points on the traversed edge
+				if (this.resetEdges)
+				{
+					this.graph.resetEdge(edges[j]);
+				}
+
+			    if (this.disableEdgeStyle)
+			    {
+			    	this.setEdgeStyleEnabled(edges[j], false);
+			    }
+
+				// Looks the cell up in the indices dictionary
+				var id = mxObjectIdentity.get(cells[j]);
+				var index = this.indices[id];
+
+				// Check the connected cell in part of the vertex list to be
+				// acted on by this layout
+				if (index != null)
+				{
+					this.neighbours[i][j] = index;
+				}
+
+				// Else if index of the other cell doesn't correspond to
+				// any cell listed to be acted upon in this layout. Set
+				// the index to the value of this vertex (a dummy self-loop)
+				// so the attraction force of the edge is not calculated
+				else
+				{
+					this.neighbours[i][j] = i;
+				}
+			}
+		}
+		this.temperature = this.initialTemp;
+
+		// If max number of iterations has not been set, guess it
+		if (this.maxIterations == 0)
+		{
+			this.maxIterations = 20 * Math.sqrt(n);
+		}
+		
+		// Main iteration loop
+		for (this.iteration = 0; this.iteration < this.maxIterations; this.iteration++)
+		{
+			if (!this.allowedToRun)
+			{
+				return;
+			}
+			
+			// Calculate repulsive forces on all vertices
+			this.calcRepulsion();
+
+			// Calculate attractive forces through edges
+			this.calcAttraction();
+
+			this.calcPositions();
+			this.reduceTemperature();
+		}
+
+		var minx = null;
+		var miny = null;
+		
+		for (var i = 0; i < this.vertexArray.length; i++)
+		{
+			var vertex = this.vertexArray[i];
+			
+			if (this.isVertexMovable(vertex))
+			{
+				var bounds = this.getVertexBounds(vertex);
+				
+				if (bounds != null)
+				{
+					this.cellLocation[i][0] -= bounds.width / 2.0;
+					this.cellLocation[i][1] -= bounds.height / 2.0;
+					
+					var x = this.graph.snap(this.cellLocation[i][0]);
+					var y = this.graph.snap(this.cellLocation[i][1]);
+					
+					this.setVertexLocation(vertex, x, y);
+					
+					if (minx == null)
+					{
+						minx = x;
+					}
+					else
+					{
+						minx = Math.min(minx, x);
+					}
+					
+					if (miny == null)
+					{
+						miny = y;
+					}
+					else
+					{
+						miny = Math.min(miny, y);
+					}
+				}
+			}
+		}
+		
+		// Modifies the cloned geometries in-place. Not needed
+		// to clone the geometries again as we're in the same
+		// undoable change.
+		var dx = -(minx || 0) + 1;
+		var dy = -(miny || 0) + 1;
+		
+		if (initialBounds != null)
+		{
+			dx += initialBounds.x;
+			dy += initialBounds.y;
+		}
+		
+		this.graph.moveCells(this.vertexArray, dx, dy);
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+};
+
+/**
+ * Function: calcPositions
+ * 
+ * Takes the displacements calculated for each cell and applies them to the
+ * local cache of cell positions. Limits the displacement to the current
+ * temperature.
+ */
+mxFastOrganicLayout.prototype.calcPositions = function()
+{
+	for (var index = 0; index < this.vertexArray.length; index++)
+	{
+		if (this.isMoveable[index])
+		{
+			// Get the distance of displacement for this node for this
+			// iteration
+			var deltaLength = Math.sqrt(this.dispX[index] * this.dispX[index] +
+				this.dispY[index] * this.dispY[index]);
+
+			if (deltaLength < 0.001)
+			{
+				deltaLength = 0.001;
+			}
+
+			// Scale down by the current temperature if less than the
+			// displacement distance
+			var newXDisp = this.dispX[index] / deltaLength
+				* Math.min(deltaLength, this.temperature);
+
+			var newYDisp = this.dispY[index] / deltaLength
+				* Math.min(deltaLength, this.temperature);
+
+			// reset displacements
+			this.dispX[index] = 0;
+			this.dispY[index] = 0;
+
+			// Update the cached cell locations
+			this.cellLocation[index][0] += newXDisp;
+			this.cellLocation[index][1] += newYDisp;
+		}
+	}
+};
+
+/**
+ * Function: calcAttraction
+ * 
+ * Calculates the attractive forces between all laid out nodes linked by
+ * edges
+ */
+mxFastOrganicLayout.prototype.calcAttraction = function()
+{
+	// Check the neighbours of each vertex and calculate the attractive
+	// force of the edge connecting them
+	for (var i = 0; i < this.vertexArray.length; i++)
+	{
+		for (var k = 0; k < this.neighbours[i].length; k++)
+		{
+			// Get the index of the othe cell in the vertex array
+			var j = this.neighbours[i][k];
+			
+			// Do not proceed self-loops
+			if (i != j &&
+				this.isMoveable[i] &&
+				this.isMoveable[j])
+			{
+				var xDelta = this.cellLocation[i][0] - this.cellLocation[j][0];
+				var yDelta = this.cellLocation[i][1] - this.cellLocation[j][1];
+
+				// The distance between the nodes
+				var deltaLengthSquared = xDelta * xDelta + yDelta
+						* yDelta - this.radiusSquared[i] - this.radiusSquared[j];
+
+				if (deltaLengthSquared < this.minDistanceLimitSquared)
+				{
+					deltaLengthSquared = this.minDistanceLimitSquared;
+				}
+				
+				var deltaLength = Math.sqrt(deltaLengthSquared);
+				var force = (deltaLengthSquared) / this.forceConstant;
+
+				var displacementX = (xDelta / deltaLength) * force;
+				var displacementY = (yDelta / deltaLength) * force;
+				
+				this.dispX[i] -= displacementX;
+				this.dispY[i] -= displacementY;
+				
+				this.dispX[j] += displacementX;
+				this.dispY[j] += displacementY;
+			}
+		}
+	}
+};
+
+/**
+ * Function: calcRepulsion
+ * 
+ * Calculates the repulsive forces between all laid out nodes
+ */
+mxFastOrganicLayout.prototype.calcRepulsion = function()
+{
+	var vertexCount = this.vertexArray.length;
+
+	for (var i = 0; i < vertexCount; i++)
+	{
+		for (var j = i; j < vertexCount; j++)
+		{
+			// Exits if the layout is no longer allowed to run
+			if (!this.allowedToRun)
+			{
+				return;
+			}
+
+			if (j != i &&
+				this.isMoveable[i] &&
+				this.isMoveable[j])
+			{
+				var xDelta = this.cellLocation[i][0] - this.cellLocation[j][0];
+				var yDelta = this.cellLocation[i][1] - this.cellLocation[j][1];
+
+				if (xDelta == 0)
+				{
+					xDelta = 0.01 + Math.random();
+				}
+				
+				if (yDelta == 0)
+				{
+					yDelta = 0.01 + Math.random();
+				}
+				
+				// Distance between nodes
+				var deltaLength = Math.sqrt((xDelta * xDelta)
+						+ (yDelta * yDelta));
+				var deltaLengthWithRadius = deltaLength - this.radius[i]
+						- this.radius[j];
+
+				if (deltaLengthWithRadius > this.maxDistanceLimit)
+				{
+					// Ignore vertices too far apart
+					continue;
+				}
+
+				if (deltaLengthWithRadius < this.minDistanceLimit)
+				{
+					deltaLengthWithRadius = this.minDistanceLimit;
+				}
+
+				var force = this.forceConstantSquared / deltaLengthWithRadius;
+
+				var displacementX = (xDelta / deltaLength) * force;
+				var displacementY = (yDelta / deltaLength) * force;
+				
+				this.dispX[i] += displacementX;
+				this.dispY[i] += displacementY;
+
+				this.dispX[j] -= displacementX;
+				this.dispY[j] -= displacementY;
+			}
+		}
+	}
+};
+
+/**
+ * Function: reduceTemperature
+ * 
+ * Reduces the temperature of the layout from an initial setting in a linear
+ * fashion to zero.
+ */
+mxFastOrganicLayout.prototype.reduceTemperature = function()
+{
+	this.temperature = this.initialTemp * (1.0 - this.iteration / this.maxIterations);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCircleLayout
+ * 
+ * Extends <mxGraphLayout> to implement a circluar layout for a given radius.
+ * The vertices do not need to be connected for this layout to work and all
+ * connections between vertices are not taken into account.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var layout = new mxCircleLayout(graph);
+ * layout.execute(graph.getDefaultParent());
+ * (end)
+ * 
+ * Constructor: mxCircleLayout
+ *
+ * Constructs a new circular layout for the specified radius.
+ *
+ * Arguments:
+ * 
+ * graph - <mxGraph> that contains the cells.
+ * radius - Optional radius as an int. Default is 100.
+ */
+function mxCircleLayout(graph, radius)
+{
+	mxGraphLayout.call(this, graph);
+	this.radius = (radius != null) ? radius : 100;
+};
+
+/**
+ * Extends mxGraphLayout.
+ */
+mxCircleLayout.prototype = new mxGraphLayout();
+mxCircleLayout.prototype.constructor = mxCircleLayout;
+
+/**
+ * Variable: radius
+ * 
+ * Integer specifying the size of the radius. Default is 100.
+ */
+mxCircleLayout.prototype.radius = null;
+
+/**
+ * Variable: moveCircle
+ * 
+ * Boolean specifying if the circle should be moved to the top,
+ * left corner specified by <x0> and <y0>. Default is false.
+ */
+mxCircleLayout.prototype.moveCircle = false;
+
+/**
+ * Variable: x0
+ * 
+ * Integer specifying the left coordinate of the circle.
+ * Default is 0.
+ */
+mxCircleLayout.prototype.x0 = 0;
+
+/**
+ * Variable: y0
+ * 
+ * Integer specifying the top coordinate of the circle.
+ * Default is 0.
+ */
+mxCircleLayout.prototype.y0 = 0;
+
+/**
+ * Variable: resetEdges
+ * 
+ * Specifies if all edge points of traversed edges should be removed.
+ * Default is true.
+ */
+mxCircleLayout.prototype.resetEdges = true;
+
+/**
+ * Variable: disableEdgeStyle
+ * 
+ * Specifies if the STYLE_NOEDGESTYLE flag should be set on edges that are
+ * modified by the result. Default is true.
+ */
+mxCircleLayout.prototype.disableEdgeStyle = true;
+
+/**
+ * Function: execute
+ * 
+ * Implements <mxGraphLayout.execute>.
+ */
+mxCircleLayout.prototype.execute = function(parent)
+{
+	var model = this.graph.getModel();
+
+	// Moves the vertices to build a circle. Makes sure the
+	// radius is large enough for the vertices to not
+	// overlap
+	model.beginUpdate();
+	try
+	{
+		// Gets all vertices inside the parent and finds
+		// the maximum dimension of the largest vertex
+		var max = 0;
+		var top = null;
+		var left = null;
+		var vertices = [];
+		var childCount = model.getChildCount(parent);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			var cell = model.getChildAt(parent, i);
+			
+			if (!this.isVertexIgnored(cell))
+			{
+				vertices.push(cell);
+				var bounds = this.getVertexBounds(cell);
+				
+				if (top == null)
+				{
+					top = bounds.y;
+				}
+				else
+				{
+					top = Math.min(top, bounds.y);
+				}
+				
+				if (left == null)
+				{
+					left = bounds.x;
+				}
+				else
+				{
+					left = Math.min(left, bounds.x);
+				}
+				
+				max = Math.max(max, Math.max(bounds.width, bounds.height));
+			}
+			else if (!this.isEdgeIgnored(cell))
+			{
+				// Resets the points on the traversed edge
+				if (this.resetEdges)
+				{
+					this.graph.resetEdge(cell);
+				}
+
+			    if (this.disableEdgeStyle)
+			    {
+			    	this.setEdgeStyleEnabled(cell, false);
+			    }
+			}
+		}
+		
+		var r = this.getRadius(vertices.length, max);
+
+		// Moves the circle to the specified origin
+		if (this.moveCircle)
+		{
+			left = this.x0;
+			top = this.y0;
+		}
+		
+		this.circle(vertices, r, left, top);
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+};
+
+/**
+ * Function: getRadius
+ * 
+ * Returns the radius to be used for the given vertex count. Max is the maximum
+ * width or height of all vertices in the layout.
+ */
+mxCircleLayout.prototype.getRadius = function(count, max)
+{
+	return Math.max(count * max / Math.PI, this.radius);
+};
+
+/**
+ * Function: circle
+ * 
+ * Executes the circular layout for the specified array
+ * of vertices and the given radius. This is called from
+ * <execute>.
+ */
+mxCircleLayout.prototype.circle = function(vertices, r, left, top)
+{
+	var vertexCount = vertices.length;
+	var phi = 2 * Math.PI / vertexCount;
+	
+	for (var i = 0; i < vertexCount; i++)
+	{
+		if (this.isVertexMovable(vertices[i]))
+		{
+			this.setVertexLocation(vertices[i],
+				left + r + r * Math.sin(i*phi),
+				top + r + r * Math.cos(i*phi));
+		}
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxParallelEdgeLayout
+ * 
+ * Extends <mxGraphLayout> for arranging parallel edges. This layout works
+ * on edges for all pairs of vertices where there is more than one edge
+ * connecting the latter.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var layout = new mxParallelEdgeLayout(graph);
+ * layout.execute(graph.getDefaultParent());
+ * (end)
+ * 
+ * To run the layout for the parallel edges of a changed edge only, the
+ * following code can be used.
+ * 
+ * (code)
+ * var layout = new mxParallelEdgeLayout(graph);
+ * 
+ * graph.addListener(mxEvent.CELL_CONNECTED, function(sender, evt)
+ * {
+ *   var model = graph.getModel();
+ *   var edge = evt.getProperty('edge');
+ *   var src = model.getTerminal(edge, true);
+ *   var trg = model.getTerminal(edge, false);
+ *   
+ *   layout.isEdgeIgnored = function(edge2)
+ *   {
+ *     var src2 = model.getTerminal(edge2, true);
+ *     var trg2 = model.getTerminal(edge2, false);
+ *     
+ *     return !(model.isEdge(edge2) && ((src == src2 && trg == trg2) || (src == trg2 && trg == src2)));
+ *   };
+ *   
+ *   layout.execute(graph.getDefaultParent());
+ * });
+ * (end)
+ * 
+ * Constructor: mxCompactTreeLayout
+ * 
+ * Constructs a new fast organic layout for the specified graph.
+ */
+function mxParallelEdgeLayout(graph)
+{
+	mxGraphLayout.call(this, graph);
+};
+
+/**
+ * Extends mxGraphLayout.
+ */
+mxParallelEdgeLayout.prototype = new mxGraphLayout();
+mxParallelEdgeLayout.prototype.constructor = mxParallelEdgeLayout;
+
+/**
+ * Variable: spacing
+ * 
+ * Defines the spacing between the parallels. Default is 20.
+ */
+mxParallelEdgeLayout.prototype.spacing = 20;
+
+/**
+ * Function: execute
+ * 
+ * Implements <mxGraphLayout.execute>.
+ */
+mxParallelEdgeLayout.prototype.execute = function(parent)
+{
+	var lookup = this.findParallels(parent);
+	
+	this.graph.model.beginUpdate();	
+	try
+	{
+		for (var i in lookup)
+		{
+			var parallels = lookup[i];
+
+			if (parallels.length > 1)
+			{
+				this.layout(parallels);
+			}
+		}
+	}
+	finally
+	{
+		this.graph.model.endUpdate();
+	}
+};
+
+/**
+ * Function: findParallels
+ * 
+ * Finds the parallel edges in the given parent.
+ */
+mxParallelEdgeLayout.prototype.findParallels = function(parent)
+{
+	var model = this.graph.getModel();
+	var lookup = [];
+	var childCount = model.getChildCount(parent);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = model.getChildAt(parent, i);
+		
+		if (!this.isEdgeIgnored(child))
+		{
+			var id = this.getEdgeId(child);
+			
+			if (id != null)
+			{
+				if (lookup[id] == null)
+				{
+					lookup[id] = [];
+				}
+				
+				lookup[id].push(child);
+			}
+		}
+	}
+	
+	return lookup;
+};
+
+/**
+ * Function: getEdgeId
+ * 
+ * Returns a unique ID for the given edge. The id is independent of the
+ * edge direction and is built using the visible terminal of the given
+ * edge.
+ */
+mxParallelEdgeLayout.prototype.getEdgeId = function(edge)
+{
+	var view = this.graph.getView();
+	
+	// Cannot used cached visible terminal because this could be triggered in BEFORE_UNDO
+	var src = view.getVisibleTerminal(edge, true);
+	var trg = view.getVisibleTerminal(edge, false);
+
+	if (src != null && trg != null)
+	{
+		src = mxObjectIdentity.get(src);
+		trg = mxObjectIdentity.get(trg);
+		
+		return (src > trg) ? trg + '-' + src : src + '-' + trg;
+	}
+	
+	return null;
+};
+
+/**
+ * Function: layout
+ * 
+ * Lays out the parallel edges in the given array.
+ */
+mxParallelEdgeLayout.prototype.layout = function(parallels)
+{
+	var edge = parallels[0];
+	var view = this.graph.getView();
+	var model = this.graph.getModel();
+	var src = model.getGeometry(view.getVisibleTerminal(edge, true));
+	var trg = model.getGeometry(view.getVisibleTerminal(edge, false));
+	
+	// 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)
+	{
+		// Routes parallel edges
+		var scx = src.x + src.width / 2;
+		var scy = src.y + src.height / 2;
+		
+		var tcx = trg.x + trg.width / 2;
+		var tcy = trg.y + 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;
+			}
+		}
+	}
+};
+
+/**
+ * Function: route
+ * 
+ * Routes the given edge via the given point.
+ */
+mxParallelEdgeLayout.prototype.route = function(edge, x, y)
+{
+	if (this.graph.isCellMovable(edge))
+	{
+		this.setEdgePoints(edge, [new mxPoint(x, y)]);
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCompositeLayout
+ * 
+ * Allows to compose multiple layouts into a single layout. The master layout
+ * is the layout that handles move operations if another layout than the first
+ * element in <layouts> should be used. The <master> layout is not executed as
+ * the code assumes that it is part of <layouts>.
+ * 
+ * Example:
+ * (code)
+ * var first = new mxFastOrganicLayout(graph);
+ * var second = new mxParallelEdgeLayout(graph);
+ * var layout = new mxCompositeLayout(graph, [first, second], first);
+ * layout.execute(graph.getDefaultParent());
+ * (end)
+ * 
+ * Constructor: mxCompositeLayout
+ *
+ * Constructs a new layout using the given layouts. The graph instance is
+ * required for creating the transaction that contains all layouts.
+ *
+ * Arguments:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * layouts - Array of <mxGraphLayouts>.
+ * master - Optional layout that handles moves. If no layout is given then
+ * the first layout of the above array is used to handle moves.
+ */
+function mxCompositeLayout(graph, layouts, master)
+{
+	mxGraphLayout.call(this, graph);
+	this.layouts = layouts;
+	this.master = master;
+};
+
+/**
+ * Extends mxGraphLayout.
+ */
+mxCompositeLayout.prototype = new mxGraphLayout();
+mxCompositeLayout.prototype.constructor = mxCompositeLayout;
+	
+/**
+ * Variable: layouts
+ * 
+ * Holds the array of <mxGraphLayouts> that this layout contains.
+ */
+mxCompositeLayout.prototype.layouts = null;
+
+/**
+ * Variable: layouts
+ * 
+ * Reference to the <mxGraphLayouts> that handles moves. If this is null
+ * then the first layout in <layouts> is used.
+ */
+mxCompositeLayout.prototype.master = null;
+
+/**
+ * Function: moveCell
+ * 
+ * Implements <mxGraphLayout.moveCell> by calling move on <master> or the first
+ * layout in <layouts>.
+ */
+mxCompositeLayout.prototype.moveCell = function(cell, x, y)
+{
+	if (this.master != null)
+	{
+		this.master.move.apply(this.master, arguments);
+	}
+	else
+	{
+		this.layouts[0].move.apply(this.layouts[0], arguments);
+	}
+};
+
+/**
+ * Function: execute
+ * 
+ * Implements <mxGraphLayout.execute> by executing all <layouts> in a
+ * single transaction.
+ */
+mxCompositeLayout.prototype.execute = function(parent)
+{
+	var model = this.graph.getModel();
+	
+	model.beginUpdate();
+	try
+	{
+		for (var i = 0; i < this.layouts.length; i++)
+		{
+			this.layouts[i].execute.apply(this.layouts[i], arguments);
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxEdgeLabelLayout
+ * 
+ * Extends <mxGraphLayout> to implement an edge label layout. This layout
+ * makes use of cell states, which means the graph must be validated in
+ * a graph view (so that the label bounds are available) before this layout
+ * can be executed.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var layout = new mxEdgeLabelLayout(graph);
+ * layout.execute(graph.getDefaultParent());
+ * (end)
+ * 
+ * Constructor: mxEdgeLabelLayout
+ *
+ * Constructs a new edge label layout.
+ *
+ * Arguments:
+ * 
+ * graph - <mxGraph> that contains the cells.
+ */
+function mxEdgeLabelLayout(graph, radius)
+{
+	mxGraphLayout.call(this, graph);
+};
+
+/**
+ * Extends mxGraphLayout.
+ */
+mxEdgeLabelLayout.prototype = new mxGraphLayout();
+mxEdgeLabelLayout.prototype.constructor = mxEdgeLabelLayout;
+
+/**
+ * Function: execute
+ * 
+ * Implements <mxGraphLayout.execute>.
+ */
+mxEdgeLabelLayout.prototype.execute = function(parent)
+{
+	var view = this.graph.view;
+	var model = this.graph.getModel();
+	
+	// Gets all vertices and edges inside the parent
+	var edges = [];
+	var vertices = [];
+	var childCount = model.getChildCount(parent);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		var cell = model.getChildAt(parent, i);
+		var state = view.getState(cell);
+		
+		if (state != null)
+		{
+			if (!this.isVertexIgnored(cell))
+			{
+				vertices.push(state);
+			}
+			else if (!this.isEdgeIgnored(cell))
+			{
+				edges.push(state);
+			}
+		}
+	}
+	
+	this.placeLabels(vertices, edges);
+};
+
+/**
+ * Function: placeLabels
+ * 
+ * Places the labels of the given edges.
+ */
+mxEdgeLabelLayout.prototype.placeLabels = function(v, e)
+{
+	var model = this.graph.getModel();
+	
+	// Moves the vertices to build a circle. Makes sure the
+	// radius is large enough for the vertices to not
+	// overlap
+	model.beginUpdate();
+	try
+	{
+		for (var i = 0; i < e.length; i++)
+		{
+			var edge = e[i];
+			
+			if (edge != null && edge.text != null &&
+				edge.text.boundingBox != null)
+			{
+				for (var j = 0; j < v.length; j++)
+				{
+					var vertex = v[j];
+					
+					if (vertex != null)
+					{
+						this.avoid(edge, vertex);
+					}
+				}
+			}
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+};
+
+/**
+ * Function: avoid
+ * 
+ * Places the labels of the given edges.
+ */
+mxEdgeLabelLayout.prototype.avoid = function(edge, vertex)
+{
+	var model = this.graph.getModel();
+	var labRect = edge.text.boundingBox;
+	
+	if (mxUtils.intersects(labRect, vertex))
+	{
+		var dy1 = -labRect.y - labRect.height + vertex.y;
+		var dy2 = -labRect.y + vertex.y + vertex.height;
+		
+		var dy = (Math.abs(dy1) < Math.abs(dy2)) ? dy1 : dy2;
+		
+		var dx1 = -labRect.x - labRect.width + vertex.x;
+		var dx2 = -labRect.x + vertex.x + vertex.width;
+	
+		var dx = (Math.abs(dx1) < Math.abs(dx2)) ? dx1 : dx2;
+		
+		if (Math.abs(dx) < Math.abs(dy))
+		{
+			dy = 0;
+		}
+		else
+		{
+			dx = 0;
+		}
+	
+		var g = model.getGeometry(edge.cell);
+		
+		if (g != null)
+		{
+			g = g.clone();
+			
+			if (g.offset != null)
+			{
+				g.offset.x += dx;
+				g.offset.y += dy;
+			}
+			else
+			{
+				g.offset = new mxPoint(dx, dy);
+			}
+			
+			model.setGeometry(edge.cell, g);
+		}
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphAbstractHierarchyCell
+ * 
+ * An abstraction of an internal hierarchy node or edge
+ * 
+ * Constructor: mxGraphAbstractHierarchyCell
+ *
+ * Constructs a new hierarchical layout algorithm.
+ *
+ * Arguments:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * deterministic - Optional boolean that specifies if this layout should be
+ * deterministic. Default is true.
+ */
+function mxGraphAbstractHierarchyCell()
+{
+	this.x = [];
+	this.y = [];
+	this.temp = [];
+};
+
+/**
+ * Variable: maxRank
+ * 
+ * The maximum rank this cell occupies. Default is -1.
+ */
+mxGraphAbstractHierarchyCell.prototype.maxRank = -1;
+
+/**
+ * Variable: minRank
+ * 
+ * The minimum rank this cell occupies. Default is -1.
+ */
+mxGraphAbstractHierarchyCell.prototype.minRank = -1;
+
+/**
+ * Variable: x
+ * 
+ * The x position of this cell for each layer it occupies
+ */
+mxGraphAbstractHierarchyCell.prototype.x = null;
+
+/**
+ * Variable: y
+ * 
+ * The y position of this cell for each layer it occupies
+ */
+mxGraphAbstractHierarchyCell.prototype.y = null;
+
+/**
+ * Variable: width
+ * 
+ * The width of this cell
+ */
+mxGraphAbstractHierarchyCell.prototype.width = 0;
+
+/**
+ * Variable: height
+ * 
+ * The height of this cell
+ */
+mxGraphAbstractHierarchyCell.prototype.height = 0;
+
+/**
+ * Variable: nextLayerConnectedCells
+ * 
+ * A cached version of the cells this cell connects to on the next layer up
+ */
+mxGraphAbstractHierarchyCell.prototype.nextLayerConnectedCells = null;
+
+/**
+ * Variable: previousLayerConnectedCells
+ * 
+ * A cached version of the cells this cell connects to on the next layer down
+ */
+mxGraphAbstractHierarchyCell.prototype.previousLayerConnectedCells = null;
+
+/**
+ * Variable: temp
+ * 
+ * Temporary variable for general use. Generally, try to avoid
+ * carrying information between stages. Currently, the longest
+ * path layering sets temp to the rank position in fixRanks()
+ * and the crossing reduction uses this. This meant temp couldn't
+ * be used for hashing the nodes in the model dfs and so hashCode
+ * was created
+ */
+mxGraphAbstractHierarchyCell.prototype.temp = null;
+
+/**
+ * Function: getNextLayerConnectedCells
+ * 
+ * Returns the cells this cell connects to on the next layer up
+ */
+mxGraphAbstractHierarchyCell.prototype.getNextLayerConnectedCells = function(layer)
+{
+	return null;
+};
+
+/**
+ * Function: getPreviousLayerConnectedCells
+ * 
+ * Returns the cells this cell connects to on the next layer down
+ */
+mxGraphAbstractHierarchyCell.prototype.getPreviousLayerConnectedCells = function(layer)
+{
+	return null;
+};
+
+/**
+ * Function: isEdge
+ * 
+ * Returns whether or not this cell is an edge
+ */
+mxGraphAbstractHierarchyCell.prototype.isEdge = function()
+{
+	return false;
+};
+
+/**
+ * Function: isVertex
+ * 
+ * Returns whether or not this cell is a node
+ */
+mxGraphAbstractHierarchyCell.prototype.isVertex = function()
+{
+	return false;
+};
+
+/**
+ * Function: getGeneralPurposeVariable
+ * 
+ * Gets the value of temp for the specified layer
+ */
+mxGraphAbstractHierarchyCell.prototype.getGeneralPurposeVariable = function(layer)
+{
+	return null;
+};
+
+/**
+ * Function: setGeneralPurposeVariable
+ * 
+ * Set the value of temp for the specified layer
+ */
+mxGraphAbstractHierarchyCell.prototype.setGeneralPurposeVariable = function(layer, value)
+{
+	return null;
+};
+
+/**
+ * Function: setX
+ * 
+ * Set the value of x for the specified layer
+ */
+mxGraphAbstractHierarchyCell.prototype.setX = function(layer, value)
+{
+	if (this.isVertex())
+	{
+		this.x[0] = value;
+	}
+	else if (this.isEdge())
+	{
+		this.x[layer - this.minRank - 1] = value;
+	}
+};
+
+/**
+ * Function: getX
+ * 
+ * Gets the value of x on the specified layer
+ */
+mxGraphAbstractHierarchyCell.prototype.getX = function(layer)
+{
+	if (this.isVertex())
+	{
+		return this.x[0];
+	}
+	else if (this.isEdge())
+	{
+		return this.x[layer - this.minRank - 1];
+	}
+
+	return 0.0;
+};
+
+/**
+ * Function: setY
+ * 
+ * Set the value of y for the specified layer
+ */
+mxGraphAbstractHierarchyCell.prototype.setY = function(layer, value)
+{
+	if (this.isVertex())
+	{
+		this.y[0] = value;
+	}
+	else if (this.isEdge())
+	{
+		this.y[layer -this. minRank - 1] = value;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphHierarchyNode
+ * 
+ * An abstraction of a hierarchical edge for the hierarchy layout
+ * 
+ * Constructor: mxGraphHierarchyNode
+ *
+ * Constructs an internal node to represent the specified real graph cell
+ *
+ * Arguments:
+ * 
+ * cell - the real graph cell this node represents
+ */
+function mxGraphHierarchyNode(cell)
+{
+	mxGraphAbstractHierarchyCell.apply(this, arguments);
+	this.cell = cell;
+	this.id = mxObjectIdentity.get(cell);
+	this.connectsAsTarget = [];
+	this.connectsAsSource = [];
+};
+
+/**
+ * Extends mxGraphAbstractHierarchyCell.
+ */
+mxGraphHierarchyNode.prototype = new mxGraphAbstractHierarchyCell();
+mxGraphHierarchyNode.prototype.constructor = mxGraphHierarchyNode;
+
+/**
+ * Variable: cell
+ * 
+ * The graph cell this object represents.
+ */
+mxGraphHierarchyNode.prototype.cell = null;
+
+/**
+ * Variable: id
+ * 
+ * The object identity of the wrapped cell
+ */
+mxGraphHierarchyNode.prototype.id = null;
+
+/**
+ * Variable: connectsAsTarget
+ * 
+ * Collection of hierarchy edges that have this node as a target
+ */
+mxGraphHierarchyNode.prototype.connectsAsTarget = null;
+
+/**
+ * Variable: connectsAsSource
+ * 
+ * Collection of hierarchy edges that have this node as a source
+ */
+mxGraphHierarchyNode.prototype.connectsAsSource = null;
+
+/**
+ * Variable: hashCode
+ * 
+ * Assigns a unique hashcode for each node. Used by the model dfs instead
+ * of copying HashSets
+ */
+mxGraphHierarchyNode.prototype.hashCode = false;
+
+/**
+ * Function: getRankValue
+ * 
+ * Returns the integer value of the layer that this node resides in
+ */
+mxGraphHierarchyNode.prototype.getRankValue = function(layer)
+{
+	return this.maxRank;
+};
+
+/**
+ * Function: getNextLayerConnectedCells
+ * 
+ * Returns the cells this cell connects to on the next layer up
+ */
+mxGraphHierarchyNode.prototype.getNextLayerConnectedCells = function(layer)
+{
+	if (this.nextLayerConnectedCells == null)
+	{
+		this.nextLayerConnectedCells = [];
+		this.nextLayerConnectedCells[0] = [];
+		
+		for (var i = 0; i < this.connectsAsTarget.length; i++)
+		{
+			var edge = this.connectsAsTarget[i];
+
+			if (edge.maxRank == -1 || edge.maxRank == layer + 1)
+			{
+				// Either edge is not in any rank or
+				// no dummy nodes in edge, add node of other side of edge
+				this.nextLayerConnectedCells[0].push(edge.source);
+			}
+			else
+			{
+				// Edge spans at least two layers, add edge
+				this.nextLayerConnectedCells[0].push(edge);
+			}
+		}
+	}
+
+	return this.nextLayerConnectedCells[0];
+};
+
+/**
+ * Function: getPreviousLayerConnectedCells
+ * 
+ * Returns the cells this cell connects to on the next layer down
+ */
+mxGraphHierarchyNode.prototype.getPreviousLayerConnectedCells = function(layer)
+{
+	if (this.previousLayerConnectedCells == null)
+	{
+		this.previousLayerConnectedCells = [];
+		this.previousLayerConnectedCells[0] = [];
+		
+		for (var i = 0; i < this.connectsAsSource.length; i++)
+		{
+			var edge = this.connectsAsSource[i];
+
+			if (edge.minRank == -1 || edge.minRank == layer - 1)
+			{
+				// No dummy nodes in edge, add node of other side of edge
+				this.previousLayerConnectedCells[0].push(edge.target);
+			}
+			else
+			{
+				// Edge spans at least two layers, add edge
+				this.previousLayerConnectedCells[0].push(edge);
+			}
+		}
+	}
+
+	return this.previousLayerConnectedCells[0];
+};
+
+/**
+ * Function: isVertex
+ * 
+ * Returns true.
+ */
+mxGraphHierarchyNode.prototype.isVertex = function()
+{
+	return true;
+};
+
+/**
+ * Function: getGeneralPurposeVariable
+ * 
+ * Gets the value of temp for the specified layer
+ */
+mxGraphHierarchyNode.prototype.getGeneralPurposeVariable = function(layer)
+{
+	return this.temp[0];
+};
+
+/**
+ * Function: setGeneralPurposeVariable
+ * 
+ * Set the value of temp for the specified layer
+ */
+mxGraphHierarchyNode.prototype.setGeneralPurposeVariable = function(layer, value)
+{
+	this.temp[0] = value;
+};
+
+/**
+ * Function: isAncestor
+ */
+mxGraphHierarchyNode.prototype.isAncestor = function(otherNode)
+{
+	// Firstly, the hash code of this node needs to be shorter than the
+	// other node
+	if (otherNode != null && this.hashCode != null && otherNode.hashCode != null
+			&& this.hashCode.length < otherNode.hashCode.length)
+	{
+		if (this.hashCode == otherNode.hashCode)
+		{
+			return true;
+		}
+		
+		if (this.hashCode == null || this.hashCode == null)
+		{
+			return false;
+		}
+		
+		// Secondly, this hash code must match the start of the other
+		// node's hash code. Arrays.equals cannot be used here since
+		// the arrays are different length, and we do not want to
+		// perform another array copy.
+		for (var i = 0; i < this.hashCode.length; i++)
+		{
+			if (this.hashCode[i] != otherNode.hashCode[i])
+			{
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	return false;
+};
+
+/**
+ * Function: getCoreCell
+ * 
+ * Gets the core vertex associated with this wrapper
+ */
+mxGraphHierarchyNode.prototype.getCoreCell = function()
+{
+	return this.cell;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphHierarchyEdge
+ * 
+ * An abstraction of a hierarchical edge for the hierarchy layout
+ * 
+ * Constructor: mxGraphHierarchyEdge
+ *
+ * Constructs a hierarchy edge
+ *
+ * Arguments:
+ * 
+ * edges - a list of real graph edges this abstraction represents
+ */
+function mxGraphHierarchyEdge(edges)
+{
+	mxGraphAbstractHierarchyCell.apply(this, arguments);
+	this.edges = edges;
+	this.ids = [];
+	
+	for (var i = 0; i < edges.length; i++)
+	{
+		this.ids.push(mxObjectIdentity.get(edges[i]));
+	}
+};
+
+/**
+ * Extends mxGraphAbstractHierarchyCell.
+ */
+mxGraphHierarchyEdge.prototype = new mxGraphAbstractHierarchyCell();
+mxGraphHierarchyEdge.prototype.constructor = mxGraphHierarchyEdge;
+
+/**
+ * Variable: edges
+ * 
+ * The graph edge(s) this object represents. Parallel edges are all grouped
+ * together within one hierarchy edge.
+ */
+mxGraphHierarchyEdge.prototype.edges = null;
+
+/**
+ * Variable: ids
+ * 
+ * The object identities of the wrapped cells
+ */
+mxGraphHierarchyEdge.prototype.ids = null;
+
+/**
+ * Variable: source
+ * 
+ * The node this edge is sourced at
+ */
+mxGraphHierarchyEdge.prototype.source = null;
+
+/**
+ * Variable: target
+ * 
+ * The node this edge targets
+ */
+mxGraphHierarchyEdge.prototype.target = null;
+
+/**
+ * Variable: isReversed
+ * 
+ * Whether or not the direction of this edge has been reversed
+ * internally to create a DAG for the hierarchical layout
+ */
+mxGraphHierarchyEdge.prototype.isReversed = false;
+
+/**
+ * Function: invert
+ * 
+ * Inverts the direction of this internal edge(s)
+ */
+mxGraphHierarchyEdge.prototype.invert = function(layer)
+{
+	var temp = this.source;
+	this.source = this.target;
+	this.target = temp;
+	this.isReversed = !this.isReversed;
+};
+
+/**
+ * Function: getNextLayerConnectedCells
+ * 
+ * Returns the cells this cell connects to on the next layer up
+ */
+mxGraphHierarchyEdge.prototype.getNextLayerConnectedCells = function(layer)
+{
+	if (this.nextLayerConnectedCells == null)
+	{
+		this.nextLayerConnectedCells = [];
+		
+		for (var i = 0; i < this.temp.length; i++)
+		{
+			this.nextLayerConnectedCells[i] = [];
+			
+			if (i == this.temp.length - 1)
+			{
+				this.nextLayerConnectedCells[i].push(this.source);
+			}
+			else
+			{
+				this.nextLayerConnectedCells[i].push(this);
+			}
+		}
+	}
+	
+	return this.nextLayerConnectedCells[layer - this.minRank - 1];
+};
+
+/**
+ * Function: getPreviousLayerConnectedCells
+ * 
+ * Returns the cells this cell connects to on the next layer down
+ */
+mxGraphHierarchyEdge.prototype.getPreviousLayerConnectedCells = function(layer)
+{
+	if (this.previousLayerConnectedCells == null)
+	{
+		this.previousLayerConnectedCells = [];
+
+		for (var i = 0; i < this.temp.length; i++)
+		{
+			this.previousLayerConnectedCells[i] = [];
+			
+			if (i == 0)
+			{
+				this.previousLayerConnectedCells[i].push(this.target);
+			}
+			else
+			{
+				this.previousLayerConnectedCells[i].push(this);
+			}
+		}
+	}
+
+	return this.previousLayerConnectedCells[layer - this.minRank - 1];
+};
+
+/**
+ * Function: isEdge
+ * 
+ * Returns true.
+ */
+mxGraphHierarchyEdge.prototype.isEdge = function()
+{
+	return true;
+};
+
+/**
+ * Function: getGeneralPurposeVariable
+ * 
+ * Gets the value of temp for the specified layer
+ */
+mxGraphHierarchyEdge.prototype.getGeneralPurposeVariable = function(layer)
+{
+	return this.temp[layer - this.minRank - 1];
+};
+
+/**
+ * Function: setGeneralPurposeVariable
+ * 
+ * Set the value of temp for the specified layer
+ */
+mxGraphHierarchyEdge.prototype.setGeneralPurposeVariable = function(layer, value)
+{
+	this.temp[layer - this.minRank - 1] = value;
+};
+
+/**
+ * Function: getCoreCell
+ * 
+ * Gets the first core edge associated with this wrapper
+ */
+mxGraphHierarchyEdge.prototype.getCoreCell = function()
+{
+	if (this.edges != null && this.edges.length > 0)
+	{
+		return this.edges[0];
+	}
+	
+	return null;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphHierarchyModel
+ *
+ * Internal model of a hierarchical graph. This model stores nodes and edges
+ * equivalent to the real graph nodes and edges, but also stores the rank of the
+ * cells, the order within the ranks and the new candidate locations of cells.
+ * The internal model also reverses edge direction were appropriate , ignores
+ * self-loop and groups parallels together under one edge object.
+ *
+ * Constructor: mxGraphHierarchyModel
+ *
+ * Creates an internal ordered graph model using the vertices passed in. If
+ * there are any, leftward edge need to be inverted in the internal model
+ *
+ * Arguments:
+ *
+ * graph - the facade describing the graph to be operated on
+ * vertices - the vertices for this hierarchy
+ * ordered - whether or not the vertices are already ordered
+ * deterministic - whether or not this layout should be deterministic on each
+ * tightenToSource - whether or not to tighten vertices towards the sources
+ * scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
+ * usage
+ */
+function mxGraphHierarchyModel(layout, vertices, roots, parent, tightenToSource)
+{
+	var graph = layout.getGraph();
+	this.tightenToSource = tightenToSource;
+	this.roots = roots;
+	this.parent = parent;
+
+	// map of cells to internal cell needed for second run through
+	// to setup the sink of edges correctly
+	this.vertexMapper = new mxDictionary();
+	this.edgeMapper = new mxDictionary();
+	this.maxRank = 0;
+	var internalVertices = [];
+
+	if (vertices == null)
+	{
+		vertices = this.graph.getChildVertices(parent);
+	}
+
+	this.maxRank = this.SOURCESCANSTARTRANK;
+	// map of cells to internal cell needed for second run through
+	// to setup the sink of edges correctly. Guess size by number
+	// of edges is roughly same as number of vertices.
+	this.createInternalCells(layout, vertices, internalVertices);
+
+	// Go through edges set their sink values. Also check the
+	// ordering if and invert edges if necessary
+	for (var i = 0; i < vertices.length; i++)
+	{
+		var edges = internalVertices[i].connectsAsSource;
+
+		for (var j = 0; j < edges.length; j++)
+		{
+			var internalEdge = edges[j];
+			var realEdges = internalEdge.edges;
+
+			// Only need to process the first real edge, since
+			// all the edges connect to the same other vertex
+			if (realEdges != null && realEdges.length > 0)
+			{
+				var realEdge = realEdges[0];
+				var targetCell = layout.getVisibleTerminal(
+						realEdge, false);
+				var internalTargetCell = this.vertexMapper.get(targetCell);
+
+				if (internalVertices[i] == internalTargetCell)
+				{
+					// If there are parallel edges going between two vertices and not all are in the same direction
+					// you can have navigated across one direction when doing the cycle reversal that isn't the same
+					// direction as the first real edge in the array above. When that happens the if above catches
+					// that and we correct the target cell before continuing.
+					// This branch only detects this single case
+					targetCell = layout.getVisibleTerminal(
+							realEdge, true);
+					internalTargetCell = this.vertexMapper.get(targetCell);
+				}
+				
+				if (internalTargetCell != null
+						&& internalVertices[i] != internalTargetCell)
+				{
+					internalEdge.target = internalTargetCell;
+
+					if (internalTargetCell.connectsAsTarget.length == 0)
+					{
+						internalTargetCell.connectsAsTarget = [];
+					}
+
+					if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0)
+					{
+						internalTargetCell.connectsAsTarget.push(internalEdge);
+					}
+				}
+			}
+		}
+
+		// Use the temp variable in the internal nodes to mark this
+		// internal vertex as having been visited.
+		internalVertices[i].temp[0] = 1;
+	}
+};
+
+/**
+ * Variable: maxRank
+ *
+ * Stores the largest rank number allocated
+ */
+mxGraphHierarchyModel.prototype.maxRank = null;
+
+/**
+ * Variable: vertexMapper
+ *
+ * Map from graph vertices to internal model nodes.
+ */
+mxGraphHierarchyModel.prototype.vertexMapper = null;
+
+/**
+ * Variable: edgeMapper
+ *
+ * Map from graph edges to internal model edges
+ */
+mxGraphHierarchyModel.prototype.edgeMapper = null;
+
+/**
+ * Variable: ranks
+ *
+ * Mapping from rank number to actual rank
+ */
+mxGraphHierarchyModel.prototype.ranks = null;
+
+/**
+ * Variable: roots
+ *
+ * Store of roots of this hierarchy model, these are real graph cells, not
+ * internal cells
+ */
+mxGraphHierarchyModel.prototype.roots = null;
+
+/**
+ * Variable: parent
+ *
+ * The parent cell whose children are being laid out
+ */
+mxGraphHierarchyModel.prototype.parent = null;
+
+/**
+ * Variable: dfsCount
+ *
+ * Count of the number of times the ancestor dfs has been used.
+ */
+mxGraphHierarchyModel.prototype.dfsCount = 0;
+
+/**
+ * Variable: SOURCESCANSTARTRANK
+ *
+ * High value to start source layering scan rank value from.
+ */
+mxGraphHierarchyModel.prototype.SOURCESCANSTARTRANK = 100000000;
+
+/**
+ * Variable: tightenToSource
+ *
+ * Whether or not to tighten the assigned ranks of vertices up towards
+ * the source cells.
+ */
+mxGraphHierarchyModel.prototype.tightenToSource = false;
+
+/**
+ * Function: createInternalCells
+ *
+ * Creates all edges in the internal model
+ *
+ * Parameters:
+ *
+ * layout - Reference to the <mxHierarchicalLayout> algorithm.
+ * vertices - Array of <mxCells> that represent the vertices whom are to
+ * have an internal representation created.
+ * internalVertices - The array of <mxGraphHierarchyNodes> to have their
+ * information filled in using the real vertices.
+ */
+mxGraphHierarchyModel.prototype.createInternalCells = function(layout, vertices, internalVertices)
+{
+	var graph = layout.getGraph();
+
+	// Create internal edges
+	for (var i = 0; i < vertices.length; i++)
+	{
+		internalVertices[i] = new mxGraphHierarchyNode(vertices[i]);
+		this.vertexMapper.put(vertices[i], internalVertices[i]);
+
+		// If the layout is deterministic, order the cells
+		//List outgoingCells = graph.getNeighbours(vertices[i], deterministic);
+		var conns = layout.getEdges(vertices[i]);
+		internalVertices[i].connectsAsSource = [];
+
+		// Create internal edges, but don't do any rank assignment yet
+		// First use the information from the greedy cycle remover to
+		// invert the leftward edges internally
+		for (var j = 0; j < conns.length; j++)
+		{
+			var cell = layout.getVisibleTerminal(conns[j], false);
+
+			// Looking for outgoing edges only
+			if (cell != vertices[i] && layout.graph.model.isVertex(cell) &&
+					!layout.isVertexIgnored(cell))
+			{
+				// We process all edge between this source and its targets
+				// If there are edges going both ways, we need to collect
+				// them all into one internal edges to avoid looping problems
+				// later. We assume this direction (source -> target) is the 
+				// natural direction if at least half the edges are going in
+				// that direction.
+
+				// The check below for edges[0] being in the vertex mapper is
+				// in case we've processed this the other way around
+				// (target -> source) and the number of edges in each direction
+				// are the same. All the graph edges will have been assigned to
+				// an internal edge going the other way, so we don't want to 
+				// process them again
+				var undirectedEdges = layout.getEdgesBetween(vertices[i],
+						cell, false);
+				var directedEdges = layout.getEdgesBetween(vertices[i],
+						cell, true);
+				
+				if (undirectedEdges != null &&
+						undirectedEdges.length > 0 &&
+						this.edgeMapper.get(undirectedEdges[0]) == null &&
+						directedEdges.length * 2 >= undirectedEdges.length)
+				{
+					var internalEdge = new mxGraphHierarchyEdge(undirectedEdges);
+
+					for (var k = 0; k < undirectedEdges.length; k++)
+					{
+						var edge = undirectedEdges[k];
+						this.edgeMapper.put(edge, internalEdge);
+
+						// Resets all point on the edge and disables the edge style
+						// without deleting it from the cell style
+						graph.resetEdge(edge);
+
+					    if (layout.disableEdgeStyle)
+					    {
+					    	layout.setEdgeStyleEnabled(edge, false);
+					    	layout.setOrthogonalEdge(edge,true);
+					    }
+					}
+
+					internalEdge.source = internalVertices[i];
+
+					if (mxUtils.indexOf(internalVertices[i].connectsAsSource, internalEdge) < 0)
+					{
+						internalVertices[i].connectsAsSource.push(internalEdge);
+					}
+				}
+			}
+		}
+
+		// Ensure temp variable is cleared from any previous use
+		internalVertices[i].temp[0] = 0;
+	}
+};
+
+/**
+ * Function: initialRank
+ *
+ * Basic determination of minimum layer ranking by working from from sources
+ * or sinks and working through each node in the relevant edge direction.
+ * Starting at the sinks is basically a longest path layering algorithm.
+*/
+mxGraphHierarchyModel.prototype.initialRank = function()
+{
+	var startNodes = [];
+
+	if (this.roots != null)
+	{
+		for (var i = 0; i < this.roots.length; i++)
+		{
+			var internalNode = this.vertexMapper.get(this.roots[i]);
+
+			if (internalNode != null)
+			{
+				startNodes.push(internalNode);
+			}
+		}
+	}
+
+	var internalNodes = this.vertexMapper.getValues();
+	
+	for (var i=0; i < internalNodes.length; i++)
+	{
+		// Mark the node as not having had a layer assigned
+		internalNodes[i].temp[0] = -1;
+	}
+
+	var startNodesCopy = startNodes.slice();
+
+	while (startNodes.length > 0)
+	{
+		var internalNode = startNodes[0];
+		var layerDeterminingEdges;
+		var edgesToBeMarked;
+
+		layerDeterminingEdges = internalNode.connectsAsTarget;
+		edgesToBeMarked = internalNode.connectsAsSource;
+
+		// flag to keep track of whether or not all layer determining
+		// edges have been scanned
+		var allEdgesScanned = true;
+
+		// Work out the layer of this node from the layer determining
+		// edges. The minimum layer number of any node connected by one of
+		// the layer determining edges variable
+		var minimumLayer = this.SOURCESCANSTARTRANK;
+
+		for (var i = 0; i < layerDeterminingEdges.length; i++)
+		{
+			var internalEdge = layerDeterminingEdges[i];
+
+			if (internalEdge.temp[0] == 5270620)
+			{
+				// This edge has been scanned, get the layer of the
+				// node on the other end
+				var otherNode = internalEdge.source;
+				minimumLayer = Math.min(minimumLayer, otherNode.temp[0] - 1);
+			}
+			else
+			{
+				allEdgesScanned = false;
+
+				break;
+			}
+		}
+
+		// If all edge have been scanned, assign the layer, mark all
+		// edges in the other direction and remove from the nodes list
+		if (allEdgesScanned)
+		{
+			internalNode.temp[0] = minimumLayer;
+			this.maxRank = Math.min(this.maxRank, minimumLayer);
+
+			if (edgesToBeMarked != null)
+			{
+				for (var i = 0; i < edgesToBeMarked.length; i++)
+				{
+					var internalEdge = edgesToBeMarked[i];
+
+					// Assign unique stamp ( y/m/d/h )
+					internalEdge.temp[0] = 5270620;
+
+					// Add node on other end of edge to LinkedList of
+					// nodes to be analysed
+					var otherNode = internalEdge.target;
+
+					// Only add node if it hasn't been assigned a layer
+					if (otherNode.temp[0] == -1)
+					{
+						startNodes.push(otherNode);
+
+						// Mark this other node as neither being
+						// unassigned nor assigned so it isn't
+						// added to this list again, but it's
+						// layer isn't used in any calculation.
+						otherNode.temp[0] = -2;
+					}
+				}
+			}
+
+			startNodes.shift();
+		}
+		else
+		{
+			// Not all the edges have been scanned, get to the back of
+			// the class and put the dunces cap on
+			var removedCell = startNodes.shift();
+			startNodes.push(internalNode);
+
+			if (removedCell == internalNode && startNodes.length == 1)
+			{
+				// This is an error condition, we can't get out of
+				// this loop. It could happen for more than one node
+				// but that's a lot harder to detect. Log the error
+				// TODO make log comment
+				break;
+			}
+		}
+	}
+
+	// Normalize the ranks down from their large starting value to place
+	// at least 1 sink on layer 0
+	for (var i=0; i < internalNodes.length; i++)
+	{
+		// Mark the node as not having had a layer assigned
+		internalNodes[i].temp[0] -= this.maxRank;
+	}
+	
+	// Tighten the rank 0 nodes as far as possible
+	for ( var i = 0; i < startNodesCopy.length; i++)
+	{
+		var internalNode = startNodesCopy[i];
+		var currentMaxLayer = 0;
+		var layerDeterminingEdges = internalNode.connectsAsSource;
+
+		for ( var j = 0; j < layerDeterminingEdges.length; j++)
+		{
+			var internalEdge = layerDeterminingEdges[j];
+			var otherNode = internalEdge.target;
+			internalNode.temp[0] = Math.max(currentMaxLayer,
+					otherNode.temp[0] + 1);
+			currentMaxLayer = internalNode.temp[0];
+		}
+	}
+	
+	// Reset the maxRank to that which would be expected for a from-sink
+	// scan
+	this.maxRank = this.SOURCESCANSTARTRANK - this.maxRank;
+};
+
+/**
+ * Function: fixRanks
+ *
+ * Fixes the layer assignments to the values stored in the nodes. Also needs
+ * to create dummy nodes for edges that cross layers.
+ */
+mxGraphHierarchyModel.prototype.fixRanks = function()
+{
+	var rankList = [];
+	this.ranks = [];
+
+	for (var i = 0; i < this.maxRank + 1; i++)
+	{
+		rankList[i] = [];
+		this.ranks[i] = rankList[i];
+	}
+
+	// Perform a DFS to obtain an initial ordering for each rank.
+	// Without doing this you would end up having to process
+	// crossings for a standard tree.
+	var rootsArray = null;
+
+	if (this.roots != null)
+	{
+		var oldRootsArray = this.roots;
+		rootsArray = [];
+
+		for (var i = 0; i < oldRootsArray.length; i++)
+		{
+			var cell = oldRootsArray[i];
+			var internalNode = this.vertexMapper.get(cell);
+			rootsArray[i] = internalNode;
+		}
+	}
+
+	this.visit(function(parent, node, edge, layer, seen)
+	{
+		if (seen == 0 && node.maxRank < 0 && node.minRank < 0)
+		{
+			rankList[node.temp[0]].push(node);
+			node.maxRank = node.temp[0];
+			node.minRank = node.temp[0];
+
+			// Set temp[0] to the nodes position in the rank
+			node.temp[0] = rankList[node.maxRank].length - 1;
+		}
+
+		if (parent != null && edge != null)
+		{
+			var parentToCellRankDifference = parent.maxRank - node.maxRank;
+
+			if (parentToCellRankDifference > 1)
+			{
+				// There are ranks in between the parent and current cell
+				edge.maxRank = parent.maxRank;
+				edge.minRank = node.maxRank;
+				edge.temp = [];
+				edge.x = [];
+				edge.y = [];
+
+				for (var i = edge.minRank + 1; i < edge.maxRank; i++)
+				{
+					// The connecting edge must be added to the
+					// appropriate ranks
+					rankList[i].push(edge);
+					edge.setGeneralPurposeVariable(i, rankList[i]
+							.length - 1);
+				}
+			}
+		}
+	}, rootsArray, false, null);
+};
+
+/**
+ * Function: visit
+ *
+ * A depth first search through the internal heirarchy model.
+ *
+ * Parameters:
+ *
+ * visitor - The visitor function pattern to be called for each node.
+ * trackAncestors - Whether or not the search is to keep track all nodes
+ * directly above this one in the search path.
+ */
+mxGraphHierarchyModel.prototype.visit = function(visitor, dfsRoots, trackAncestors, seenNodes)
+{
+	// Run dfs through on all roots
+	if (dfsRoots != null)
+	{
+		for (var i = 0; i < dfsRoots.length; i++)
+		{
+			var internalNode = dfsRoots[i];
+
+			if (internalNode != null)
+			{
+				if (seenNodes == null)
+				{
+					seenNodes = new Object();
+				}
+
+				if (trackAncestors)
+				{
+					// Set up hash code for root
+					internalNode.hashCode = [];
+					internalNode.hashCode[0] = this.dfsCount;
+					internalNode.hashCode[1] = i;
+					this.extendedDfs(null, internalNode, null, visitor, seenNodes,
+							internalNode.hashCode, i, 0);
+				}
+				else
+				{
+					this.dfs(null, internalNode, null, visitor, seenNodes, 0);
+				}
+			}
+		}
+
+		this.dfsCount++;
+	}
+};
+
+/**
+ * Function: dfs
+ *
+ * Performs a depth first search on the internal hierarchy model
+ *
+ * Parameters:
+ *
+ * parent - the parent internal node of the current internal node
+ * root - the current internal node
+ * connectingEdge - the internal edge connecting the internal node and the parent
+ * internal node, if any
+ * visitor - the visitor pattern to be called for each node
+ * seen - a set of all nodes seen by this dfs a set of all of the
+ * ancestor node of the current node
+ * layer - the layer on the dfs tree ( not the same as the model ranks )
+ */
+mxGraphHierarchyModel.prototype.dfs = function(parent, root, connectingEdge, visitor, seen, layer)
+{
+	if (root != null)
+	{
+		var rootId = root.id;
+
+		if (seen[rootId] == null)
+		{
+			seen[rootId] = root;
+			visitor(parent, root, connectingEdge, layer, 0);
+
+			// Copy the connects as source list so that visitors
+			// can change the original for edge direction inversions
+			var outgoingEdges = root.connectsAsSource.slice();
+			
+			for (var i = 0; i< outgoingEdges.length; i++)
+			{
+				var internalEdge = outgoingEdges[i];
+				var targetNode = internalEdge.target;
+
+				// Root check is O(|roots|)
+				this.dfs(root, targetNode, internalEdge, visitor, seen,
+						layer + 1);
+			}
+		}
+		else
+		{
+			// Use the int field to indicate this node has been seen
+			visitor(parent, root, connectingEdge, layer, 1);
+		}
+	}
+};
+
+/**
+ * Function: extendedDfs
+ *
+ * Performs a depth first search on the internal hierarchy model. This dfs
+ * extends the default version by keeping track of cells ancestors, but it
+ * should be only used when necessary because of it can be computationally
+ * intensive for deep searches.
+ *
+ * Parameters:
+ *
+ * parent - the parent internal node of the current internal node
+ * root - the current internal node
+ * connectingEdge - the internal edge connecting the internal node and the parent
+ * internal node, if any
+ * visitor - the visitor pattern to be called for each node
+ * seen - a set of all nodes seen by this dfs
+ * ancestors - the parent hash code
+ * childHash - the new hash code for this node
+ * layer - the layer on the dfs tree ( not the same as the model ranks )
+ */
+mxGraphHierarchyModel.prototype.extendedDfs = function(parent, root, connectingEdge, visitor, seen, ancestors, childHash, layer)
+{
+	// Explanation of custom hash set. Previously, the ancestors variable
+	// was passed through the dfs as a HashSet. The ancestors were copied
+	// into a new HashSet and when the new child was processed it was also
+	// added to the set. If the current node was in its ancestor list it
+	// meant there is a cycle in the graph and this information is passed
+	// to the visitor.visit() in the seen parameter. The HashSet clone was
+	// very expensive on CPU so a custom hash was developed using primitive
+	// types. temp[] couldn't be used so hashCode[] was added to each node.
+	// Each new child adds another int to the array, copying the prefix
+	// from its parent. Child of the same parent add different ints (the
+	// limit is therefore 2^32 children per parent...). If a node has a
+	// child with the hashCode already set then the child code is compared
+	// to the same portion of the current nodes array. If they match there
+	// is a loop.
+	// Note that the basic mechanism would only allow for 1 use of this
+	// functionality, so the root nodes have two ints. The second int is
+	// incremented through each node root and the first is incremented
+	// through each run of the dfs algorithm (therefore the dfs is not
+	// thread safe). The hash code of each node is set if not already set,
+	// or if the first int does not match that of the current run.
+	if (root != null)
+	{
+		if (parent != null)
+		{
+			// Form this nodes hash code if necessary, that is, if the
+			// hashCode variable has not been initialized or if the
+			// start of the parent hash code does not equal the start of
+			// this nodes hash code, indicating the code was set on a
+			// previous run of this dfs.
+			if (root.hashCode == null ||
+				root.hashCode[0] != parent.hashCode[0])
+			{
+				var hashCodeLength = parent.hashCode.length + 1;
+				root.hashCode = parent.hashCode.slice();
+				root.hashCode[hashCodeLength - 1] = childHash;
+			}
+		}
+
+		var rootId = root.id;
+
+		if (seen[rootId] == null)
+		{
+			seen[rootId] = root;
+			visitor(parent, root, connectingEdge, layer, 0);
+
+			// Copy the connects as source list so that visitors
+			// can change the original for edge direction inversions
+			var outgoingEdges = root.connectsAsSource.slice();
+
+			for (var i = 0; i < outgoingEdges.length; i++)
+			{
+				var internalEdge = outgoingEdges[i];
+				var targetNode = internalEdge.target;
+
+				// Root check is O(|roots|)
+				this.extendedDfs(root, targetNode, internalEdge, visitor, seen,
+						root.hashCode, i, layer + 1);
+			}
+		}
+		else
+		{
+			// Use the int field to indicate this node has been seen
+			visitor(parent, root, connectingEdge, layer, 1);
+		}
+	}
+};
+/**
+ * Copyright (c) 2006-2016, JGraph Ltd
+ * Copyright (c) 2006-2016, Gaudenz Alder
+ */
+/**
+ * Class: mxSwimlaneModel
+ *
+ * Internal model of a hierarchical graph. This model stores nodes and edges
+ * equivalent to the real graph nodes and edges, but also stores the rank of the
+ * cells, the order within the ranks and the new candidate locations of cells.
+ * The internal model also reverses edge direction were appropriate , ignores
+ * self-loop and groups parallels together under one edge object.
+ *
+ * Constructor: mxSwimlaneModel
+ *
+ * Creates an internal ordered graph model using the vertices passed in. If
+ * there are any, leftward edge need to be inverted in the internal model
+ *
+ * Arguments:
+ *
+ * graph - the facade describing the graph to be operated on
+ * vertices - the vertices for this hierarchy
+ * ordered - whether or not the vertices are already ordered
+ * deterministic - whether or not this layout should be deterministic on each
+ * tightenToSource - whether or not to tighten vertices towards the sources
+ * scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
+ * usage
+ */
+function mxSwimlaneModel(layout, vertices, roots, parent, tightenToSource)
+{
+	var graph = layout.getGraph();
+	this.tightenToSource = tightenToSource;
+	this.roots = roots;
+	this.parent = parent;
+
+	// map of cells to internal cell needed for second run through
+	// to setup the sink of edges correctly
+	this.vertexMapper = new mxDictionary();
+	this.edgeMapper = new mxDictionary();
+	this.maxRank = 0;
+	var internalVertices = [];
+
+	if (vertices == null)
+	{
+		vertices = this.graph.getChildVertices(parent);
+	}
+
+	this.maxRank = this.SOURCESCANSTARTRANK;
+	// map of cells to internal cell needed for second run through
+	// to setup the sink of edges correctly. Guess size by number
+	// of edges is roughly same as number of vertices.
+	this.createInternalCells(layout, vertices, internalVertices);
+
+	// Go through edges set their sink values. Also check the
+	// ordering if and invert edges if necessary
+	for (var i = 0; i < vertices.length; i++)
+	{
+		var edges = internalVertices[i].connectsAsSource;
+
+		for (var j = 0; j < edges.length; j++)
+		{
+			var internalEdge = edges[j];
+			var realEdges = internalEdge.edges;
+
+			// Only need to process the first real edge, since
+			// all the edges connect to the same other vertex
+			if (realEdges != null && realEdges.length > 0)
+			{
+				var realEdge = realEdges[0];
+				var targetCell = layout.getVisibleTerminal(
+						realEdge, false);
+				var internalTargetCell = this.vertexMapper.get(targetCell);
+
+				if (internalVertices[i] == internalTargetCell)
+				{
+					// If there are parallel edges going between two vertices and not all are in the same direction
+					// you can have navigated across one direction when doing the cycle reversal that isn't the same
+					// direction as the first real edge in the array above. When that happens the if above catches
+					// that and we correct the target cell before continuing.
+					// This branch only detects this single case
+					targetCell = layout.getVisibleTerminal(
+							realEdge, true);
+					internalTargetCell = this.vertexMapper.get(targetCell);
+				}
+
+				if (internalTargetCell != null
+						&& internalVertices[i] != internalTargetCell)
+				{
+					internalEdge.target = internalTargetCell;
+
+					if (internalTargetCell.connectsAsTarget.length == 0)
+					{
+						internalTargetCell.connectsAsTarget = [];
+					}
+
+					if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0)
+					{
+						internalTargetCell.connectsAsTarget.push(internalEdge);
+					}
+				}
+			}
+		}
+
+		// Use the temp variable in the internal nodes to mark this
+		// internal vertex as having been visited.
+		internalVertices[i].temp[0] = 1;
+	}
+};
+
+/**
+ * Variable: maxRank
+ *
+ * Stores the largest rank number allocated
+ */
+mxSwimlaneModel.prototype.maxRank = null;
+
+/**
+ * Variable: vertexMapper
+ *
+ * Map from graph vertices to internal model nodes.
+ */
+mxSwimlaneModel.prototype.vertexMapper = null;
+
+/**
+ * Variable: edgeMapper
+ *
+ * Map from graph edges to internal model edges
+ */
+mxSwimlaneModel.prototype.edgeMapper = null;
+
+/**
+ * Variable: ranks
+ *
+ * Mapping from rank number to actual rank
+ */
+mxSwimlaneModel.prototype.ranks = null;
+
+/**
+ * Variable: roots
+ *
+ * Store of roots of this hierarchy model, these are real graph cells, not
+ * internal cells
+ */
+mxSwimlaneModel.prototype.roots = null;
+
+/**
+ * Variable: parent
+ *
+ * The parent cell whose children are being laid out
+ */
+mxSwimlaneModel.prototype.parent = null;
+
+/**
+ * Variable: dfsCount
+ *
+ * Count of the number of times the ancestor dfs has been used.
+ */
+mxSwimlaneModel.prototype.dfsCount = 0;
+
+/**
+ * Variable: SOURCESCANSTARTRANK
+ *
+ * High value to start source layering scan rank value from.
+ */
+mxSwimlaneModel.prototype.SOURCESCANSTARTRANK = 100000000;
+
+/**
+ * Variable: tightenToSource
+ *
+ * Whether or not to tighten the assigned ranks of vertices up towards
+ * the source cells.
+ */
+mxGraphHierarchyModel.prototype.tightenToSource = false;
+
+/**
+ * Variable: ranksPerGroup
+ *
+ * An array of the number of ranks within each swimlane
+ */
+mxSwimlaneModel.prototype.ranksPerGroup = null;
+
+/**
+ * Function: createInternalCells
+ *
+ * Creates all edges in the internal model
+ *
+ * Parameters:
+ *
+ * layout - Reference to the <mxHierarchicalLayout> algorithm.
+ * vertices - Array of <mxCells> that represent the vertices whom are to
+ * have an internal representation created.
+ * internalVertices - The array of <mxGraphHierarchyNodes> to have their
+ * information filled in using the real vertices.
+ */
+mxSwimlaneModel.prototype.createInternalCells = function(layout, vertices, internalVertices)
+{
+	var graph = layout.getGraph();
+	var swimlanes = layout.swimlanes;
+
+	// Create internal edges
+	for (var i = 0; i < vertices.length; i++)
+	{
+		internalVertices[i] = new mxGraphHierarchyNode(vertices[i]);
+		this.vertexMapper.put(vertices[i], internalVertices[i]);
+		internalVertices[i].swimlaneIndex = -1;
+
+		for (var ii = 0; ii < swimlanes.length; ii++)
+		{
+			if (graph.model.getParent(vertices[i]) == swimlanes[ii])
+			{
+				internalVertices[i].swimlaneIndex = ii;
+				break;
+			}
+		}
+
+		// If the layout is deterministic, order the cells
+		//List outgoingCells = graph.getNeighbours(vertices[i], deterministic);
+		var conns = layout.getEdges(vertices[i]);
+		internalVertices[i].connectsAsSource = [];
+
+		// Create internal edges, but don't do any rank assignment yet
+		// First use the information from the greedy cycle remover to
+		// invert the leftward edges internally
+		for (var j = 0; j < conns.length; j++)
+		{
+			var cell = layout.getVisibleTerminal(conns[j], false);
+
+			// Looking for outgoing edges only
+			if (cell != vertices[i] && layout.graph.model.isVertex(cell) &&
+					!layout.isVertexIgnored(cell))
+			{
+				// We process all edge between this source and its targets
+				// If there are edges going both ways, we need to collect
+				// them all into one internal edges to avoid looping problems
+				// later. We assume this direction (source -> target) is the 
+				// natural direction if at least half the edges are going in
+				// that direction.
+
+				// The check below for edges[0] being in the vertex mapper is
+				// in case we've processed this the other way around
+				// (target -> source) and the number of edges in each direction
+				// are the same. All the graph edges will have been assigned to
+				// an internal edge going the other way, so we don't want to 
+				// process them again
+				var undirectedEdges = layout.getEdgesBetween(vertices[i],
+						cell, false);
+				var directedEdges = layout.getEdgesBetween(vertices[i],
+						cell, true);
+				
+				if (undirectedEdges != null &&
+						undirectedEdges.length > 0 &&
+						this.edgeMapper.get(undirectedEdges[0]) == null &&
+						directedEdges.length * 2 >= undirectedEdges.length)
+				{
+					var internalEdge = new mxGraphHierarchyEdge(undirectedEdges);
+
+					for (var k = 0; k < undirectedEdges.length; k++)
+					{
+						var edge = undirectedEdges[k];
+						this.edgeMapper.put(edge, internalEdge);
+
+						// Resets all point on the edge and disables the edge style
+						// without deleting it from the cell style
+						graph.resetEdge(edge);
+
+					    if (layout.disableEdgeStyle)
+					    {
+					    	layout.setEdgeStyleEnabled(edge, false);
+					    	layout.setOrthogonalEdge(edge,true);
+					    }
+					}
+
+					internalEdge.source = internalVertices[i];
+
+					if (mxUtils.indexOf(internalVertices[i].connectsAsSource, internalEdge) < 0)
+					{
+						internalVertices[i].connectsAsSource.push(internalEdge);
+					}
+				}
+			}
+		}
+
+		// Ensure temp variable is cleared from any previous use
+		internalVertices[i].temp[0] = 0;
+	}
+};
+
+/**
+ * Function: initialRank
+ *
+ * Basic determination of minimum layer ranking by working from from sources
+ * or sinks and working through each node in the relevant edge direction.
+ * Starting at the sinks is basically a longest path layering algorithm.
+*/
+mxSwimlaneModel.prototype.initialRank = function()
+{
+	this.ranksPerGroup = [];
+	
+	var startNodes = [];
+	var seen = new Object();
+
+	if (this.roots != null)
+	{
+		for (var i = 0; i < this.roots.length; i++)
+		{
+			var internalNode = this.vertexMapper.get(this.roots[i]);
+			this.maxChainDfs(null, internalNode, null, seen, 0);
+
+			if (internalNode != null)
+			{
+				startNodes.push(internalNode);
+			}
+		}
+	}
+
+	// Calculate the lower and upper rank bounds of each swimlane
+	var lowerRank = [];
+	var upperRank = [];
+	
+	for (var i = this.ranksPerGroup.length - 1; i >= 0; i--)
+	{
+		if (i == this.ranksPerGroup.length - 1)
+		{
+			lowerRank[i] = 0;
+		}
+		else
+		{
+			lowerRank[i] = upperRank[i+1] + 1;
+		}
+		
+		upperRank[i] = lowerRank[i] + this.ranksPerGroup[i];
+	}
+	
+	this.maxRank = upperRank[0];
+
+	var internalNodes = this.vertexMapper.getValues();
+	
+	for (var i=0; i < internalNodes.length; i++)
+	{
+		// Mark the node as not having had a layer assigned
+		internalNodes[i].temp[0] = -1;
+	}
+
+	var startNodesCopy = startNodes.slice();
+	
+	while (startNodes.length > 0)
+	{
+		var internalNode = startNodes[0];
+		var layerDeterminingEdges;
+		var edgesToBeMarked;
+
+		layerDeterminingEdges = internalNode.connectsAsTarget;
+		edgesToBeMarked = internalNode.connectsAsSource;
+
+		// flag to keep track of whether or not all layer determining
+		// edges have been scanned
+		var allEdgesScanned = true;
+
+		// Work out the layer of this node from the layer determining
+		// edges. The minimum layer number of any node connected by one of
+		// the layer determining edges variable
+		var minimumLayer = upperRank[0];
+
+		for (var i = 0; i < layerDeterminingEdges.length; i++)
+		{
+			var internalEdge = layerDeterminingEdges[i];
+
+			if (internalEdge.temp[0] == 5270620)
+			{
+				// This edge has been scanned, get the layer of the
+				// node on the other end
+				var otherNode = internalEdge.source;
+				minimumLayer = Math.min(minimumLayer, otherNode.temp[0] - 1);
+			}
+			else
+			{
+				allEdgesScanned = false;
+
+				break;
+			}
+		}
+
+		// If all edge have been scanned, assign the layer, mark all
+		// edges in the other direction and remove from the nodes list
+		if (allEdgesScanned)
+		{
+			if (minimumLayer > upperRank[internalNode.swimlaneIndex])
+			{
+				minimumLayer = upperRank[internalNode.swimlaneIndex];
+			}
+
+			internalNode.temp[0] = minimumLayer;
+
+			if (edgesToBeMarked != null)
+			{
+				for (var i = 0; i < edgesToBeMarked.length; i++)
+				{
+					var internalEdge = edgesToBeMarked[i];
+
+					// Assign unique stamp ( y/m/d/h )
+					internalEdge.temp[0] = 5270620;
+
+					// Add node on other end of edge to LinkedList of
+					// nodes to be analysed
+					var otherNode = internalEdge.target;
+
+					// Only add node if it hasn't been assigned a layer
+					if (otherNode.temp[0] == -1)
+					{
+						startNodes.push(otherNode);
+
+						// Mark this other node as neither being
+						// unassigned nor assigned so it isn't
+						// added to this list again, but it's
+						// layer isn't used in any calculation.
+						otherNode.temp[0] = -2;
+					}
+				}
+			}
+
+			startNodes.shift();
+		}
+		else
+		{
+			// Not all the edges have been scanned, get to the back of
+			// the class and put the dunces cap on
+			var removedCell = startNodes.shift();
+			startNodes.push(internalNode);
+
+			if (removedCell == internalNode && startNodes.length == 1)
+			{
+				// This is an error condition, we can't get out of
+				// this loop. It could happen for more than one node
+				// but that's a lot harder to detect. Log the error
+				// TODO make log comment
+				break;
+			}
+		}
+	}
+
+	// Normalize the ranks down from their large starting value to place
+	// at least 1 sink on layer 0
+//	for (var key in this.vertexMapper)
+//	{
+//		var internalNode = this.vertexMapper[key];
+//		// Mark the node as not having had a layer assigned
+//		internalNode.temp[0] -= this.maxRank;
+//	}
+	
+	// Tighten the rank 0 nodes as far as possible
+//	for ( var i = 0; i < startNodesCopy.length; i++)
+//	{
+//		var internalNode = startNodesCopy[i];
+//		var currentMaxLayer = 0;
+//		var layerDeterminingEdges = internalNode.connectsAsSource;
+//
+//		for ( var j = 0; j < layerDeterminingEdges.length; j++)
+//		{
+//			var internalEdge = layerDeterminingEdges[j];
+//			var otherNode = internalEdge.target;
+//			internalNode.temp[0] = Math.max(currentMaxLayer,
+//					otherNode.temp[0] + 1);
+//			currentMaxLayer = internalNode.temp[0];
+//		}
+//	}
+};
+
+/**
+ * Function: maxChainDfs
+ *
+ * Performs a depth first search on the internal hierarchy model. This dfs
+ * extends the default version by keeping track of chains within groups.
+ * Any cycles should be removed prior to running, but previously seen cells
+ * are ignored.
+ *
+ * Parameters:
+ *
+ * parent - the parent internal node of the current internal node
+ * root - the current internal node
+ * connectingEdge - the internal edge connecting the internal node and the parent
+ * internal node, if any
+ * seen - a set of all nodes seen by this dfs
+ * chainCount - the number of edges in the chain of vertices going through
+ * the current swimlane
+ */
+mxSwimlaneModel.prototype.maxChainDfs = function(parent, root, connectingEdge, seen, chainCount)
+{
+	if (root != null)
+	{
+		var rootId = mxCellPath.create(root.cell);
+
+		if (seen[rootId] == null)
+		{
+			seen[rootId] = root;
+			var slIndex = root.swimlaneIndex;
+			
+			if (this.ranksPerGroup[slIndex] == null || this.ranksPerGroup[slIndex] < chainCount)
+			{
+				this.ranksPerGroup[slIndex] = chainCount;
+			}
+
+			// Copy the connects as source list so that visitors
+			// can change the original for edge direction inversions
+			var outgoingEdges = root.connectsAsSource.slice();
+
+			for (var i = 0; i < outgoingEdges.length; i++)
+			{
+				var internalEdge = outgoingEdges[i];
+				var targetNode = internalEdge.target;
+
+				// Only navigate in source->target direction within the same
+				// swimlane, or from a lower index swimlane to a higher one
+				if (root.swimlaneIndex < targetNode.swimlaneIndex)
+				{
+					this.maxChainDfs(root, targetNode, internalEdge, mxUtils.clone(seen, null , true), 0);
+				}
+				else if (root.swimlaneIndex == targetNode.swimlaneIndex)
+				{
+					this.maxChainDfs(root, targetNode, internalEdge, mxUtils.clone(seen, null , true), chainCount + 1);
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: fixRanks
+ *
+ * Fixes the layer assignments to the values stored in the nodes. Also needs
+ * to create dummy nodes for edges that cross layers.
+ */
+mxSwimlaneModel.prototype.fixRanks = function()
+{
+	var rankList = [];
+	this.ranks = [];
+
+	for (var i = 0; i < this.maxRank + 1; i++)
+	{
+		rankList[i] = [];
+		this.ranks[i] = rankList[i];
+	}
+
+	// Perform a DFS to obtain an initial ordering for each rank.
+	// Without doing this you would end up having to process
+	// crossings for a standard tree.
+	var rootsArray = null;
+
+	if (this.roots != null)
+	{
+		var oldRootsArray = this.roots;
+		rootsArray = [];
+
+		for (var i = 0; i < oldRootsArray.length; i++)
+		{
+			var cell = oldRootsArray[i];
+			var internalNode = this.vertexMapper.get(cell);
+			rootsArray[i] = internalNode;
+		}
+	}
+
+	this.visit(function(parent, node, edge, layer, seen)
+	{
+		if (seen == 0 && node.maxRank < 0 && node.minRank < 0)
+		{
+			rankList[node.temp[0]].push(node);
+			node.maxRank = node.temp[0];
+			node.minRank = node.temp[0];
+
+			// Set temp[0] to the nodes position in the rank
+			node.temp[0] = rankList[node.maxRank].length - 1;
+		}
+
+		if (parent != null && edge != null)
+		{
+			var parentToCellRankDifference = parent.maxRank - node.maxRank;
+
+			if (parentToCellRankDifference > 1)
+			{
+				// There are ranks in between the parent and current cell
+				edge.maxRank = parent.maxRank;
+				edge.minRank = node.maxRank;
+				edge.temp = [];
+				edge.x = [];
+				edge.y = [];
+
+				for (var i = edge.minRank + 1; i < edge.maxRank; i++)
+				{
+					// The connecting edge must be added to the
+					// appropriate ranks
+					rankList[i].push(edge);
+					edge.setGeneralPurposeVariable(i, rankList[i]
+							.length - 1);
+				}
+			}
+		}
+	}, rootsArray, false, null);
+};
+
+/**
+ * Function: visit
+ *
+ * A depth first search through the internal heirarchy model.
+ *
+ * Parameters:
+ *
+ * visitor - The visitor function pattern to be called for each node.
+ * trackAncestors - Whether or not the search is to keep track all nodes
+ * directly above this one in the search path.
+ */
+mxSwimlaneModel.prototype.visit = function(visitor, dfsRoots, trackAncestors, seenNodes)
+{
+	// Run dfs through on all roots
+	if (dfsRoots != null)
+	{
+		for (var i = 0; i < dfsRoots.length; i++)
+		{
+			var internalNode = dfsRoots[i];
+
+			if (internalNode != null)
+			{
+				if (seenNodes == null)
+				{
+					seenNodes = new Object();
+				}
+
+				if (trackAncestors)
+				{
+					// Set up hash code for root
+					internalNode.hashCode = [];
+					internalNode.hashCode[0] = this.dfsCount;
+					internalNode.hashCode[1] = i;
+					this.extendedDfs(null, internalNode, null, visitor, seenNodes,
+							internalNode.hashCode, i, 0);
+				}
+				else
+				{
+					this.dfs(null, internalNode, null, visitor, seenNodes, 0);
+				}
+			}
+		}
+
+		this.dfsCount++;
+	}
+};
+
+/**
+ * Function: dfs
+ *
+ * Performs a depth first search on the internal hierarchy model
+ *
+ * Parameters:
+ *
+ * parent - the parent internal node of the current internal node
+ * root - the current internal node
+ * connectingEdge - the internal edge connecting the internal node and the parent
+ * internal node, if any
+ * visitor - the visitor pattern to be called for each node
+ * seen - a set of all nodes seen by this dfs a set of all of the
+ * ancestor node of the current node
+ * layer - the layer on the dfs tree ( not the same as the model ranks )
+ */
+mxSwimlaneModel.prototype.dfs = function(parent, root, connectingEdge, visitor, seen, layer)
+{
+	if (root != null)
+	{
+		var rootId = root.id;
+
+		if (seen[rootId] == null)
+		{
+			seen[rootId] = root;
+			visitor(parent, root, connectingEdge, layer, 0);
+
+			// Copy the connects as source list so that visitors
+			// can change the original for edge direction inversions
+			var outgoingEdges = root.connectsAsSource.slice();
+			
+			for (var i = 0; i< outgoingEdges.length; i++)
+			{
+				var internalEdge = outgoingEdges[i];
+				var targetNode = internalEdge.target;
+
+				// Root check is O(|roots|)
+				this.dfs(root, targetNode, internalEdge, visitor, seen,
+						layer + 1);
+			}
+		}
+		else
+		{
+			// Use the int field to indicate this node has been seen
+			visitor(parent, root, connectingEdge, layer, 1);
+		}
+	}
+};
+
+/**
+ * Function: extendedDfs
+ *
+ * Performs a depth first search on the internal hierarchy model. This dfs
+ * extends the default version by keeping track of cells ancestors, but it
+ * should be only used when necessary because of it can be computationally
+ * intensive for deep searches.
+ *
+ * Parameters:
+ *
+ * parent - the parent internal node of the current internal node
+ * root - the current internal node
+ * connectingEdge - the internal edge connecting the internal node and the parent
+ * internal node, if any
+ * visitor - the visitor pattern to be called for each node
+ * seen - a set of all nodes seen by this dfs
+ * ancestors - the parent hash code
+ * childHash - the new hash code for this node
+ * layer - the layer on the dfs tree ( not the same as the model ranks )
+ */
+mxSwimlaneModel.prototype.extendedDfs = function(parent, root, connectingEdge, visitor, seen, ancestors, childHash, layer)
+{
+	// Explanation of custom hash set. Previously, the ancestors variable
+	// was passed through the dfs as a HashSet. The ancestors were copied
+	// into a new HashSet and when the new child was processed it was also
+	// added to the set. If the current node was in its ancestor list it
+	// meant there is a cycle in the graph and this information is passed
+	// to the visitor.visit() in the seen parameter. The HashSet clone was
+	// very expensive on CPU so a custom hash was developed using primitive
+	// types. temp[] couldn't be used so hashCode[] was added to each node.
+	// Each new child adds another int to the array, copying the prefix
+	// from its parent. Child of the same parent add different ints (the
+	// limit is therefore 2^32 children per parent...). If a node has a
+	// child with the hashCode already set then the child code is compared
+	// to the same portion of the current nodes array. If they match there
+	// is a loop.
+	// Note that the basic mechanism would only allow for 1 use of this
+	// functionality, so the root nodes have two ints. The second int is
+	// incremented through each node root and the first is incremented
+	// through each run of the dfs algorithm (therefore the dfs is not
+	// thread safe). The hash code of each node is set if not already set,
+	// or if the first int does not match that of the current run.
+	if (root != null)
+	{
+		if (parent != null)
+		{
+			// Form this nodes hash code if necessary, that is, if the
+			// hashCode variable has not been initialized or if the
+			// start of the parent hash code does not equal the start of
+			// this nodes hash code, indicating the code was set on a
+			// previous run of this dfs.
+			if (root.hashCode == null ||
+				root.hashCode[0] != parent.hashCode[0])
+			{
+				var hashCodeLength = parent.hashCode.length + 1;
+				root.hashCode = parent.hashCode.slice();
+				root.hashCode[hashCodeLength - 1] = childHash;
+			}
+		}
+
+		var rootId = root.id;
+
+		if (seen[rootId] == null)
+		{
+			seen[rootId] = root;
+			visitor(parent, root, connectingEdge, layer, 0);
+
+			// Copy the connects as source list so that visitors
+			// can change the original for edge direction inversions
+			var outgoingEdges = root.connectsAsSource.slice();
+			var incomingEdges = root.connectsAsTarget.slice();
+
+			for (var i = 0; i < outgoingEdges.length; i++)
+			{
+				var internalEdge = outgoingEdges[i];
+				var targetNode = internalEdge.target;
+				
+				// Only navigate in source->target direction within the same
+				// swimlane, or from a lower index swimlane to a higher one
+				if (root.swimlaneIndex <= targetNode.swimlaneIndex)
+				{
+					this.extendedDfs(root, targetNode, internalEdge, visitor, seen,
+							root.hashCode, i, layer + 1);
+				}
+			}
+			
+			for (var i = 0; i < incomingEdges.length; i++)
+			{
+				var internalEdge = incomingEdges[i];
+				var targetNode = internalEdge.source;
+
+				// Only navigate in target->source direction from a lower index 
+				// swimlane to a higher one
+				if (root.swimlaneIndex < targetNode.swimlaneIndex)
+				{
+					this.extendedDfs(root, targetNode, internalEdge, visitor, seen,
+							root.hashCode, i, layer + 1);
+				}
+			}
+		}
+		else
+		{
+			// Use the int field to indicate this node has been seen
+			visitor(parent, root, connectingEdge, layer, 1);
+		}
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxHierarchicalLayoutStage
+ * 
+ * The specific layout interface for hierarchical layouts. It adds a
+ * <code>run</code> method with a parameter for the hierarchical layout model
+ * that is shared between the layout stages.
+ * 
+ * Constructor: mxHierarchicalLayoutStage
+ *
+ * Constructs a new hierarchical layout stage.
+ */
+function mxHierarchicalLayoutStage() { };
+
+/**
+ * Function: execute
+ * 
+ * Takes the graph detail and configuration information within the facade
+ * and creates the resulting laid out graph within that facade for further
+ * use.
+ */
+mxHierarchicalLayoutStage.prototype.execute = function(parent) { };
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxMedianHybridCrossingReduction
+ * 
+ * Sets the horizontal locations of node and edge dummy nodes on each layer.
+ * Uses median down and up weighings as well heuristic to straighten edges as
+ * far as possible.
+ * 
+ * Constructor: mxMedianHybridCrossingReduction
+ *
+ * Creates a coordinate assignment.
+ * 
+ * Arguments:
+ * 
+ * intraCellSpacing - the minimum buffer between cells on the same rank
+ * interRankCellSpacing - the minimum distance between cells on adjacent ranks
+ * orientation - the position of the root node(s) relative to the graph
+ * initialX - the leftmost coordinate node placement starts at
+ */
+function mxMedianHybridCrossingReduction(layout)
+{
+	this.layout = layout;
+};
+
+/**
+ * Extends mxMedianHybridCrossingReduction.
+ */
+mxMedianHybridCrossingReduction.prototype = new mxHierarchicalLayoutStage();
+mxMedianHybridCrossingReduction.prototype.constructor = mxMedianHybridCrossingReduction;
+
+/**
+ * Variable: layout
+ * 
+ * Reference to the enclosing <mxHierarchicalLayout>.
+ */
+mxMedianHybridCrossingReduction.prototype.layout = null;
+
+/**
+ * Variable: maxIterations
+ * 
+ * The maximum number of iterations to perform whilst reducing edge
+ * crossings. Default is 24.
+ */
+mxMedianHybridCrossingReduction.prototype.maxIterations = 24;
+
+/**
+ * Variable: nestedBestRanks
+ * 
+ * Stores each rank as a collection of cells in the best order found for
+ * each layer so far
+ */
+mxMedianHybridCrossingReduction.prototype.nestedBestRanks = null;
+
+/**
+ * Variable: currentBestCrossings
+ * 
+ * The total number of crossings found in the best configuration so far
+ */
+mxMedianHybridCrossingReduction.prototype.currentBestCrossings = 0;
+
+/**
+ * Variable: iterationsWithoutImprovement
+ * 
+ * The total number of crossings found in the best configuration so far
+ */
+mxMedianHybridCrossingReduction.prototype.iterationsWithoutImprovement = 0;
+
+/**
+ * Variable: maxNoImprovementIterations
+ * 
+ * The total number of crossings found in the best configuration so far
+ */
+mxMedianHybridCrossingReduction.prototype.maxNoImprovementIterations = 2;
+
+/**
+ * Function: execute
+ * 
+ * Performs a vertex ordering within ranks as described by Gansner et al
+ * 1993
+ */
+mxMedianHybridCrossingReduction.prototype.execute = function(parent)
+{
+	var model = this.layout.getModel();
+
+	// Stores initial ordering as being the best one found so far
+	this.nestedBestRanks = [];
+	
+	for (var i = 0; i < model.ranks.length; i++)
+	{
+		this.nestedBestRanks[i] = model.ranks[i].slice();
+	}
+
+	var iterationsWithoutImprovement = 0;
+	var currentBestCrossings = this.calculateCrossings(model);
+
+	for (var i = 0; i < this.maxIterations &&
+		iterationsWithoutImprovement < this.maxNoImprovementIterations; i++)
+	{
+		this.weightedMedian(i, model);
+		this.transpose(i, model);
+		var candidateCrossings = this.calculateCrossings(model);
+
+		if (candidateCrossings < currentBestCrossings)
+		{
+			currentBestCrossings = candidateCrossings;
+			iterationsWithoutImprovement = 0;
+
+			// Store the current rankings as the best ones
+			for (var j = 0; j < this.nestedBestRanks.length; j++)
+			{
+				var rank = model.ranks[j];
+
+				for (var k = 0; k < rank.length; k++)
+				{
+					var cell = rank[k];
+					this.nestedBestRanks[j][cell.getGeneralPurposeVariable(j)] = cell;
+				}
+			}
+		}
+		else
+		{
+			// Increase count of iterations where we haven't improved the
+			// layout
+			iterationsWithoutImprovement++;
+
+			// Restore the best values to the cells
+			for (var j = 0; j < this.nestedBestRanks.length; j++)
+			{
+				var rank = model.ranks[j];
+				
+				for (var k = 0; k < rank.length; k++)
+				{
+					var cell = rank[k];
+					cell.setGeneralPurposeVariable(j, k);
+				}
+			}
+		}
+		
+		if (currentBestCrossings == 0)
+		{
+			// Do nothing further
+			break;
+		}
+	}
+
+	// Store the best rankings but in the model
+	var ranks = [];
+	var rankList = [];
+
+	for (var i = 0; i < model.maxRank + 1; i++)
+	{
+		rankList[i] = [];
+		ranks[i] = rankList[i];
+	}
+
+	for (var i = 0; i < this.nestedBestRanks.length; i++)
+	{
+		for (var j = 0; j < this.nestedBestRanks[i].length; j++)
+		{
+			rankList[i].push(this.nestedBestRanks[i][j]);
+		}
+	}
+
+	model.ranks = ranks;
+};
+
+
+/**
+ * Function: calculateCrossings
+ * 
+ * Calculates the total number of edge crossing in the current graph.
+ * Returns the current number of edge crossings in the hierarchy graph
+ * model in the current candidate layout
+ * 
+ * Parameters:
+ * 
+ * model - the internal model describing the hierarchy
+ */
+mxMedianHybridCrossingReduction.prototype.calculateCrossings = function(model)
+{
+	var numRanks = model.ranks.length;
+	var totalCrossings = 0;
+
+	for (var i = 1; i < numRanks; i++)
+	{
+		totalCrossings += this.calculateRankCrossing(i, model);
+	}
+	
+	return totalCrossings;
+};
+
+/**
+ * Function: calculateRankCrossing
+ * 
+ * Calculates the number of edges crossings between the specified rank and
+ * the rank below it. Returns the number of edges crossings with the rank
+ * beneath
+ * 
+ * Parameters:
+ * 
+ * i -  the topmost rank of the pair ( higher rank value )
+ * model - the internal model describing the hierarchy
+ */
+mxMedianHybridCrossingReduction.prototype.calculateRankCrossing = function(i, model)
+{
+	var totalCrossings = 0;
+	var rank = model.ranks[i];
+	var previousRank = model.ranks[i - 1];
+
+	var tmpIndices = [];
+
+	// Iterate over the top rank and fill in the connection information
+	for (var j = 0; j < rank.length; j++)
+	{
+		var node = rank[j];
+		var rankPosition = node.getGeneralPurposeVariable(i);
+		var connectedCells = node.getPreviousLayerConnectedCells(i);
+		var nodeIndices = [];
+
+		for (var k = 0; k < connectedCells.length; k++)
+		{
+			var connectedNode = connectedCells[k];
+			var otherCellRankPosition = connectedNode.getGeneralPurposeVariable(i - 1);
+			nodeIndices.push(otherCellRankPosition);
+		}
+		
+		nodeIndices.sort(function(x, y) { return x - y; });
+		tmpIndices[rankPosition] = nodeIndices;
+	}
+	
+	var indices = [];
+
+	for (var j = 0; j < tmpIndices.length; j++)
+	{
+		indices = indices.concat(tmpIndices[j]);
+	}
+
+	var firstIndex = 1;
+	
+	while (firstIndex < previousRank.length)
+	{
+		firstIndex <<= 1;
+	}
+
+	var treeSize = 2 * firstIndex - 1;
+	firstIndex -= 1;
+
+	var tree = [];
+	
+	for (var j = 0; j < treeSize; ++j)
+	{
+		tree[j] = 0;
+	}
+
+	for (var j = 0; j < indices.length; j++)
+	{
+		var index = indices[j];
+	    var treeIndex = index + firstIndex;
+	    ++tree[treeIndex];
+	    
+	    while (treeIndex > 0)
+	    {
+	    	if (treeIndex % 2)
+	    	{
+	    		totalCrossings += tree[treeIndex + 1];
+	    	}
+	      
+	    	treeIndex = (treeIndex - 1) >> 1;
+	    	++tree[treeIndex];
+	    }
+	}
+
+	return totalCrossings;
+};
+
+/**
+ * Function: transpose
+ * 
+ * Takes each possible adjacent cell pair on each rank and checks if
+ * swapping them around reduces the number of crossing
+ * 
+ * Parameters:
+ * 
+ * mainLoopIteration - the iteration number of the main loop
+ * model - the internal model describing the hierarchy
+ */
+mxMedianHybridCrossingReduction.prototype.transpose = function(mainLoopIteration, model)
+{
+	var improved = true;
+
+	// Track the number of iterations in case of looping
+	var count = 0;
+	var maxCount = 10;
+	while (improved && count++ < maxCount)
+	{
+		// On certain iterations allow allow swapping of cell pairs with
+		// equal edge crossings switched or not switched. This help to
+		// nudge a stuck layout into a lower crossing total.
+		var nudge = mainLoopIteration % 2 == 1 && count % 2 == 1;
+		improved = false;
+		
+		for (var i = 0; i < model.ranks.length; i++)
+		{
+			var rank = model.ranks[i];
+			var orderedCells = [];
+			
+			for (var j = 0; j < rank.length; j++)
+			{
+				var cell = rank[j];
+				var tempRank = cell.getGeneralPurposeVariable(i);
+				
+				// FIXME: Workaround to avoid negative tempRanks
+				if (tempRank < 0)
+				{
+					tempRank = j;
+				}
+				orderedCells[tempRank] = cell;
+			}
+			
+			var leftCellAboveConnections = null;
+			var leftCellBelowConnections = null;
+			var rightCellAboveConnections = null;
+			var rightCellBelowConnections = null;
+			
+			var leftAbovePositions = null;
+			var leftBelowPositions = null;
+			var rightAbovePositions = null;
+			var rightBelowPositions = null;
+			
+			var leftCell = null;
+			var rightCell = null;
+
+			for (var j = 0; j < (rank.length - 1); j++)
+			{
+				// For each intra-rank adjacent pair of cells
+				// see if swapping them around would reduce the
+				// number of edges crossing they cause in total
+				// On every cell pair except the first on each rank, we
+				// can save processing using the previous values for the
+				// right cell on the new left cell
+				if (j == 0)
+				{
+					leftCell = orderedCells[j];
+					leftCellAboveConnections = leftCell
+							.getNextLayerConnectedCells(i);
+					leftCellBelowConnections = leftCell
+							.getPreviousLayerConnectedCells(i);
+					leftAbovePositions = [];
+					leftBelowPositions = [];
+					
+					for (var k = 0; k < leftCellAboveConnections.length; k++)
+					{
+						leftAbovePositions[k] = leftCellAboveConnections[k].getGeneralPurposeVariable(i + 1);
+					}
+					
+					for (var k = 0; k < leftCellBelowConnections.length; k++)
+					{
+						leftBelowPositions[k] = leftCellBelowConnections[k].getGeneralPurposeVariable(i - 1);
+					}
+				}
+				else
+				{
+					leftCellAboveConnections = rightCellAboveConnections;
+					leftCellBelowConnections = rightCellBelowConnections;
+					leftAbovePositions = rightAbovePositions;
+					leftBelowPositions = rightBelowPositions;
+					leftCell = rightCell;
+				}
+				
+				rightCell = orderedCells[j + 1];
+				rightCellAboveConnections = rightCell
+						.getNextLayerConnectedCells(i);
+				rightCellBelowConnections = rightCell
+						.getPreviousLayerConnectedCells(i);
+
+				rightAbovePositions = [];
+				rightBelowPositions = [];
+
+				for (var k = 0; k < rightCellAboveConnections.length; k++)
+				{
+					rightAbovePositions[k] = rightCellAboveConnections[k].getGeneralPurposeVariable(i + 1);
+				}
+				
+				for (var k = 0; k < rightCellBelowConnections.length; k++)
+				{
+					rightBelowPositions[k] = rightCellBelowConnections[k].getGeneralPurposeVariable(i - 1);
+				}
+
+				var totalCurrentCrossings = 0;
+				var totalSwitchedCrossings = 0;
+				
+				for (var k = 0; k < leftAbovePositions.length; k++)
+				{
+					for (var ik = 0; ik < rightAbovePositions.length; ik++)
+					{
+						if (leftAbovePositions[k] > rightAbovePositions[ik])
+						{
+							totalCurrentCrossings++;
+						}
+
+						if (leftAbovePositions[k] < rightAbovePositions[ik])
+						{
+							totalSwitchedCrossings++;
+						}
+					}
+				}
+				
+				for (var k = 0; k < leftBelowPositions.length; k++)
+				{
+					for (var ik = 0; ik < rightBelowPositions.length; ik++)
+					{
+						if (leftBelowPositions[k] > rightBelowPositions[ik])
+						{
+							totalCurrentCrossings++;
+						}
+
+						if (leftBelowPositions[k] < rightBelowPositions[ik])
+						{
+							totalSwitchedCrossings++;
+						}
+					}
+				}
+				
+				if ((totalSwitchedCrossings < totalCurrentCrossings) ||
+					(totalSwitchedCrossings == totalCurrentCrossings &&
+					nudge))
+				{
+					var temp = leftCell.getGeneralPurposeVariable(i);
+					leftCell.setGeneralPurposeVariable(i, rightCell
+							.getGeneralPurposeVariable(i));
+					rightCell.setGeneralPurposeVariable(i, temp);
+
+					// With this pair exchanged we have to switch all of
+					// values for the left cell to the right cell so the
+					// next iteration for this rank uses it as the left
+					// cell again
+					rightCellAboveConnections = leftCellAboveConnections;
+					rightCellBelowConnections = leftCellBelowConnections;
+					rightAbovePositions = leftAbovePositions;
+					rightBelowPositions = leftBelowPositions;
+					rightCell = leftCell;
+					
+					if (!nudge)
+					{
+						// Don't count nudges as improvement or we'll end
+						// up stuck in two combinations and not finishing
+						// as early as we should
+						improved = true;
+					}
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: weightedMedian
+ * 
+ * Sweeps up or down the layout attempting to minimise the median placement
+ * of connected cells on adjacent ranks
+ * 
+ * Parameters:
+ * 
+ * iteration - the iteration number of the main loop
+ * model - the internal model describing the hierarchy
+ */
+mxMedianHybridCrossingReduction.prototype.weightedMedian = function(iteration, model)
+{
+	// Reverse sweep direction each time through this method
+	var downwardSweep = (iteration % 2 == 0);
+	if (downwardSweep)
+	{
+		for (var j = model.maxRank - 1; j >= 0; j--)
+		{
+			this.medianRank(j, downwardSweep);
+		}
+	}
+	else
+	{
+		for (var j = 1; j < model.maxRank; j++)
+		{
+			this.medianRank(j, downwardSweep);
+		}
+	}
+};
+
+/**
+ * Function: medianRank
+ * 
+ * Attempts to minimise the median placement of connected cells on this rank
+ * and one of the adjacent ranks
+ * 
+ * Parameters:
+ * 
+ * rankValue - the layer number of this rank
+ * downwardSweep - whether or not this is a downward sweep through the graph
+ */
+mxMedianHybridCrossingReduction.prototype.medianRank = function(rankValue, downwardSweep)
+{
+	var numCellsForRank = this.nestedBestRanks[rankValue].length;
+	var medianValues = [];
+	var reservedPositions = [];
+
+	for (var i = 0; i < numCellsForRank; i++)
+	{
+		var cell = this.nestedBestRanks[rankValue][i];
+		var sorterEntry = new MedianCellSorter();
+		sorterEntry.cell = cell;
+
+		// Flip whether or not equal medians are flipped on up and down
+		// sweeps
+		// TODO re-implement some kind of nudge
+		// medianValues[i].nudge = !downwardSweep;
+		var nextLevelConnectedCells;
+		
+		if (downwardSweep)
+		{
+			nextLevelConnectedCells = cell
+					.getNextLayerConnectedCells(rankValue);
+		}
+		else
+		{
+			nextLevelConnectedCells = cell
+					.getPreviousLayerConnectedCells(rankValue);
+		}
+		
+		var nextRankValue;
+		
+		if (downwardSweep)
+		{
+			nextRankValue = rankValue + 1;
+		}
+		else
+		{
+			nextRankValue = rankValue - 1;
+		}
+
+		if (nextLevelConnectedCells != null
+				&& nextLevelConnectedCells.length != 0)
+		{
+			sorterEntry.medianValue = this.medianValue(
+					nextLevelConnectedCells, nextRankValue);
+			medianValues.push(sorterEntry);
+		}
+		else
+		{
+			// Nodes with no adjacent vertices are flagged in the reserved array
+			// to indicate they should be left in their current position.
+			reservedPositions[cell.getGeneralPurposeVariable(rankValue)] = true;
+		}
+	}
+	
+	medianValues.sort(MedianCellSorter.prototype.compare);
+	
+	// Set the new position of each node within the rank using
+	// its temp variable
+	for (var i = 0; i < numCellsForRank; i++)
+	{
+		if (reservedPositions[i] == null)
+		{
+			var cell = medianValues.shift().cell;
+			cell.setGeneralPurposeVariable(rankValue, i);
+		}
+	}
+};
+
+/**
+ * Function: medianValue
+ * 
+ * Calculates the median rank order positioning for the specified cell using
+ * the connected cells on the specified rank. Returns the median rank
+ * ordering value of the connected cells
+ * 
+ * Parameters:
+ * 
+ * connectedCells - the cells on the specified rank connected to the
+ * specified cell
+ * rankValue - the rank that the connected cell lie upon
+ */
+mxMedianHybridCrossingReduction.prototype.medianValue = function(connectedCells, rankValue)
+{
+	var medianValues = [];
+	var arrayCount = 0;
+	
+	for (var i = 0; i < connectedCells.length; i++)
+	{
+		var cell = connectedCells[i];
+		medianValues[arrayCount++] = cell.getGeneralPurposeVariable(rankValue);
+	}
+
+	// Sort() sorts lexicographically by default (i.e. 11 before 9) so force
+	// numerical order sort
+	medianValues.sort(function(a,b){return a - b;});
+	
+	if (arrayCount % 2 == 1)
+	{
+		// For odd numbers of adjacent vertices return the median
+		return medianValues[Math.floor(arrayCount / 2)];
+	}
+	else if (arrayCount == 2)
+	{
+		return ((medianValues[0] + medianValues[1]) / 2.0);
+	}
+	else
+	{
+		var medianPoint = arrayCount / 2;
+		var leftMedian = medianValues[medianPoint - 1] - medianValues[0];
+		var rightMedian = medianValues[arrayCount - 1]
+				- medianValues[medianPoint];
+
+		return (medianValues[medianPoint - 1] * rightMedian + medianValues[medianPoint]
+				* leftMedian)
+				/ (leftMedian + rightMedian);
+	}
+};
+
+/**
+ * Class: MedianCellSorter
+ * 
+ * A utility class used to track cells whilst sorting occurs on the median
+ * values. Does not violate (x.compareTo(y)==0) == (x.equals(y))
+ *
+ * Constructor: MedianCellSorter
+ * 
+ * Constructs a new median cell sorter.
+ */
+function MedianCellSorter()
+{
+	// empty
+};
+
+/**
+ * Variable: medianValue
+ * 
+ * The weighted value of the cell stored.
+ */
+MedianCellSorter.prototype.medianValue = 0;
+
+/**
+ * Variable: cell
+ * 
+ * The cell whose median value is being calculated
+ */
+MedianCellSorter.prototype.cell = false;
+
+/**
+ * Function: compare
+ * 
+ * Compares two MedianCellSorters.
+ */
+MedianCellSorter.prototype.compare = function(a, b)
+{
+	if (a != null && b != null)
+	{
+		if (b.medianValue > a.medianValue)
+		{
+			return -1;
+		}
+		else if (b.medianValue < a.medianValue)
+		{
+			return 1;
+		}
+		else
+		{
+			return 0;
+		}
+	}
+	else
+	{
+		return 0;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxMinimumCycleRemover
+ * 
+ * An implementation of the first stage of the Sugiyama layout. Straightforward
+ * longest path calculation of layer assignment
+ * 
+ * Constructor: mxMinimumCycleRemover
+ *
+ * Creates a cycle remover for the given internal model.
+ */
+function mxMinimumCycleRemover(layout)
+{
+	this.layout = layout;
+};
+
+/**
+ * Extends mxHierarchicalLayoutStage.
+ */
+mxMinimumCycleRemover.prototype = new mxHierarchicalLayoutStage();
+mxMinimumCycleRemover.prototype.constructor = mxMinimumCycleRemover;
+
+/**
+ * Variable: layout
+ * 
+ * Reference to the enclosing <mxHierarchicalLayout>.
+ */
+mxMinimumCycleRemover.prototype.layout = null;
+
+/**
+ * Function: execute
+ * 
+ * Takes the graph detail and configuration information within the facade
+ * and creates the resulting laid out graph within that facade for further
+ * use.
+ */
+mxMinimumCycleRemover.prototype.execute = function(parent)
+{
+	var model = this.layout.getModel();
+	var seenNodes = new Object();
+	var unseenNodesArray = model.vertexMapper.getValues();
+	var unseenNodes = new Object();
+	
+	for (var i = 0; i < unseenNodesArray.length; i++)
+	{
+		unseenNodes[unseenNodesArray[i].id] = unseenNodesArray[i];
+	}
+	
+	// Perform a dfs through the internal model. If a cycle is found,
+	// reverse it.
+	var rootsArray = null;
+	
+	if (model.roots != null)
+	{
+		var modelRoots = model.roots;
+		rootsArray = [];
+		
+		for (var i = 0; i < modelRoots.length; i++)
+		{
+			rootsArray[i] = model.vertexMapper.get(modelRoots[i]);
+		}
+	}
+
+	model.visit(function(parent, node, connectingEdge, layer, seen)
+	{
+		// Check if the cell is in it's own ancestor list, if so
+		// invert the connecting edge and reverse the target/source
+		// relationship to that edge in the parent and the cell
+		if (node.isAncestor(parent))
+		{
+			connectingEdge.invert();
+			mxUtils.remove(connectingEdge, parent.connectsAsSource);
+			parent.connectsAsTarget.push(connectingEdge);
+			mxUtils.remove(connectingEdge, node.connectsAsTarget);
+			node.connectsAsSource.push(connectingEdge);
+		}
+		
+		seenNodes[node.id] = node;
+		delete unseenNodes[node.id];
+	}, rootsArray, true, null);
+
+	// If there are any nodes that should be nodes that the dfs can miss
+	// these need to be processed with the dfs and the roots assigned
+	// correctly to form a correct internal model
+	var seenNodesCopy = mxUtils.clone(seenNodes, null, true);
+
+	// Pick a random cell and dfs from it
+	model.visit(function(parent, node, connectingEdge, layer, seen)
+	{
+		// Check if the cell is in it's own ancestor list, if so
+		// invert the connecting edge and reverse the target/source
+		// relationship to that edge in the parent and the cell
+		if (node.isAncestor(parent))
+		{
+			connectingEdge.invert();
+			mxUtils.remove(connectingEdge, parent.connectsAsSource);
+			node.connectsAsSource.push(connectingEdge);
+			parent.connectsAsTarget.push(connectingEdge);
+			mxUtils.remove(connectingEdge, node.connectsAsTarget);
+		}
+		
+		seenNodes[node.id] = node;
+		delete unseenNodes[node.id];
+	}, unseenNodes, true, seenNodesCopy);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCoordinateAssignment
+ * 
+ * Sets the horizontal locations of node and edge dummy nodes on each layer.
+ * Uses median down and up weighings as well as heuristics to straighten edges as
+ * far as possible.
+ * 
+ * Constructor: mxCoordinateAssignment
+ *
+ * Creates a coordinate assignment.
+ * 
+ * Arguments:
+ * 
+ * intraCellSpacing - the minimum buffer between cells on the same rank
+ * interRankCellSpacing - the minimum distance between cells on adjacent ranks
+ * orientation - the position of the root node(s) relative to the graph
+ * initialX - the leftmost coordinate node placement starts at
+ */
+function mxCoordinateAssignment(layout, intraCellSpacing, interRankCellSpacing,
+	orientation, initialX, parallelEdgeSpacing)
+{
+	this.layout = layout;
+	this.intraCellSpacing = intraCellSpacing;
+	this.interRankCellSpacing = interRankCellSpacing;
+	this.orientation = orientation;
+	this.initialX = initialX;
+	this.parallelEdgeSpacing = parallelEdgeSpacing;
+};
+
+/**
+ * Extends mxHierarchicalLayoutStage.
+ */
+mxCoordinateAssignment.prototype = new mxHierarchicalLayoutStage();
+mxCoordinateAssignment.prototype.constructor = mxCoordinateAssignment;
+
+/**
+ * Variable: layout
+ * 
+ * Reference to the enclosing <mxHierarchicalLayout>.
+ */
+mxCoordinateAssignment.prototype.layout = null;
+
+/**
+ * Variable: intraCellSpacing
+ * 
+ * The minimum buffer between cells on the same rank. Default is 30.
+ */
+mxCoordinateAssignment.prototype.intraCellSpacing = 30;
+
+/**
+ * Variable: interRankCellSpacing
+ * 
+ * The minimum distance between cells on adjacent ranks. Default is 10.
+ */
+mxCoordinateAssignment.prototype.interRankCellSpacing = 100;
+
+/**
+ * Variable: parallelEdgeSpacing
+ * 
+ * The distance between each parallel edge on each ranks for long edges.
+ * Default is 10.
+ */
+mxCoordinateAssignment.prototype.parallelEdgeSpacing = 10;
+
+/**
+ * Variable: maxIterations
+ * 
+ * The number of heuristic iterations to run. Default is 8.
+ */
+mxCoordinateAssignment.prototype.maxIterations = 8;
+
+/**
+ * Variable: prefHozEdgeSep
+ * 
+ * The preferred horizontal distance between edges exiting a vertex
+ */
+mxCoordinateAssignment.prototype.prefHozEdgeSep = 5;
+
+/**
+ * Variable: prefVertEdgeOff
+ * 
+ * The preferred vertical offset between edges exiting a vertex
+ */
+mxCoordinateAssignment.prototype.prefVertEdgeOff = 2;
+
+/**
+ * Variable: minEdgeJetty
+ * 
+ * The minimum distance for an edge jetty from a vertex
+ */
+mxCoordinateAssignment.prototype.minEdgeJetty = 12;
+
+/**
+ * Variable: channelBuffer
+ * 
+ * The size of the vertical buffer in the center of inter-rank channels
+ * where edge control points should not be placed
+ */
+mxCoordinateAssignment.prototype.channelBuffer = 4;
+
+/**
+ * Variable: jettyPositions
+ * 
+ * Map of internal edges and (x,y) pair of positions of the start and end jetty
+ * for that edge where it connects to the source and target vertices.
+ * Note this should technically be a WeakHashMap, but since JS does not
+ * have an equivalent, housekeeping must be performed before using.
+ * i.e. check all edges are still in the model and clear the values.
+ * Note that the y co-ord is the offset of the jetty, not the
+ * absolute point
+ */
+mxCoordinateAssignment.prototype.jettyPositions = null;
+
+/**
+ * Variable: orientation
+ * 
+ * The position of the root ( start ) node(s) relative to the rest of the
+ * laid out graph. Default is <mxConstants.DIRECTION_NORTH>.
+ */
+mxCoordinateAssignment.prototype.orientation = mxConstants.DIRECTION_NORTH;
+
+/**
+ * Variable: initialX
+ * 
+ * The minimum x position node placement starts at
+ */
+mxCoordinateAssignment.prototype.initialX = null;
+
+/**
+ * Variable: limitX
+ * 
+ * The maximum x value this positioning lays up to
+ */
+mxCoordinateAssignment.prototype.limitX = null;
+
+/**
+ * Variable: currentXDelta
+ * 
+ * The sum of x-displacements for the current iteration
+ */
+mxCoordinateAssignment.prototype.currentXDelta = null;
+
+/**
+ * Variable: widestRank
+ * 
+ * The rank that has the widest x position
+ */
+mxCoordinateAssignment.prototype.widestRank = null;
+
+/**
+ * Variable: rankTopY
+ * 
+ * Internal cache of top-most values of Y for each rank
+ */
+mxCoordinateAssignment.prototype.rankTopY = null;
+
+/**
+ * Variable: rankBottomY
+ * 
+ * Internal cache of bottom-most value of Y for each rank
+ */
+mxCoordinateAssignment.prototype.rankBottomY = null;
+
+/**
+ * Variable: widestRankValue
+ * 
+ * The X-coordinate of the edge of the widest rank
+ */
+mxCoordinateAssignment.prototype.widestRankValue = null;
+
+/**
+ * Variable: rankWidths
+ * 
+ * The width of all the ranks
+ */
+mxCoordinateAssignment.prototype.rankWidths = null;
+
+/**
+ * Variable: rankY
+ * 
+ * The Y-coordinate of all the ranks
+ */
+mxCoordinateAssignment.prototype.rankY = null;
+
+/**
+ * Variable: fineTuning
+ * 
+ * Whether or not to perform local optimisations and iterate multiple times
+ * through the algorithm. Default is true.
+ */
+mxCoordinateAssignment.prototype.fineTuning = true;
+
+/**
+ * Variable: nextLayerConnectedCache
+ * 
+ * A store of connections to the layer above for speed
+ */
+mxCoordinateAssignment.prototype.nextLayerConnectedCache = null;
+
+/**
+ * Variable: previousLayerConnectedCache
+ * 
+ * A store of connections to the layer below for speed
+ */
+mxCoordinateAssignment.prototype.previousLayerConnectedCache = null;
+
+/**
+ * Variable: groupPadding
+ * 
+ * Padding added to resized parents
+ */
+mxCoordinateAssignment.prototype.groupPadding = 10;
+
+/**
+ * Utility method to display current positions
+ */
+mxCoordinateAssignment.prototype.printStatus = function()
+{
+	var model = this.layout.getModel();
+	mxLog.show();
+
+	mxLog.writeln('======Coord assignment debug=======');
+
+	for (var j = 0; j < model.ranks.length; j++)
+	{
+		mxLog.write('Rank ', j, ' : ' );
+		var rank = model.ranks[j];
+		
+		for (var k = 0; k < rank.length; k++)
+		{
+			var cell = rank[k];
+			
+			mxLog.write(cell.getGeneralPurposeVariable(j), '  ');
+		}
+		mxLog.writeln();
+	}
+	
+	mxLog.writeln('====================================');
+};
+
+/**
+ * Function: execute
+ * 
+ * A basic horizontal coordinate assignment algorithm
+ */
+mxCoordinateAssignment.prototype.execute = function(parent)
+{
+	this.jettyPositions = Object();
+	var model = this.layout.getModel();
+	this.currentXDelta = 0.0;
+
+	this.initialCoords(this.layout.getGraph(), model);
+	
+//	this.printStatus();
+	
+	if (this.fineTuning)
+	{
+		this.minNode(model);
+	}
+	
+	var bestXDelta = 100000000.0;
+	
+	if (this.fineTuning)
+	{
+		for (var i = 0; i < this.maxIterations; i++)
+		{
+//			this.printStatus();
+		
+			// Median Heuristic
+			if (i != 0)
+			{
+				this.medianPos(i, model);
+				this.minNode(model);
+			}
+			
+			// if the total offset is less for the current positioning,
+			// there are less heavily angled edges and so the current
+			// positioning is used
+			if (this.currentXDelta < bestXDelta)
+			{
+				for (var j = 0; j < model.ranks.length; j++)
+				{
+					var rank = model.ranks[j];
+					
+					for (var k = 0; k < rank.length; k++)
+					{
+						var cell = rank[k];
+						cell.setX(j, cell.getGeneralPurposeVariable(j));
+					}
+				}
+				
+				bestXDelta = this.currentXDelta;
+			}
+			else
+			{
+				// Restore the best positions
+				for (var j = 0; j < model.ranks.length; j++)
+				{
+					var rank = model.ranks[j];
+					
+					for (var k = 0; k < rank.length; k++)
+					{
+						var cell = rank[k];
+						cell.setGeneralPurposeVariable(j, cell.getX(j));
+					}
+				}
+			}
+			
+			this.minPath(this.layout.getGraph(), model);
+			
+			this.currentXDelta = 0;
+		}
+	}
+	
+	this.setCellLocations(this.layout.getGraph(), model);
+};
+
+/**
+ * Function: minNode
+ * 
+ * Performs one median positioning sweep in both directions
+ */
+mxCoordinateAssignment.prototype.minNode = function(model)
+{
+	// Queue all nodes
+	var nodeList = [];
+	
+	// Need to be able to map from cell to cellWrapper
+	var map = new mxDictionary();
+	var rank = [];
+	
+	for (var i = 0; i <= model.maxRank; i++)
+	{
+		rank[i] = model.ranks[i];
+		
+		for (var j = 0; j < rank[i].length; j++)
+		{
+			// Use the weight to store the rank and visited to store whether
+			// or not the cell is in the list
+			var node = rank[i][j];
+			var nodeWrapper = new WeightedCellSorter(node, i);
+			nodeWrapper.rankIndex = j;
+			nodeWrapper.visited = true;
+			nodeList.push(nodeWrapper);
+			
+			map.put(node, nodeWrapper);
+		}
+	}
+	
+	// Set a limit of the maximum number of times we will access the queue
+	// in case a loop appears
+	var maxTries = nodeList.length * 10;
+	var count = 0;
+	
+	// Don't move cell within this value of their median
+	var tolerance = 1;
+	
+	while (nodeList.length > 0 && count <= maxTries)
+	{
+		var cellWrapper = nodeList.shift();
+		var cell = cellWrapper.cell;
+		
+		var rankValue = cellWrapper.weightedValue;
+		var rankIndex = parseInt(cellWrapper.rankIndex);
+		
+		var nextLayerConnectedCells = cell.getNextLayerConnectedCells(rankValue);
+		var previousLayerConnectedCells = cell.getPreviousLayerConnectedCells(rankValue);
+		
+		var numNextLayerConnected = nextLayerConnectedCells.length;
+		var numPreviousLayerConnected = previousLayerConnectedCells.length;
+
+		var medianNextLevel = this.medianXValue(nextLayerConnectedCells,
+				rankValue + 1);
+		var medianPreviousLevel = this.medianXValue(previousLayerConnectedCells,
+				rankValue - 1);
+
+		var numConnectedNeighbours = numNextLayerConnected
+				+ numPreviousLayerConnected;
+		var currentPosition = cell.getGeneralPurposeVariable(rankValue);
+		var cellMedian = currentPosition;
+		
+		if (numConnectedNeighbours > 0)
+		{
+			cellMedian = (medianNextLevel * numNextLayerConnected + medianPreviousLevel
+					* numPreviousLayerConnected)
+					/ numConnectedNeighbours;
+		}
+
+		// Flag storing whether or not position has changed
+		var positionChanged = false;
+		
+		if (cellMedian < currentPosition - tolerance)
+		{
+			if (rankIndex == 0)
+			{
+				cell.setGeneralPurposeVariable(rankValue, cellMedian);
+				positionChanged = true;
+			}
+			else
+			{
+				var leftCell = rank[rankValue][rankIndex - 1];
+				var leftLimit = leftCell
+						.getGeneralPurposeVariable(rankValue);
+				leftLimit = leftLimit + leftCell.width / 2
+						+ this.intraCellSpacing + cell.width / 2;
+
+				if (leftLimit < cellMedian)
+				{
+					cell.setGeneralPurposeVariable(rankValue, cellMedian);
+					positionChanged = true;
+				}
+				else if (leftLimit < cell
+						.getGeneralPurposeVariable(rankValue)
+						- tolerance)
+				{
+					cell.setGeneralPurposeVariable(rankValue, leftLimit);
+					positionChanged = true;
+				}
+			}
+		}
+		else if (cellMedian > currentPosition + tolerance)
+		{
+			var rankSize = rank[rankValue].length;
+			
+			if (rankIndex == rankSize - 1)
+			{
+				cell.setGeneralPurposeVariable(rankValue, cellMedian);
+				positionChanged = true;
+			}
+			else
+			{
+				var rightCell = rank[rankValue][rankIndex + 1];
+				var rightLimit = rightCell
+						.getGeneralPurposeVariable(rankValue);
+				rightLimit = rightLimit - rightCell.width / 2
+						- this.intraCellSpacing - cell.width / 2;
+				
+				if (rightLimit > cellMedian)
+				{
+					cell.setGeneralPurposeVariable(rankValue, cellMedian);
+					positionChanged = true;
+				}
+				else if (rightLimit > cell
+						.getGeneralPurposeVariable(rankValue)
+						+ tolerance)
+				{
+					cell.setGeneralPurposeVariable(rankValue, rightLimit);
+					positionChanged = true;
+				}
+			}
+		}
+		
+		if (positionChanged)
+		{
+			// Add connected nodes to map and list
+			for (var i = 0; i < nextLayerConnectedCells.length; i++)
+			{
+				var connectedCell = nextLayerConnectedCells[i];
+				var connectedCellWrapper = map.get(connectedCell);
+				
+				if (connectedCellWrapper != null)
+				{
+					if (connectedCellWrapper.visited == false)
+					{
+						connectedCellWrapper.visited = true;
+						nodeList.push(connectedCellWrapper);
+					}
+				}
+			}
+
+			// Add connected nodes to map and list
+			for (var i = 0; i < previousLayerConnectedCells.length; i++)
+			{
+				var connectedCell = previousLayerConnectedCells[i];
+				var connectedCellWrapper = map.get(connectedCell);
+
+				if (connectedCellWrapper != null)
+				{
+					if (connectedCellWrapper.visited == false)
+					{
+						connectedCellWrapper.visited = true;
+						nodeList.push(connectedCellWrapper);
+					}
+				}
+			}
+		}
+		
+		cellWrapper.visited = false;
+		count++;
+	}
+};
+
+/**
+ * Function: medianPos
+ * 
+ * Performs one median positioning sweep in one direction
+ * 
+ * Parameters:
+ * 
+ * i - the iteration of the whole process
+ * model - an internal model of the hierarchical layout
+ */
+mxCoordinateAssignment.prototype.medianPos = function(i, model)
+{
+	// Reverse sweep direction each time through this method
+	var downwardSweep = (i % 2 == 0);
+	
+	if (downwardSweep)
+	{
+		for (var j = model.maxRank; j > 0; j--)
+		{
+			this.rankMedianPosition(j - 1, model, j);
+		}
+	}
+	else
+	{
+		for (var j = 0; j < model.maxRank - 1; j++)
+		{
+			this.rankMedianPosition(j + 1, model, j);
+		}
+	}
+};
+
+/**
+ * Function: rankMedianPosition
+ * 
+ * Performs median minimisation over one rank.
+ * 
+ * Parameters:
+ * 
+ * rankValue - the layer number of this rank
+ * model - an internal model of the hierarchical layout
+ * nextRankValue - the layer number whose connected cels are to be laid out
+ * relative to
+ */
+mxCoordinateAssignment.prototype.rankMedianPosition = function(rankValue, model, nextRankValue)
+{
+	var rank = model.ranks[rankValue];
+
+	// Form an array of the order in which the cell are to be processed
+	// , the order is given by the weighted sum of the in or out edges,
+	// depending on whether we're traveling up or down the hierarchy.
+	var weightedValues = [];
+	var cellMap = new Object();
+
+	for (var i = 0; i < rank.length; i++)
+	{
+		var currentCell = rank[i];
+		weightedValues[i] = new WeightedCellSorter();
+		weightedValues[i].cell = currentCell;
+		weightedValues[i].rankIndex = i;
+		cellMap[currentCell.id] = weightedValues[i];
+		var nextLayerConnectedCells = null;
+		
+		if (nextRankValue < rankValue)
+		{
+			nextLayerConnectedCells = currentCell
+					.getPreviousLayerConnectedCells(rankValue);
+		}
+		else
+		{
+			nextLayerConnectedCells = currentCell
+					.getNextLayerConnectedCells(rankValue);
+		}
+
+		// Calculate the weighing based on this node type and those this
+		// node is connected to on the next layer
+		weightedValues[i].weightedValue = this.calculatedWeightedValue(
+				currentCell, nextLayerConnectedCells);
+	}
+
+	weightedValues.sort(WeightedCellSorter.prototype.compare);
+
+	// Set the new position of each node within the rank using
+	// its temp variable
+	
+	for (var i = 0; i < weightedValues.length; i++)
+	{
+		var numConnectionsNextLevel = 0;
+		var cell = weightedValues[i].cell;
+		var nextLayerConnectedCells = null;
+		var medianNextLevel = 0;
+
+		if (nextRankValue < rankValue)
+		{
+			nextLayerConnectedCells = cell.getPreviousLayerConnectedCells(
+					rankValue).slice();
+		}
+		else
+		{
+			nextLayerConnectedCells = cell.getNextLayerConnectedCells(
+					rankValue).slice();
+		}
+
+		if (nextLayerConnectedCells != null)
+		{
+			numConnectionsNextLevel = nextLayerConnectedCells.length;
+			
+			if (numConnectionsNextLevel > 0)
+			{
+				medianNextLevel = this.medianXValue(nextLayerConnectedCells,
+						nextRankValue);
+			}
+			else
+			{
+				// For case of no connections on the next level set the
+				// median to be the current position and try to be
+				// positioned there
+				medianNextLevel = cell.getGeneralPurposeVariable(rankValue);
+			}
+		}
+
+		var leftBuffer = 0.0;
+		var leftLimit = -100000000.0;
+		
+		for (var j = weightedValues[i].rankIndex - 1; j >= 0;)
+		{
+			var weightedValue = cellMap[rank[j].id];
+			
+			if (weightedValue != null)
+			{
+				var leftCell = weightedValue.cell;
+				
+				if (weightedValue.visited)
+				{
+					// The left limit is the right hand limit of that
+					// cell plus any allowance for unallocated cells
+					// in-between
+					leftLimit = leftCell
+							.getGeneralPurposeVariable(rankValue)
+							+ leftCell.width
+							/ 2.0
+							+ this.intraCellSpacing
+							+ leftBuffer + cell.width / 2.0;
+					j = -1;
+				}
+				else
+				{
+					leftBuffer += leftCell.width + this.intraCellSpacing;
+					j--;
+				}
+			}
+		}
+
+		var rightBuffer = 0.0;
+		var rightLimit = 100000000.0;
+		
+		for (var j = weightedValues[i].rankIndex + 1; j < weightedValues.length;)
+		{
+			var weightedValue = cellMap[rank[j].id];
+			
+			if (weightedValue != null)
+			{
+				var rightCell = weightedValue.cell;
+				
+				if (weightedValue.visited)
+				{
+					// The left limit is the right hand limit of that
+					// cell plus any allowance for unallocated cells
+					// in-between
+					rightLimit = rightCell
+							.getGeneralPurposeVariable(rankValue)
+							- rightCell.width
+							/ 2.0
+							- this.intraCellSpacing
+							- rightBuffer - cell.width / 2.0;
+					j = weightedValues.length;
+				}
+				else
+				{
+					rightBuffer += rightCell.width + this.intraCellSpacing;
+					j++;
+				}
+			}
+		}
+		
+		if (medianNextLevel >= leftLimit && medianNextLevel <= rightLimit)
+		{
+			cell.setGeneralPurposeVariable(rankValue, medianNextLevel);
+		}
+		else if (medianNextLevel < leftLimit)
+		{
+			// Couldn't place at median value, place as close to that
+			// value as possible
+			cell.setGeneralPurposeVariable(rankValue, leftLimit);
+			this.currentXDelta += leftLimit - medianNextLevel;
+		}
+		else if (medianNextLevel > rightLimit)
+		{
+			// Couldn't place at median value, place as close to that
+			// value as possible
+			cell.setGeneralPurposeVariable(rankValue, rightLimit);
+			this.currentXDelta += medianNextLevel - rightLimit;
+		}
+
+		weightedValues[i].visited = true;
+	}
+};
+
+/**
+ * Function: calculatedWeightedValue
+ * 
+ * Calculates the priority the specified cell has based on the type of its
+ * cell and the cells it is connected to on the next layer
+ * 
+ * Parameters:
+ * 
+ * currentCell - the cell whose weight is to be calculated
+ * collection - the cells the specified cell is connected to
+ */
+mxCoordinateAssignment.prototype.calculatedWeightedValue = function(currentCell, collection)
+{
+	var totalWeight = 0;
+	
+	for (var i = 0; i < collection.length; i++)
+	{
+		var cell = collection[i];
+
+		if (currentCell.isVertex() && cell.isVertex())
+		{
+			totalWeight++;
+		}
+		else if (currentCell.isEdge() && cell.isEdge())
+		{
+			totalWeight += 8;
+		}
+		else
+		{
+			totalWeight += 2;
+		}
+	}
+
+	return totalWeight;
+};
+
+/**
+ * Function: medianXValue
+ * 
+ * Calculates the median position of the connected cell on the specified
+ * rank
+ * 
+ * Parameters:
+ * 
+ * connectedCells - the cells the candidate connects to on this level
+ * rankValue - the layer number of this rank
+ */
+mxCoordinateAssignment.prototype.medianXValue = function(connectedCells, rankValue)
+{
+	if (connectedCells.length == 0)
+	{
+		return 0;
+	}
+
+	var medianValues = [];
+
+	for (var i = 0; i < connectedCells.length; i++)
+	{
+		medianValues[i] = connectedCells[i].getGeneralPurposeVariable(rankValue);
+	}
+
+	medianValues.sort(function(a,b){return a - b;});
+	
+	if (connectedCells.length % 2 == 1)
+	{
+		// For odd numbers of adjacent vertices return the median
+		return medianValues[Math.floor(connectedCells.length / 2)];
+	}
+	else
+	{
+		var medianPoint = connectedCells.length / 2;
+		var leftMedian = medianValues[medianPoint - 1];
+		var rightMedian = medianValues[medianPoint];
+
+		return ((leftMedian + rightMedian) / 2);
+	}
+};
+
+/**
+ * Function: initialCoords
+ * 
+ * Sets up the layout in an initial positioning. The ranks are all centered
+ * as much as possible along the middle vertex in each rank. The other cells
+ * are then placed as close as possible on either side.
+ * 
+ * Parameters:
+ * 
+ * facade - the facade describing the input graph
+ * model - an internal model of the hierarchical layout
+ */
+mxCoordinateAssignment.prototype.initialCoords = function(facade, model)
+{
+	this.calculateWidestRank(facade, model);
+
+	// Sweep up and down from the widest rank
+	for (var i = this.widestRank; i >= 0; i--)
+	{
+		if (i < model.maxRank)
+		{
+			this.rankCoordinates(i, facade, model);
+		}
+	}
+
+	for (var i = this.widestRank+1; i <= model.maxRank; i++)
+	{
+		if (i > 0)
+		{
+			this.rankCoordinates(i, facade, model);
+		}
+	}
+};
+
+/**
+ * Function: rankCoordinates
+ * 
+ * Sets up the layout in an initial positioning. All the first cells in each
+ * rank are moved to the left and the rest of the rank inserted as close
+ * together as their size and buffering permits. This method works on just
+ * the specified rank.
+ * 
+ * Parameters:
+ * 
+ * rankValue - the current rank being processed
+ * graph - the facade describing the input graph
+ * model - an internal model of the hierarchical layout
+ */
+mxCoordinateAssignment.prototype.rankCoordinates = function(rankValue, graph, model)
+{
+	var rank = model.ranks[rankValue];
+	var maxY = 0.0;
+	var localX = this.initialX + (this.widestRankValue - this.rankWidths[rankValue])
+			/ 2;
+
+	// Store whether or not any of the cells' bounds were unavailable so
+	// to only issue the warning once for all cells
+	var boundsWarning = false;
+	
+	for (var i = 0; i < rank.length; i++)
+	{
+		var node = rank[i];
+		
+		if (node.isVertex())
+		{
+			var bounds = this.layout.getVertexBounds(node.cell);
+
+			if (bounds != null)
+			{
+				if (this.orientation == mxConstants.DIRECTION_NORTH ||
+					this.orientation == mxConstants.DIRECTION_SOUTH)
+				{
+					node.width = bounds.width;
+					node.height = bounds.height;
+				}
+				else
+				{
+					node.width = bounds.height;
+					node.height = bounds.width;
+				}
+			}
+			else
+			{
+				boundsWarning = true;
+			}
+
+			maxY = Math.max(maxY, node.height);
+		}
+		else if (node.isEdge())
+		{
+			// The width is the number of additional parallel edges
+			// time the parallel edge spacing
+			var numEdges = 1;
+
+			if (node.edges != null)
+			{
+				numEdges = node.edges.length;
+			}
+			else
+			{
+				mxLog.warn('edge.edges is null');
+			}
+
+			node.width = (numEdges - 1) * this.parallelEdgeSpacing;
+		}
+
+		// Set the initial x-value as being the best result so far
+		localX += node.width / 2.0;
+		node.setX(rankValue, localX);
+		node.setGeneralPurposeVariable(rankValue, localX);
+		localX += node.width / 2.0;
+		localX += this.intraCellSpacing;
+	}
+
+	if (boundsWarning == true)
+	{
+		mxLog.warn('At least one cell has no bounds');
+	}
+};
+
+/**
+ * Function: calculateWidestRank
+ * 
+ * Calculates the width rank in the hierarchy. Also set the y value of each
+ * rank whilst performing the calculation
+ * 
+ * Parameters:
+ * 
+ * graph - the facade describing the input graph
+ * model - an internal model of the hierarchical layout
+ */
+mxCoordinateAssignment.prototype.calculateWidestRank = function(graph, model)
+{
+	// Starting y co-ordinate
+	var y = -this.interRankCellSpacing;
+	
+	// Track the widest cell on the last rank since the y
+	// difference depends on it
+	var lastRankMaxCellHeight = 0.0;
+	this.rankWidths = [];
+	this.rankY = [];
+
+	for (var rankValue = model.maxRank; rankValue >= 0; rankValue--)
+	{
+		// Keep track of the widest cell on this rank
+		var maxCellHeight = 0.0;
+		var rank = model.ranks[rankValue];
+		var localX = this.initialX;
+
+		// Store whether or not any of the cells' bounds were unavailable so
+		// to only issue the warning once for all cells
+		var boundsWarning = false;
+		
+		for (var i = 0; i < rank.length; i++)
+		{
+			var node = rank[i];
+
+			if (node.isVertex())
+			{
+				var bounds = this.layout.getVertexBounds(node.cell);
+
+				if (bounds != null)
+				{
+					if (this.orientation == mxConstants.DIRECTION_NORTH ||
+						this.orientation == mxConstants.DIRECTION_SOUTH)
+					{
+						node.width = bounds.width;
+						node.height = bounds.height;
+					}
+					else
+					{
+						node.width = bounds.height;
+						node.height = bounds.width;
+					}
+				}
+				else
+				{
+					boundsWarning = true;
+				}
+
+				maxCellHeight = Math.max(maxCellHeight, node.height);
+			}
+			else if (node.isEdge())
+			{
+				// The width is the number of additional parallel edges
+				// time the parallel edge spacing
+				var numEdges = 1;
+
+				if (node.edges != null)
+				{
+					numEdges = node.edges.length;
+				}
+				else
+				{
+					mxLog.warn('edge.edges is null');
+				}
+
+				node.width = (numEdges - 1) * this.parallelEdgeSpacing;
+			}
+
+			// Set the initial x-value as being the best result so far
+			localX += node.width / 2.0;
+			node.setX(rankValue, localX);
+			node.setGeneralPurposeVariable(rankValue, localX);
+			localX += node.width / 2.0;
+			localX += this.intraCellSpacing;
+
+			if (localX > this.widestRankValue)
+			{
+				this.widestRankValue = localX;
+				this.widestRank = rankValue;
+			}
+
+			this.rankWidths[rankValue] = localX;
+		}
+
+		if (boundsWarning == true)
+		{
+			mxLog.warn('At least one cell has no bounds');
+		}
+
+		this.rankY[rankValue] = y;
+		var distanceToNextRank = maxCellHeight / 2.0
+				+ lastRankMaxCellHeight / 2.0 + this.interRankCellSpacing;
+		lastRankMaxCellHeight = maxCellHeight;
+
+		if (this.orientation == mxConstants.DIRECTION_NORTH ||
+			this.orientation == mxConstants.DIRECTION_WEST)
+		{
+			y += distanceToNextRank;
+		}
+		else
+		{
+			y -= distanceToNextRank;
+		}
+
+		for (var i = 0; i < rank.length; i++)
+		{
+			var cell = rank[i];
+			cell.setY(rankValue, y);
+		}
+	}
+};
+
+/**
+ * Function: minPath
+ * 
+ * Straightens out chains of virtual nodes where possibleacade to those stored after this layout
+ * processing step has completed.
+ * 
+ * Parameters:
+ *
+ * graph - the facade describing the input graph
+ * model - an internal model of the hierarchical layout
+ */
+mxCoordinateAssignment.prototype.minPath = function(graph, model)
+{
+	// Work down and up each edge with at least 2 control points
+	// trying to straighten each one out. If the same number of
+	// straight segments are formed in both directions, the 
+	// preferred direction used is the one where the final
+	// control points have the least offset from the connectable 
+	// region of the terminating vertices
+	var edges = model.edgeMapper.getValues();
+	
+	for (var j = 0; j < edges.length; j++)
+	{
+		var cell = edges[j];
+		
+		if (cell.maxRank - cell.minRank - 1 < 1)
+		{
+			continue;
+		}
+
+		// At least two virtual nodes in the edge
+		// Check first whether the edge is already straight
+		var referenceX = cell
+				.getGeneralPurposeVariable(cell.minRank + 1);
+		var edgeStraight = true;
+		var refSegCount = 0;
+		
+		for (var i = cell.minRank + 2; i < cell.maxRank; i++)
+		{
+			var x = cell.getGeneralPurposeVariable(i);
+
+			if (referenceX != x)
+			{
+				edgeStraight = false;
+				referenceX = x;
+			}
+			else
+			{
+				refSegCount++;
+			}
+		}
+
+		if (!edgeStraight)
+		{
+			var upSegCount = 0;
+			var downSegCount = 0;
+			var upXPositions = [];
+			var downXPositions = [];
+
+			var currentX = cell.getGeneralPurposeVariable(cell.minRank + 1);
+
+			for (var i = cell.minRank + 1; i < cell.maxRank - 1; i++)
+			{
+				// Attempt to straight out the control point on the
+				// next segment up with the current control point.
+				var nextX = cell.getX(i + 1);
+
+				if (currentX == nextX)
+				{
+					upXPositions[i - cell.minRank - 1] = currentX;
+					upSegCount++;
+				}
+				else if (this.repositionValid(model, cell, i + 1, currentX))
+				{
+					upXPositions[i - cell.minRank - 1] = currentX;
+					upSegCount++;
+					// Leave currentX at same value
+				}
+				else
+				{
+					upXPositions[i - cell.minRank - 1] = nextX;
+					currentX = nextX;
+				}				
+			}
+
+			currentX = cell.getX(i);
+
+			for (var i = cell.maxRank - 1; i > cell.minRank + 1; i--)
+			{
+				// Attempt to straight out the control point on the
+				// next segment down with the current control point.
+				var nextX = cell.getX(i - 1);
+
+				if (currentX == nextX)
+				{
+					downXPositions[i - cell.minRank - 2] = currentX;
+					downSegCount++;
+				}
+				else if (this.repositionValid(model, cell, i - 1, currentX))
+				{
+					downXPositions[i - cell.minRank - 2] = currentX;
+					downSegCount++;
+					// Leave currentX at same value
+				}
+				else
+				{
+					downXPositions[i - cell.minRank - 2] = cell.getX(i-1);
+					currentX = nextX;
+				}
+			}
+
+			if (downSegCount > refSegCount || upSegCount > refSegCount)
+			{
+				if (downSegCount >= upSegCount)
+				{
+					// Apply down calculation values
+					for (var i = cell.maxRank - 2; i > cell.minRank; i--)
+					{
+						cell.setX(i, downXPositions[i - cell.minRank - 1]);
+					}
+				}
+				else if (upSegCount > downSegCount)
+				{
+					// Apply up calculation values
+					for (var i = cell.minRank + 2; i < cell.maxRank; i++)
+					{
+						cell.setX(i, upXPositions[i - cell.minRank - 2]);
+					}
+				}
+				else
+				{
+					// Neither direction provided a favourable result
+					// But both calculations are better than the
+					// existing solution, so apply the one with minimal
+					// offset to attached vertices at either end.
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: repositionValid
+ * 
+ * Determines whether or not a node may be moved to the specified x 
+ * position on the specified rank
+ * 
+ * Parameters:
+ *
+ * model - the layout model
+ * cell - the cell being analysed
+ * rank - the layer of the cell
+ * position - the x position being sought
+ */
+mxCoordinateAssignment.prototype.repositionValid = function(model, cell, rank, position)
+{
+	var rankArray = model.ranks[rank];
+	var rankIndex = -1;
+
+	for (var i = 0; i < rankArray.length; i++)
+	{
+		if (cell == rankArray[i])
+		{
+			rankIndex = i;
+			break;
+		}
+	}
+
+	if (rankIndex < 0)
+	{
+		return false;
+	}
+
+	var currentX = cell.getGeneralPurposeVariable(rank);
+
+	if (position < currentX)
+	{
+		// Trying to move node to the left.
+		if (rankIndex == 0)
+		{
+			// Left-most node, can move anywhere
+			return true;
+		}
+
+		var leftCell = rankArray[rankIndex - 1];
+		var leftLimit = leftCell.getGeneralPurposeVariable(rank);
+		leftLimit = leftLimit + leftCell.width / 2
+				+ this.intraCellSpacing + cell.width / 2;
+
+		if (leftLimit <= position)
+		{
+			return true;
+		}
+		else
+		{
+			return false;
+		}
+	}
+	else if (position > currentX)
+	{
+		// Trying to move node to the right.
+		if (rankIndex == rankArray.length - 1)
+		{
+			// Right-most node, can move anywhere
+			return true;
+		}
+
+		var rightCell = rankArray[rankIndex + 1];
+		var rightLimit = rightCell.getGeneralPurposeVariable(rank);
+		rightLimit = rightLimit - rightCell.width / 2
+				- this.intraCellSpacing - cell.width / 2;
+
+		if (rightLimit >= position)
+		{
+			return true;
+		}
+		else
+		{
+			return false;
+		}
+	}
+
+	return true;
+};
+
+/**
+ * Function: setCellLocations
+ * 
+ * Sets the cell locations in the facade to those stored after this layout
+ * processing step has completed.
+ * 
+ * Parameters:
+ *
+ * graph - the input graph
+ * model - the layout model
+ */
+mxCoordinateAssignment.prototype.setCellLocations = function(graph, model)
+{
+	this.rankTopY = [];
+	this.rankBottomY = [];
+
+	for (var i = 0; i < model.ranks.length; i++)
+	{
+		this.rankTopY[i] = Number.MAX_VALUE;
+		this.rankBottomY[i] = -Number.MAX_VALUE;
+	}
+	
+	var vertices = model.vertexMapper.getValues();
+
+	// Process vertices all first, since they define the lower and 
+	// limits of each rank. Between these limits lie the channels
+	// where the edges can be routed across the graph
+
+	for (var i = 0; i < vertices.length; i++)
+	{
+		this.setVertexLocation(vertices[i]);
+	}
+	
+	// Post process edge styles. Needs the vertex locations set for initial
+	// values of the top and bottoms of each rank
+	if (this.layout.edgeStyle == mxHierarchicalEdgeStyle.ORTHOGONAL
+			|| this.layout.edgeStyle == mxHierarchicalEdgeStyle.POLYLINE
+			|| this.layout.edgeStyle == mxHierarchicalEdgeStyle.CURVE)
+	{
+		this.localEdgeProcessing(model);
+	}
+
+	var edges = model.edgeMapper.getValues();
+
+	for (var i = 0; i < edges.length; i++)
+	{
+		this.setEdgePosition(edges[i]);
+	}
+};
+
+/**
+ * Function: localEdgeProcessing
+ * 
+ * Separates the x position of edges as they connect to vertices
+ * 
+ * Parameters:
+ *
+ * model - the layout model
+ */
+mxCoordinateAssignment.prototype.localEdgeProcessing = function(model)
+{
+	// Iterate through each vertex, look at the edges connected in
+	// both directions.
+	for (var rankIndex = 0; rankIndex < model.ranks.length; rankIndex++)
+	{
+		var rank = model.ranks[rankIndex];
+
+		for (var cellIndex = 0; cellIndex < rank.length; cellIndex++)
+		{
+			var cell = rank[cellIndex];
+
+			if (cell.isVertex())
+			{
+				var currentCells = cell.getPreviousLayerConnectedCells(rankIndex);
+
+				var currentRank = rankIndex - 1;
+
+				// Two loops, last connected cells, and next
+				for (var k = 0; k < 2; k++)
+				{
+					if (currentRank > -1
+							&& currentRank < model.ranks.length
+							&& currentCells != null
+							&& currentCells.length > 0)
+					{
+						var sortedCells = [];
+
+						for (var j = 0; j < currentCells.length; j++)
+						{
+							var sorter = new WeightedCellSorter(
+									currentCells[j], currentCells[j].getX(currentRank));
+							sortedCells.push(sorter);
+						}
+
+						sortedCells.sort(WeightedCellSorter.prototype.compare);
+
+						var leftLimit = cell.x[0] - cell.width / 2;
+						var rightLimit = leftLimit + cell.width;
+
+						// Connected edge count starts at 1 to allow for buffer
+						// with edge of vertex
+						var connectedEdgeCount = 0;
+						var connectedEdgeGroupCount = 0;
+						var connectedEdges = [];
+						// Calculate width requirements for all connected edges
+						for (var j = 0; j < sortedCells.length; j++)
+						{
+							var innerCell = sortedCells[j].cell;
+							var connections;
+
+							if (innerCell.isVertex())
+							{
+								// Get the connecting edge
+								if (k == 0)
+								{
+									connections = cell.connectsAsSource;
+
+								}
+								else
+								{
+									connections = cell.connectsAsTarget;
+								}
+
+								for (var connIndex = 0; connIndex < connections.length; connIndex++)
+								{
+									if (connections[connIndex].source == innerCell
+											|| connections[connIndex].target == innerCell)
+									{
+										connectedEdgeCount += connections[connIndex].edges
+												.length;
+										connectedEdgeGroupCount++;
+
+										connectedEdges.push(connections[connIndex]);
+									}
+								}
+							}
+							else
+							{
+								connectedEdgeCount += innerCell.edges.length;
+								connectedEdgeGroupCount++;
+								connectedEdges.push(innerCell);
+							}
+						}
+
+						var requiredWidth = (connectedEdgeCount + 1)
+								* this.prefHozEdgeSep;
+
+						// Add a buffer on the edges of the vertex if the edge count allows
+						if (cell.width > requiredWidth
+								+ (2 * this.prefHozEdgeSep))
+						{
+							leftLimit += this.prefHozEdgeSep;
+							rightLimit -= this.prefHozEdgeSep;
+						}
+
+						var availableWidth = rightLimit - leftLimit;
+						var edgeSpacing = availableWidth / connectedEdgeCount;
+
+						var currentX = leftLimit + edgeSpacing / 2.0;
+						var currentYOffset = this.minEdgeJetty - this.prefVertEdgeOff;
+						var maxYOffset = 0;
+
+						for (var j = 0; j < connectedEdges.length; j++)
+						{
+							var numActualEdges = connectedEdges[j].edges
+									.length;
+							var pos = this.jettyPositions[connectedEdges[j].ids[0]];
+							
+							if (pos == null)
+							{
+								pos = [];
+								this.jettyPositions[connectedEdges[j].ids[0]] = pos;
+							}
+
+							if (j < connectedEdgeCount / 2)
+							{
+								currentYOffset += this.prefVertEdgeOff;
+							}
+							else if (j > connectedEdgeCount / 2)
+							{
+								currentYOffset -= this.prefVertEdgeOff;
+							}
+							// Ignore the case if equals, this means the second of 2
+							// jettys with the same y (even number of edges)
+
+							for (var m = 0; m < numActualEdges; m++)
+							{
+								pos[m * 4 + k * 2] = currentX;
+								currentX += edgeSpacing;
+								pos[m * 4 + k * 2 + 1] = currentYOffset;
+							}
+							
+							maxYOffset = Math.max(maxYOffset,
+									currentYOffset);
+						}
+					}
+
+					currentCells = cell.getNextLayerConnectedCells(rankIndex);
+
+					currentRank = rankIndex + 1;
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: setEdgePosition
+ * 
+ * Fixes the control points
+ */
+mxCoordinateAssignment.prototype.setEdgePosition = function(cell)
+{
+	// For parallel edges we need to seperate out the points a
+	// little
+	var offsetX = 0;
+	// Only set the edge control points once
+
+	if (cell.temp[0] != 101207)
+	{
+		var maxRank = cell.maxRank;
+		var minRank = cell.minRank;
+		
+		if (maxRank == minRank)
+		{
+			maxRank = cell.source.maxRank;
+			minRank = cell.target.minRank;
+		}
+		
+		var parallelEdgeCount = 0;
+		var jettys = this.jettyPositions[cell.ids[0]];
+
+		var source = cell.isReversed ? cell.target.cell : cell.source.cell;
+		var graph = this.layout.graph;
+		var layoutReversed = this.orientation == mxConstants.DIRECTION_EAST
+				|| this.orientation == mxConstants.DIRECTION_SOUTH;
+
+		for (var i = 0; i < cell.edges.length; i++)
+		{
+			var realEdge = cell.edges[i];
+			var realSource = this.layout.getVisibleTerminal(realEdge, true);
+
+			//List oldPoints = graph.getPoints(realEdge);
+			var newPoints = [];
+
+			// Single length reversed edges end up with the jettys in the wrong
+			// places. Since single length edges only have jettys, not segment
+			// control points, we just say the edge isn't reversed in this section
+			var reversed = cell.isReversed;
+			
+			if (realSource != source)
+			{
+				// The real edges include all core model edges and these can go
+				// in both directions. If the source of the hierarchical model edge
+				// isn't the source of the specific real edge in this iteration
+				// treat if as reversed
+				reversed = !reversed;
+			}
+
+			// First jetty of edge
+			if (jettys != null)
+			{
+				var arrayOffset = reversed ? 2 : 0;
+				var y = reversed ?
+						(layoutReversed ? this.rankBottomY[minRank] : this.rankTopY[minRank]) :
+							(layoutReversed ? this.rankTopY[maxRank] : this.rankBottomY[maxRank]);
+				var jetty = jettys[parallelEdgeCount * 4 + 1 + arrayOffset];
+				
+				if (reversed != layoutReversed)
+				{
+					jetty = -jetty;
+				}
+				
+				y += jetty;
+				var x = jettys[parallelEdgeCount * 4 + arrayOffset];
+				
+				var modelSource = graph.model.getTerminal(realEdge, true);
+
+				if (this.layout.isPort(modelSource) && graph.model.getParent(modelSource) == realSource)
+				{
+					var state = graph.view.getState(modelSource);
+					
+					if (state != null)
+					{
+						x = state.x;
+					}
+					else
+					{
+						x = realSource.geometry.x + cell.source.width * modelSource.geometry.x;
+					}
+				}
+
+				if (this.orientation == mxConstants.DIRECTION_NORTH
+						|| this.orientation == mxConstants.DIRECTION_SOUTH)
+				{
+					newPoints.push(new mxPoint(x, y));
+					
+					if (this.layout.edgeStyle == mxHierarchicalEdgeStyle.CURVE)
+					{
+						newPoints.push(new mxPoint(x, y + jetty));
+					}
+				}
+				else
+				{
+					newPoints.push(new mxPoint(y, x));
+					
+					if (this.layout.edgeStyle == mxHierarchicalEdgeStyle.CURVE)
+					{
+						newPoints.push(new mxPoint(y + jetty, x));
+					}
+				}
+			}
+
+			// Declare variables to define loop through edge points and 
+			// change direction if edge is reversed
+
+			var loopStart = cell.x.length - 1;
+			var loopLimit = -1;
+			var loopDelta = -1;
+			var currentRank = cell.maxRank - 1;
+
+			if (reversed)
+			{
+				loopStart = 0;
+				loopLimit = cell.x.length;
+				loopDelta = 1;
+				currentRank = cell.minRank + 1;
+			}
+			// Reversed edges need the points inserted in
+			// reverse order
+			for (var j = loopStart; (cell.maxRank != cell.minRank) && j != loopLimit; j += loopDelta)
+			{
+				// The horizontal position in a vertical layout
+				var positionX = cell.x[j] + offsetX;
+
+				// Work out the vertical positions in a vertical layout
+				// in the edge buffer channels above and below this rank
+				var topChannelY = (this.rankTopY[currentRank] + this.rankBottomY[currentRank + 1]) / 2.0;
+				var bottomChannelY = (this.rankTopY[currentRank - 1] + this.rankBottomY[currentRank]) / 2.0;
+
+				if (reversed)
+				{
+					var tmp = topChannelY;
+					topChannelY = bottomChannelY;
+					bottomChannelY = tmp;
+				}
+
+				if (this.orientation == mxConstants.DIRECTION_NORTH ||
+					this.orientation == mxConstants.DIRECTION_SOUTH)
+				{
+					newPoints.push(new mxPoint(positionX, topChannelY));
+					newPoints.push(new mxPoint(positionX, bottomChannelY));
+				}
+				else
+				{
+					newPoints.push(new mxPoint(topChannelY, positionX));
+					newPoints.push(new mxPoint(bottomChannelY, positionX));
+				}
+
+				this.limitX = Math.max(this.limitX, positionX);
+				currentRank += loopDelta;
+			}
+
+			// Second jetty of edge
+			if (jettys != null)
+			{
+				var arrayOffset = reversed ? 2 : 0;
+				var rankY = reversed ?
+						(layoutReversed ? this.rankTopY[maxRank] : this.rankBottomY[maxRank]) :
+							(layoutReversed ? this.rankBottomY[minRank] : this.rankTopY[minRank]);
+				var jetty = jettys[parallelEdgeCount * 4 + 3 - arrayOffset];
+				
+				if (reversed != layoutReversed)
+				{
+					jetty = -jetty;
+				}
+				var y = rankY - jetty;
+				var x = jettys[parallelEdgeCount * 4 + 2 - arrayOffset];
+				
+				var modelTarget = graph.model.getTerminal(realEdge, false);
+				var realTarget = this.layout.getVisibleTerminal(realEdge, false);
+
+				if (this.layout.isPort(modelTarget) && graph.model.getParent(modelTarget) == realTarget)
+				{
+					var state = graph.view.getState(modelTarget);
+					
+					if (state != null)
+					{
+						x = state.x;
+					}
+					else
+					{
+						x = realTarget.geometry.x + cell.target.width * modelTarget.geometry.x;
+					}
+				}
+
+				if (this.orientation == mxConstants.DIRECTION_NORTH ||
+						this.orientation == mxConstants.DIRECTION_SOUTH)
+				{
+					if (this.layout.edgeStyle == mxHierarchicalEdgeStyle.CURVE)
+					{
+						newPoints.push(new mxPoint(x, y - jetty));
+					}
+
+					newPoints.push(new mxPoint(x, y));
+				}
+				else
+				{
+					if (this.layout.edgeStyle == mxHierarchicalEdgeStyle.CURVE)
+					{
+						newPoints.push(new mxPoint(y - jetty, x));
+					}
+
+					newPoints.push(new mxPoint(y, x));
+				}
+			}
+
+			if (cell.isReversed)
+			{
+				this.processReversedEdge(cell, realEdge);
+			}
+
+			this.layout.setEdgePoints(realEdge, newPoints);
+
+			// Increase offset so next edge is drawn next to
+			// this one
+			if (offsetX == 0.0)
+			{
+				offsetX = this.parallelEdgeSpacing;
+			}
+			else if (offsetX > 0)
+			{
+				offsetX = -offsetX;
+			}
+			else
+			{
+				offsetX = -offsetX + this.parallelEdgeSpacing;
+			}
+			
+			parallelEdgeCount++;
+		}
+
+		cell.temp[0] = 101207;
+	}
+};
+
+
+/**
+ * Function: setVertexLocation
+ * 
+ * Fixes the position of the specified vertex.
+ * 
+ * Parameters:
+ * 
+ * cell - the vertex to position
+ */
+mxCoordinateAssignment.prototype.setVertexLocation = function(cell)
+{
+	var realCell = cell.cell;
+	var positionX = cell.x[0] - cell.width / 2;
+	var positionY = cell.y[0] - cell.height / 2;
+
+	this.rankTopY[cell.minRank] = Math.min(this.rankTopY[cell.minRank], positionY);
+	this.rankBottomY[cell.minRank] = Math.max(this.rankBottomY[cell.minRank],
+			positionY + cell.height);
+
+	if (this.orientation == mxConstants.DIRECTION_NORTH ||
+		this.orientation == mxConstants.DIRECTION_SOUTH)
+	{
+		this.layout.setVertexLocation(realCell, positionX, positionY);
+	}
+	else
+	{
+		this.layout.setVertexLocation(realCell, positionY, positionX);
+	}
+
+	this.limitX = Math.max(this.limitX, positionX + cell.width);
+};
+
+/**
+ * Function: processReversedEdge
+ * 
+ * Hook to add additional processing
+ * 
+ * Parameters:
+ * 
+ * edge - the hierarchical model edge
+ * realEdge - the real edge in the graph
+ */
+mxCoordinateAssignment.prototype.processReversedEdge = function(graph, model)
+{
+	// hook for subclassers
+};
+
+/**
+ * Class: WeightedCellSorter
+ * 
+ * A utility class used to track cells whilst sorting occurs on the weighted
+ * sum of their connected edges. Does not violate (x.compareTo(y)==0) ==
+ * (x.equals(y))
+ *
+ * Constructor: WeightedCellSorter
+ * 
+ * Constructs a new weighted cell sorted for the given cell and weight.
+ */
+function WeightedCellSorter(cell, weightedValue)
+{
+	this.cell = cell;
+	this.weightedValue = weightedValue;
+};
+
+/**
+ * Variable: weightedValue
+ * 
+ * The weighted value of the cell stored.
+ */
+WeightedCellSorter.prototype.weightedValue = 0;
+
+/**
+ * Variable: nudge
+ * 
+ * Whether or not to flip equal weight values.
+ */
+WeightedCellSorter.prototype.nudge = false;
+
+/**
+ * Variable: visited
+ * 
+ * Whether or not this cell has been visited in the current assignment.
+ */
+WeightedCellSorter.prototype.visited = false;
+
+/**
+ * Variable: rankIndex
+ * 
+ * The index this cell is in the model rank.
+ */
+WeightedCellSorter.prototype.rankIndex = null;
+
+/**
+ * Variable: cell
+ * 
+ * The cell whose median value is being calculated.
+ */
+WeightedCellSorter.prototype.cell = null;
+
+/**
+ * Function: compare
+ * 
+ * Compares two WeightedCellSorters.
+ */
+WeightedCellSorter.prototype.compare = function(a, b)
+{
+	if (a != null && b != null)
+	{
+		if (b.weightedValue > a.weightedValue)
+		{
+			return -1;
+		}
+		else if (b.weightedValue < a.weightedValue)
+		{
+			return 1;
+		}
+		else
+		{
+			if (b.nudge)
+			{
+				return -1;
+			}
+			else
+			{
+				return 1;
+			}
+		}
+	}
+	else
+	{
+		return 0;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxSwimlaneOrdering
+ * 
+ * An implementation of the first stage of the Sugiyama layout. Straightforward
+ * longest path calculation of layer assignment
+ * 
+ * Constructor: mxSwimlaneOrdering
+ *
+ * Creates a cycle remover for the given internal model.
+ */
+function mxSwimlaneOrdering(layout)
+{
+	this.layout = layout;
+};
+
+/**
+ * Extends mxHierarchicalLayoutStage.
+ */
+mxSwimlaneOrdering.prototype = new mxHierarchicalLayoutStage();
+mxSwimlaneOrdering.prototype.constructor = mxSwimlaneOrdering;
+
+/**
+ * Variable: layout
+ * 
+ * Reference to the enclosing <mxHierarchicalLayout>.
+ */
+mxSwimlaneOrdering.prototype.layout = null;
+
+/**
+ * Function: execute
+ * 
+ * Takes the graph detail and configuration information within the facade
+ * and creates the resulting laid out graph within that facade for further
+ * use.
+ */
+mxSwimlaneOrdering.prototype.execute = function(parent)
+{
+	var model = this.layout.getModel();
+	var seenNodes = new Object();
+	var unseenNodes = mxUtils.clone(model.vertexMapper, null, true);
+	
+	// Perform a dfs through the internal model. If a cycle is found,
+	// reverse it.
+	var rootsArray = null;
+	
+	if (model.roots != null)
+	{
+		var modelRoots = model.roots;
+		rootsArray = [];
+		
+		for (var i = 0; i < modelRoots.length; i++)
+		{
+			var nodeId = mxCellPath.create(modelRoots[i]);
+			rootsArray[i] = model.vertexMapper.get(modelRoots[i]);
+		}
+	}
+
+	model.visit(function(parent, node, connectingEdge, layer, seen)
+	{
+		// Check if the cell is in it's own ancestor list, if so
+		// invert the connecting edge and reverse the target/source
+		// relationship to that edge in the parent and the cell
+		// Ancestor hashes only line up within a swimlane
+		var isAncestor = parent != null && parent.swimlaneIndex == node.swimlaneIndex && node.isAncestor(parent);
+
+		// If the source->target swimlane indices go from higher to
+		// lower, the edge is reverse
+		var reversedOverSwimlane = parent != null && connectingEdge != null &&
+						parent.swimlaneIndex < node.swimlaneIndex && connectingEdge.source == node;
+
+		if (isAncestor)
+		{
+			connectingEdge.invert();
+			mxUtils.remove(connectingEdge, parent.connectsAsSource);
+			node.connectsAsSource.push(connectingEdge);
+			parent.connectsAsTarget.push(connectingEdge);
+			mxUtils.remove(connectingEdge, node.connectsAsTarget);
+		}
+		else if (reversedOverSwimlane)
+		{
+			connectingEdge.invert();
+			mxUtils.remove(connectingEdge, parent.connectsAsTarget);
+			node.connectsAsTarget.push(connectingEdge);
+			parent.connectsAsSource.push(connectingEdge);
+			mxUtils.remove(connectingEdge, node.connectsAsSource);
+		}
+		
+		var cellId = mxCellPath.create(node.cell);
+		seenNodes[cellId] = node;
+		delete unseenNodes[cellId];
+	}, rootsArray, true, null);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxHierarchicalLayout
+ * 
+ * A hierarchical layout algorithm.
+ * 
+ * Constructor: mxHierarchicalLayout
+ *
+ * Constructs a new hierarchical layout algorithm.
+ *
+ * Arguments:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * orientation - Optional constant that defines the orientation of this
+ * layout.
+ * deterministic - Optional boolean that specifies if this layout should be
+ * deterministic. Default is true.
+ */
+function mxHierarchicalLayout(graph, orientation, deterministic)
+{
+	mxGraphLayout.call(this, graph);
+	this.orientation = (orientation != null) ? orientation : mxConstants.DIRECTION_NORTH;
+	this.deterministic = (deterministic != null) ? deterministic : true;
+};
+
+var mxHierarchicalEdgeStyle =
+{
+	ORTHOGONAL: 1,
+	POLYLINE: 2,
+	STRAIGHT: 3,
+	CURVE: 4
+};
+
+/**
+ * Extends mxGraphLayout.
+ */
+mxHierarchicalLayout.prototype = new mxGraphLayout();
+mxHierarchicalLayout.prototype.constructor = mxHierarchicalLayout;
+
+/**
+ * Variable: roots
+ * 
+ * Holds the array of <mxCell> that this layout contains.
+ */
+mxHierarchicalLayout.prototype.roots = null;
+
+/**
+ * Variable: resizeParent
+ * 
+ * Specifies if the parent should be resized after the layout so that it
+ * contains all the child cells. Default is false. See also <parentBorder>.
+ */
+mxHierarchicalLayout.prototype.resizeParent = false;
+
+/**
+ * Variable: maintainParentLocation
+ * 
+ * Specifies if the parent location should be maintained, so that the
+ * top, left corner stays the same before and after execution of
+ * the layout. Default is false for backwards compatibility.
+ */
+mxHierarchicalLayout.prototype.maintainParentLocation = false;
+
+/**
+ * Variable: moveParent
+ * 
+ * Specifies if the parent should be moved if <resizeParent> is enabled.
+ * Default is false.
+ */
+mxHierarchicalLayout.prototype.moveParent = false;
+
+/**
+ * Variable: parentBorder
+ * 
+ * The border to be added around the children if the parent is to be
+ * resized using <resizeParent>. Default is 0.
+ */
+mxHierarchicalLayout.prototype.parentBorder = 0;
+
+/**
+ * Variable: intraCellSpacing
+ * 
+ * The spacing buffer added between cells on the same layer. Default is 30.
+ */
+mxHierarchicalLayout.prototype.intraCellSpacing = 30;
+
+/**
+ * Variable: interRankCellSpacing
+ * 
+ * The spacing buffer added between cell on adjacent layers. Default is 50.
+ */
+mxHierarchicalLayout.prototype.interRankCellSpacing = 100;
+
+/**
+ * Variable: interHierarchySpacing
+ * 
+ * The spacing buffer between unconnected hierarchies. Default is 60.
+ */
+mxHierarchicalLayout.prototype.interHierarchySpacing = 60;
+
+/**
+ * Variable: parallelEdgeSpacing
+ * 
+ * The distance between each parallel edge on each ranks for long edges
+ */
+mxHierarchicalLayout.prototype.parallelEdgeSpacing = 10;
+
+/**
+ * Variable: orientation
+ * 
+ * The position of the root node(s) relative to the laid out graph in.
+ * Default is <mxConstants.DIRECTION_NORTH>.
+ */
+mxHierarchicalLayout.prototype.orientation = mxConstants.DIRECTION_NORTH;
+
+/**
+ * Variable: fineTuning
+ * 
+ * Whether or not to perform local optimisations and iterate multiple times
+ * through the algorithm. Default is true.
+ */
+mxHierarchicalLayout.prototype.fineTuning = true;
+
+/**
+ * 
+ * Variable: tightenToSource
+ * 
+ * Whether or not to tighten the assigned ranks of vertices up towards
+ * the source cells.
+ */
+mxHierarchicalLayout.prototype.tightenToSource = true;
+
+/**
+ * Variable: disableEdgeStyle
+ * 
+ * Specifies if the STYLE_NOEDGESTYLE flag should be set on edges that are
+ * modified by the result. Default is true.
+ */
+mxHierarchicalLayout.prototype.disableEdgeStyle = true;
+
+/**
+ * Variable: traverseAncestors
+ * 
+ * Whether or not to drill into child cells and layout in reverse
+ * group order. This also cause the layout to navigate edges whose 
+ * terminal vertices  * have different parents but are in the same 
+ * ancestry chain
+ */
+mxHierarchicalLayout.prototype.traverseAncestors = true;
+
+/**
+ * Variable: model
+ * 
+ * The internal <mxGraphHierarchyModel> formed of the layout.
+ */
+mxHierarchicalLayout.prototype.model = null;
+
+/**
+ * Variable: edgesSet
+ * 
+ * A cache of edges whose source terminal is the key
+ */
+mxHierarchicalLayout.prototype.edgesCache = null;
+
+/**
+ * Variable: edgesSet
+ * 
+ * A cache of edges whose source terminal is the key
+ */
+mxHierarchicalLayout.prototype.edgeSourceTermCache = null;
+
+/**
+ * Variable: edgesSet
+ * 
+ * A cache of edges whose source terminal is the key
+ */
+mxHierarchicalLayout.prototype.edgesTargetTermCache = null;
+
+/**
+ * Variable: edgeStyle
+ * 
+ * The style to apply between cell layers to edge segments
+ */
+mxHierarchicalLayout.prototype.edgeStyle = mxHierarchicalEdgeStyle.POLYLINE;
+
+/**
+ * Function: getModel
+ * 
+ * Returns the internal <mxGraphHierarchyModel> for this layout algorithm.
+ */
+mxHierarchicalLayout.prototype.getModel = function()
+{
+	return this.model;
+};
+
+/**
+ * Function: execute
+ * 
+ * Executes the layout for the children of the specified parent.
+ * 
+ * Parameters:
+ * 
+ * parent - Parent <mxCell> that contains the children to be laid out.
+ * roots - Optional starting roots of the layout.
+ */
+mxHierarchicalLayout.prototype.execute = function(parent, roots)
+{
+	this.parent = parent;
+	var model = this.graph.model;
+	this.edgesCache = new mxDictionary();
+	this.edgeSourceTermCache = new mxDictionary();
+	this.edgesTargetTermCache = new mxDictionary();
+
+	if (roots != null && !(roots instanceof Array))
+	{
+		roots = [roots];
+	}
+	
+	// If the roots are set and the parent is set, only
+	// use the roots that are some dependent of the that
+	// parent.
+	// If just the root are set, use them as-is
+	// If just the parent is set use it's immediate
+	// children as the initial set
+
+	if (roots == null && parent == null)
+	{
+		// TODO indicate the problem
+		return;
+	}
+	
+	//  Maintaining parent location
+	this.parentX = null;
+	this.parentY = null;
+	
+	if (parent != this.root && model.isVertex(parent) != null && this.maintainParentLocation)
+	{
+		var geo = this.graph.getCellGeometry(parent);
+		
+		if (geo != null)
+		{
+			this.parentX = geo.x;
+			this.parentY = geo.y;
+		}
+	}
+	
+	if (roots != null)
+	{
+		var rootsCopy = [];
+
+		for (var i = 0; i < roots.length; i++)
+		{
+			var ancestor = parent != null ? model.isAncestor(parent, roots[i]) : true;
+			
+			if (ancestor && model.isVertex(roots[i]))
+			{
+				rootsCopy.push(roots[i]);
+			}
+		}
+
+		this.roots = rootsCopy;
+	}
+	
+	model.beginUpdate();
+	try
+	{
+		this.run(parent);
+		
+		if (this.resizeParent && !this.graph.isCellCollapsed(parent))
+		{
+			this.graph.updateGroupBounds([parent], this.parentBorder, this.moveParent);
+		}
+		
+		// Maintaining parent location
+		if (this.parentX != null && this.parentY != null)
+		{
+			var geo = this.graph.getCellGeometry(parent);
+			
+			if (geo != null)
+			{
+				geo = geo.clone();
+				geo.x = this.parentX;
+				geo.y = this.parentY;
+				model.setGeometry(parent, geo);
+			}
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+};
+
+/**
+ * Function: findRoots
+ * 
+ * Returns all visible children in the given parent which do not have
+ * incoming edges. If the result is empty then the children with the
+ * maximum difference between incoming and outgoing edges are returned.
+ * This takes into account edges that are being promoted to the given
+ * root due to invisible children or collapsed cells.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose children should be checked.
+ * vertices - array of vertices to limit search to
+ */
+mxHierarchicalLayout.prototype.findRoots = function(parent, vertices)
+{
+	var roots = [];
+	
+	if (parent != null && vertices != null)
+	{
+		var model = this.graph.model;
+		var best = null;
+		var maxDiff = -100000;
+		
+		for (var i in vertices)
+		{
+			var cell = vertices[i];
+
+			if (model.isVertex(cell) && this.graph.isCellVisible(cell))
+			{
+				var conns = this.getEdges(cell);
+				var fanOut = 0;
+				var fanIn = 0;
+
+				for (var k = 0; k < conns.length; k++)
+				{
+					var src = this.getVisibleTerminal(conns[k], true);
+
+					if (src == cell)
+					{
+						fanOut++;
+					}
+					else
+					{
+						fanIn++;
+					}
+				}
+
+				if (fanIn == 0 && fanOut > 0)
+				{
+					roots.push(cell);
+				}
+
+				var diff = fanOut - fanIn;
+
+				if (diff > maxDiff)
+				{
+					maxDiff = diff;
+					best = cell;
+				}
+			}
+		}
+		
+		if (roots.length == 0 && best != null)
+		{
+			roots.push(best);
+		}
+	}
+	
+	return roots;
+};
+
+/**
+ * Function: getEdges
+ * 
+ * Returns the connected edges for the given cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose edges should be returned.
+ */
+mxHierarchicalLayout.prototype.getEdges = function(cell)
+{
+	var cachedEdges = this.edgesCache.get(cell);
+	
+	if (cachedEdges != null)
+	{
+		return cachedEdges;
+	}
+
+	var model = this.graph.model;
+	var edges = [];
+	var isCollapsed = this.graph.isCellCollapsed(cell);
+	var childCount = model.getChildCount(cell);
+
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = model.getChildAt(cell, i);
+
+		if (this.isPort(child))
+		{
+			edges = edges.concat(model.getEdges(child, true, true));
+		}
+		else if (isCollapsed || !this.graph.isCellVisible(child))
+		{
+			edges = edges.concat(model.getEdges(child, true, true));
+		}
+	}
+
+	edges = edges.concat(model.getEdges(cell, true, true));
+	var result = [];
+	
+	for (var i = 0; i < edges.length; i++)
+	{
+		var source = this.getVisibleTerminal(edges[i], true);
+		var target = this.getVisibleTerminal(edges[i], false);
+		
+		if ((source == target) || ((source != target) && ((target == cell && (this.parent == null || this.graph.isValidAncestor(source, this.parent, this.traverseAncestors))) ||
+			(source == cell && (this.parent == null ||
+					this.graph.isValidAncestor(target, this.parent, this.traverseAncestors))))))
+		{
+			result.push(edges[i]);
+		}
+	}
+
+	this.edgesCache.put(cell, result);
+
+	return result;
+};
+
+/**
+ * Function: getVisibleTerminal
+ * 
+ * Helper function to return visible terminal for edge allowing for ports
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose edges should be returned.
+ * source - Boolean that specifies whether the source or target terminal is to be returned
+ */
+mxHierarchicalLayout.prototype.getVisibleTerminal = function(edge, source)
+{
+	var terminalCache = this.edgesTargetTermCache;
+	
+	if (source)
+	{
+		terminalCache = this.edgeSourceTermCache;
+	}
+
+	var term = terminalCache.get(edge);
+
+	if (term != null)
+	{
+		return term;
+	}
+
+	var state = this.graph.view.getState(edge);
+	
+	var terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
+	
+	if (terminal == null)
+	{
+		terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
+	}
+
+	if (terminal != null)
+	{
+		if (this.isPort(terminal))
+		{
+			terminal = this.graph.model.getParent(terminal);
+		}
+		
+		terminalCache.put(edge, terminal);
+	}
+
+	return terminal;
+};
+
+/**
+ * Function: run
+ * 
+ * The API method used to exercise the layout upon the graph description
+ * and produce a separate description of the vertex position and edge
+ * routing changes made. It runs each stage of the layout that has been
+ * created.
+ */
+mxHierarchicalLayout.prototype.run = function(parent)
+{
+	// Separate out unconnected hierarchies
+	var hierarchyVertices = [];
+	var allVertexSet = [];
+
+	if (this.roots == null && parent != null)
+	{
+		var filledVertexSet = Object();
+		this.filterDescendants(parent, filledVertexSet);
+
+		this.roots = [];
+		var filledVertexSetEmpty = true;
+
+		// Poor man's isSetEmpty
+		for (var key in filledVertexSet)
+		{
+			if (filledVertexSet[key] != null)
+			{
+				filledVertexSetEmpty = false;
+				break;
+			}
+		}
+
+		while (!filledVertexSetEmpty)
+		{
+			var candidateRoots = this.findRoots(parent, filledVertexSet);
+			
+			// If the candidate root is an unconnected group cell, remove it from
+			// the layout. We may need a custom set that holds such groups and forces
+			// them to be processed for resizing and/or moving.
+			
+
+			for (var i = 0; i < candidateRoots.length; i++)
+			{
+				var vertexSet = Object();
+				hierarchyVertices.push(vertexSet);
+
+				this.traverse(candidateRoots[i], true, null, allVertexSet, vertexSet,
+						hierarchyVertices, filledVertexSet);
+			}
+
+			for (var i = 0; i < candidateRoots.length; i++)
+			{
+				this.roots.push(candidateRoots[i]);
+			}
+			
+			filledVertexSetEmpty = true;
+			
+			// Poor man's isSetEmpty
+			for (var key in filledVertexSet)
+			{
+				if (filledVertexSet[key] != null)
+				{
+					filledVertexSetEmpty = false;
+					break;
+				}
+			}
+		}
+	}
+	else
+	{
+		// Find vertex set as directed traversal from roots
+
+		for (var i = 0; i < this.roots.length; i++)
+		{
+			var vertexSet = Object();
+			hierarchyVertices.push(vertexSet);
+
+			this.traverse(this.roots[i], true, null, allVertexSet, vertexSet,
+					hierarchyVertices, null);
+		}
+	}
+
+	// Iterate through the result removing parents who have children in this layout
+	
+	// Perform a layout for each seperate hierarchy
+	// Track initial coordinate x-positioning
+	var initialX = 0;
+
+	for (var i = 0; i < hierarchyVertices.length; i++)
+	{
+		var vertexSet = hierarchyVertices[i];
+		var tmp = [];
+		
+		for (var key in vertexSet)
+		{
+			tmp.push(vertexSet[key]);
+		}
+		
+		this.model = new mxGraphHierarchyModel(this, tmp, this.roots,
+			parent, this.tightenToSource);
+
+		this.cycleStage(parent);
+		this.layeringStage();
+		
+		this.crossingStage(parent);
+		initialX = this.placementStage(initialX, parent);
+	}
+};
+
+/**
+ * Function: filterDescendants
+ * 
+ * Creates an array of descendant cells
+ */
+mxHierarchicalLayout.prototype.filterDescendants = function(cell, result)
+{
+	var model = this.graph.model;
+
+	if (model.isVertex(cell) && cell != this.parent && this.graph.isCellVisible(cell))
+	{
+		result[mxObjectIdentity.get(cell)] = cell;
+	}
+
+	if (this.traverseAncestors || cell == this.parent
+			&& this.graph.isCellVisible(cell))
+	{
+		var childCount = model.getChildCount(cell);
+
+		for (var i = 0; i < childCount; i++)
+		{
+			var child = model.getChildAt(cell, i);
+			
+			// Ignore ports in the layout vertex list, they are dealt with
+			// in the traversal mechanisms
+			if (!this.isPort(child))
+			{
+				this.filterDescendants(child, result);
+			}
+		}
+	}
+};
+
+/**
+ * Function: isPort
+ * 
+ * Returns true if the given cell is a "port", that is, when connecting to
+ * it, its parent is the connecting vertex in terms of graph traversal
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the port.
+ */
+mxHierarchicalLayout.prototype.isPort = function(cell)
+{
+	if (cell.geometry.relative)
+	{
+		return true;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: getEdgesBetween
+ * 
+ * Returns the edges between the given source and target. This takes into
+ * account collapsed and invisible cells and ports.
+ * 
+ * Parameters:
+ * 
+ * source -
+ * target -
+ * directed -
+ */
+mxHierarchicalLayout.prototype.getEdgesBetween = function(source, target, directed)
+{
+	directed = (directed != null) ? directed : false;
+	var edges = this.getEdges(source);
+	var result = [];
+
+	// Checks if the edge is connected to the correct
+	// cell and returns the first match
+	for (var i = 0; i < edges.length; i++)
+	{
+		var src = this.getVisibleTerminal(edges[i], true);
+		var trg = this.getVisibleTerminal(edges[i], false);
+
+		if ((src == source && trg == target) || (!directed && src == target && trg == source))
+		{
+			result.push(edges[i]);
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Traverses the (directed) graph invoking the given function for each
+ * visited vertex and edge. The function is invoked with the current vertex
+ * and the incoming edge as a parameter. This implementation makes sure
+ * each vertex is only visited once. The function may return false if the
+ * traversal should stop at the given vertex.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> that represents the vertex where the traversal starts.
+ * directed - boolean indicating if edges should only be traversed
+ * from source to target. Default is true.
+ * edge - Optional <mxCell> that represents the incoming edge. This is
+ * null for the first step of the traversal.
+ * allVertices - Array of cell paths for the visited cells.
+ */
+mxHierarchicalLayout.prototype.traverse = function(vertex, directed, edge, allVertices, currentComp,
+											hierarchyVertices, filledVertexSet)
+{
+	if (vertex != null && allVertices != null)
+	{
+		// Has this vertex been seen before in any traversal
+		// And if the filled vertex set is populated, only 
+		// process vertices in that it contains
+		var vertexID = mxObjectIdentity.get(vertex);
+		
+		if ((allVertices[vertexID] == null)
+				&& (filledVertexSet == null ? true : filledVertexSet[vertexID] != null))
+		{
+			if (currentComp[vertexID] == null)
+			{
+				currentComp[vertexID] = vertex;
+			}
+			if (allVertices[vertexID] == null)
+			{
+				allVertices[vertexID] = vertex;
+			}
+
+			if (filledVertexSet !== null)
+			{
+				delete filledVertexSet[vertexID];
+			}
+
+			var edges = this.getEdges(vertex);
+			var edgeIsSource = [];
+
+			for (var i = 0; i < edges.length; i++)
+			{
+				edgeIsSource[i] = (this.getVisibleTerminal(edges[i], true) == vertex);
+			}
+
+			for (var i = 0; i < edges.length; i++)
+			{
+				if (!directed || edgeIsSource[i])
+				{
+					var next = this.getVisibleTerminal(edges[i], !edgeIsSource[i]);
+					
+					// Check whether there are more edges incoming from the target vertex than outgoing
+					// The hierarchical model treats bi-directional parallel edges as being sourced
+					// from the more "sourced" terminal. If the directions are equal in number, the direction
+					// is that of the natural direction from the roots of the layout.
+					// The checks below are slightly more verbose than need be for performance reasons
+					var netCount = 1;
+
+					for (var j = 0; j < edges.length; j++)
+					{
+						if (j == i)
+						{
+							continue;
+						}
+						else
+						{
+							var isSource2 = edgeIsSource[j];
+							var otherTerm = this.getVisibleTerminal(edges[j], !isSource2);
+							
+							if (otherTerm == next)
+							{
+								if (isSource2)
+								{
+									netCount++;
+								}
+								else
+								{
+									netCount--;
+								}
+							}
+						}
+					}
+
+					if (netCount >= 0)
+					{
+						currentComp = this.traverse(next, directed, edges[i], allVertices,
+							currentComp, hierarchyVertices,
+							filledVertexSet);
+					}
+				}
+			}
+		}
+		else
+		{
+			if (currentComp[vertexID] == null)
+			{
+				// We've seen this vertex before, but not in the current component
+				// This component and the one it's in need to be merged
+
+				for (var i = 0; i < hierarchyVertices.length; i++)
+				{
+					var comp = hierarchyVertices[i];
+
+					if (comp[vertexID] != null)
+					{
+						for (var key in comp)
+						{
+							currentComp[key] = comp[key];
+						}
+						
+						// Remove the current component from the hierarchy set
+						hierarchyVertices.splice(i, 1);
+						return currentComp;
+					}
+				}
+			}
+		}
+	}
+	
+	return currentComp;
+};
+
+/**
+ * Function: cycleStage
+ * 
+ * Executes the cycle stage using mxMinimumCycleRemover.
+ */
+mxHierarchicalLayout.prototype.cycleStage = function(parent)
+{
+	var cycleStage = new mxMinimumCycleRemover(this);
+	cycleStage.execute(parent);
+};
+
+/**
+ * Function: layeringStage
+ * 
+ * Implements first stage of a Sugiyama layout.
+ */
+mxHierarchicalLayout.prototype.layeringStage = function()
+{
+	this.model.initialRank();
+	this.model.fixRanks();
+};
+
+/**
+ * Function: crossingStage
+ * 
+ * Executes the crossing stage using mxMedianHybridCrossingReduction.
+ */
+mxHierarchicalLayout.prototype.crossingStage = function(parent)
+{
+	var crossingStage = new mxMedianHybridCrossingReduction(this);
+	crossingStage.execute(parent);
+};
+
+/**
+ * Function: placementStage
+ * 
+ * Executes the placement stage using mxCoordinateAssignment.
+ */
+mxHierarchicalLayout.prototype.placementStage = function(initialX, parent)
+{
+	var placementStage = new mxCoordinateAssignment(this, this.intraCellSpacing,
+			this.interRankCellSpacing, this.orientation, initialX,
+			this.parallelEdgeSpacing);
+	placementStage.fineTuning = this.fineTuning;
+	placementStage.execute(parent);
+	
+	return placementStage.limitX + this.interHierarchySpacing;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxSwimlaneLayout
+ * 
+ * A hierarchical layout algorithm.
+ * 
+ * Constructor: mxSwimlaneLayout
+ *
+ * Constructs a new hierarchical layout algorithm.
+ *
+ * Arguments:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * orientation - Optional constant that defines the orientation of this
+ * layout.
+ * deterministic - Optional boolean that specifies if this layout should be
+ * deterministic. Default is true.
+ */
+function mxSwimlaneLayout(graph, orientation, deterministic)
+{
+	mxGraphLayout.call(this, graph);
+	this.orientation = (orientation != null) ? orientation : mxConstants.DIRECTION_NORTH;
+	this.deterministic = (deterministic != null) ? deterministic : true;
+};
+
+/**
+ * Extends mxGraphLayout.
+ */
+mxSwimlaneLayout.prototype = new mxGraphLayout();
+mxSwimlaneLayout.prototype.constructor = mxSwimlaneLayout;
+
+/**
+ * Variable: roots
+ * 
+ * Holds the array of <mxCell> that this layout contains.
+ */
+mxSwimlaneLayout.prototype.roots = null;
+
+/**
+ * Variable: swimlanes
+ * 
+ * Holds the array of <mxCell> of the ordered swimlanes to lay out
+ */
+mxSwimlaneLayout.prototype.swimlanes = null;
+
+/**
+ * Variable: dummyVertices
+ * 
+ * Holds an array of <mxCell> of dummy vertices inserted during the layout
+ * to pad out empty swimlanes
+ */
+mxSwimlaneLayout.prototype.dummyVertices = null;
+
+/**
+ * Variable: dummyVertexWidth
+ * 
+ * The cell width of any dummy vertices inserted
+ */
+mxSwimlaneLayout.prototype.dummyVertexWidth = 50;
+
+/**
+ * Variable: resizeParent
+ * 
+ * Specifies if the parent should be resized after the layout so that it
+ * contains all the child cells. Default is false. See also <parentBorder>.
+ */
+mxSwimlaneLayout.prototype.resizeParent = false;
+
+/**
+ * Variable: maintainParentLocation
+ * 
+ * Specifies if the parent location should be maintained, so that the
+ * top, left corner stays the same before and after execution of
+ * the layout. Default is false for backwards compatibility.
+ */
+mxSwimlaneLayout.prototype.maintainParentLocation = false;
+
+/**
+ * Variable: moveParent
+ * 
+ * Specifies if the parent should be moved if <resizeParent> is enabled.
+ * Default is false.
+ */
+mxSwimlaneLayout.prototype.moveParent = false;
+
+/**
+ * Variable: parentBorder
+ * 
+ * The border to be added around the children if the parent is to be
+ * resized using <resizeParent>. Default is 0.
+ */
+mxSwimlaneLayout.prototype.parentBorder = 30;
+
+/**
+ * Variable: intraCellSpacing
+ * 
+ * The spacing buffer added between cells on the same layer. Default is 30.
+ */
+mxSwimlaneLayout.prototype.intraCellSpacing = 30;
+
+/**
+ * Variable: interRankCellSpacing
+ * 
+ * The spacing buffer added between cell on adjacent layers. Default is 50.
+ */
+mxSwimlaneLayout.prototype.interRankCellSpacing = 100;
+
+/**
+ * Variable: interHierarchySpacing
+ * 
+ * The spacing buffer between unconnected hierarchies. Default is 60.
+ */
+mxSwimlaneLayout.prototype.interHierarchySpacing = 60;
+
+/**
+ * Variable: parallelEdgeSpacing
+ * 
+ * The distance between each parallel edge on each ranks for long edges
+ */
+mxSwimlaneLayout.prototype.parallelEdgeSpacing = 10;
+
+/**
+ * Variable: orientation
+ * 
+ * The position of the root node(s) relative to the laid out graph in.
+ * Default is <mxConstants.DIRECTION_NORTH>.
+ */
+mxSwimlaneLayout.prototype.orientation = mxConstants.DIRECTION_NORTH;
+
+/**
+ * Variable: fineTuning
+ * 
+ * Whether or not to perform local optimisations and iterate multiple times
+ * through the algorithm. Default is true.
+ */
+mxSwimlaneLayout.prototype.fineTuning = true;
+
+/**
+ * 
+ * Variable: tightenToSource
+ * 
+ * Whether or not to tighten the assigned ranks of vertices up towards
+ * the source cells.
+ */
+mxSwimlaneLayout.prototype.tightenToSource = true;
+
+/**
+ * Variable: disableEdgeStyle
+ * 
+ * Specifies if the STYLE_NOEDGESTYLE flag should be set on edges that are
+ * modified by the result. Default is true.
+ */
+mxSwimlaneLayout.prototype.disableEdgeStyle = true;
+
+/**
+ * Variable: traverseAncestors
+ * 
+ * Whether or not to drill into child cells and layout in reverse
+ * group order. This also cause the layout to navigate edges whose 
+ * terminal vertices  * have different parents but are in the same 
+ * ancestry chain
+ */
+mxSwimlaneLayout.prototype.traverseAncestors = true;
+
+/**
+ * Variable: model
+ * 
+ * The internal <mxSwimlaneModel> formed of the layout.
+ */
+mxSwimlaneLayout.prototype.model = null;
+
+/**
+ * Variable: edgesSet
+ * 
+ * A cache of edges whose source terminal is the key
+ */
+mxSwimlaneLayout.prototype.edgesCache = null;
+
+/**
+ * Variable: edgesSet
+ * 
+ * A cache of edges whose source terminal is the key
+ */
+mxHierarchicalLayout.prototype.edgeSourceTermCache = null;
+
+/**
+ * Variable: edgesSet
+ * 
+ * A cache of edges whose source terminal is the key
+ */
+mxHierarchicalLayout.prototype.edgesTargetTermCache = null;
+
+/**
+ * Variable: edgeStyle
+ * 
+ * The style to apply between cell layers to edge segments
+ */
+mxHierarchicalLayout.prototype.edgeStyle = mxHierarchicalEdgeStyle.POLYLINE;
+
+/**
+ * Function: getModel
+ * 
+ * Returns the internal <mxSwimlaneModel> for this layout algorithm.
+ */
+mxSwimlaneLayout.prototype.getModel = function()
+{
+	return this.model;
+};
+
+/**
+ * Function: execute
+ * 
+ * Executes the layout for the children of the specified parent.
+ * 
+ * Parameters:
+ * 
+ * parent - Parent <mxCell> that contains the children to be laid out.
+ * swimlanes - Ordered array of swimlanes to be laid out
+ */
+mxSwimlaneLayout.prototype.execute = function(parent, swimlanes)
+{
+	this.parent = parent;
+	var model = this.graph.model;
+	this.edgesCache = new mxDictionary();
+	this.edgeSourceTermCache = new mxDictionary();
+	this.edgesTargetTermCache = new mxDictionary();
+
+	// If the roots are set and the parent is set, only
+	// use the roots that are some dependent of the that
+	// parent.
+	// If just the root are set, use them as-is
+	// If just the parent is set use it's immediate
+	// children as the initial set
+
+	if (swimlanes == null || swimlanes.length < 1)
+	{
+		// TODO indicate the problem
+		return;
+	}
+
+	if (parent == null)
+	{
+		parent = model.getParent(swimlanes[0]);
+	}
+
+	//  Maintaining parent location
+	this.parentX = null;
+	this.parentY = null;
+	
+	if (parent != this.root && model.isVertex(parent) != null && this.maintainParentLocation)
+	{
+		var geo = this.graph.getCellGeometry(parent);
+		
+		if (geo != null)
+		{
+			this.parentX = geo.x;
+			this.parentY = geo.y;
+		}
+	}
+
+	this.swimlanes = swimlanes;
+	this.dummyVertices = [];
+	// Check the swimlanes all have vertices
+	// in them
+	for (var i = 0; i < swimlanes.length; i++)
+	{
+		var children = this.graph.getChildCells(swimlanes[i]);
+		
+		if (children == null || children.length == 0)
+		{
+			var vertex = this.graph.insertVertex(swimlanes[i], null, null, 0, 0, this.dummyVertexWidth, 0);
+			this.dummyVertices.push(vertex);
+		}
+	}
+	
+	model.beginUpdate();
+	try
+	{
+		this.run(parent);
+		
+		if (this.resizeParent && !this.graph.isCellCollapsed(parent))
+		{
+			this.graph.updateGroupBounds([parent], this.parentBorder, this.moveParent);
+		}
+		
+		// Maintaining parent location
+		if (this.parentX != null && this.parentY != null)
+		{
+			var geo = this.graph.getCellGeometry(parent);
+			
+			if (geo != null)
+			{
+				geo = geo.clone();
+				geo.x = this.parentX;
+				geo.y = this.parentY;
+				model.setGeometry(parent, geo);
+			}
+		}
+
+		this.graph.removeCells(this.dummyVertices);
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+};
+
+/**
+ * Function: updateGroupBounds
+ * 
+ * Updates the bounds of the given array of groups so that it includes
+ * all child vertices.
+ * 
+ */
+mxSwimlaneLayout.prototype.updateGroupBounds = function()
+{
+	// Get all vertices and edge in the layout
+	var cells = [];
+	var model = this.model;
+	
+	for (var key in model.edgeMapper)
+	{
+		var edge = model.edgeMapper[key];
+		
+		for (var i = 0; i < edge.edges.length; i++)
+		{
+			cells.push(edge.edges[i]);
+		}
+	}
+	
+	var layoutBounds = this.graph.getBoundingBoxFromGeometry(cells, true);
+	var childBounds = [];
+
+	for (var i = 0; i < this.swimlanes.length; i++)
+	{
+		var lane = this.swimlanes[i];
+		var geo = this.graph.getCellGeometry(lane);
+		
+		if (geo != null)
+		{
+			var children = this.graph.getChildCells(lane);
+			
+			var size = (this.graph.isSwimlane(lane)) ?
+					this.graph.getStartSize(lane) : new mxRectangle();
+
+			var bounds = this.graph.getBoundingBoxFromGeometry(children);
+			childBounds[i] = bounds;
+			var childrenY = bounds.y + geo.y - size.height - this.parentBorder;
+			var maxChildrenY = bounds.y + geo.y + bounds.height;
+
+			if (layoutBounds == null)
+			{
+				layoutBounds = new mxRectangle(0, childrenY, 0, maxChildrenY - childrenY);
+			}
+			else
+			{
+				layoutBounds.y = Math.min(layoutBounds.y, childrenY);
+				var maxY = Math.max(layoutBounds.y + layoutBounds.height, maxChildrenY);
+				layoutBounds.height = maxY - layoutBounds.y;
+			}
+		}
+	}
+
+	
+	for (var i = 0; i < this.swimlanes.length; i++)
+	{
+		var lane = this.swimlanes[i];
+		var geo = this.graph.getCellGeometry(lane);
+		
+		if (geo != null)
+		{
+			var children = this.graph.getChildCells(lane);
+			
+			var size = (this.graph.isSwimlane(lane)) ?
+					this.graph.getStartSize(lane) : new mxRectangle();
+
+			var newGeo = geo.clone();
+			
+			var leftGroupBorder = (i == 0) ? this.parentBorder : this.interRankCellSpacing/2;
+			newGeo.x += childBounds[i].x - size.width - leftGroupBorder;
+			newGeo.y = newGeo.y + layoutBounds.y - geo.y - this.parentBorder;
+			
+			newGeo.width = childBounds[i].width + size.width + this.interRankCellSpacing/2 + leftGroupBorder;
+			newGeo.height = layoutBounds.height + size.height + 2 * this.parentBorder;
+			
+			this.graph.model.setGeometry(lane, newGeo);
+			this.graph.moveCells(children, -childBounds[i].x + size.width + leftGroupBorder, 
+					geo.y - layoutBounds.y + this.parentBorder);
+		}
+	}
+};
+
+/**
+ * Function: findRoots
+ * 
+ * Returns all visible children in the given parent which do not have
+ * incoming edges. If the result is empty then the children with the
+ * maximum difference between incoming and outgoing edges are returned.
+ * This takes into account edges that are being promoted to the given
+ * root due to invisible children or collapsed cells.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose children should be checked.
+ * vertices - array of vertices to limit search to
+ */
+mxSwimlaneLayout.prototype.findRoots = function(parent, vertices)
+{
+	var roots = [];
+	
+	if (parent != null && vertices != null)
+	{
+		var model = this.graph.model;
+		var best = null;
+		var maxDiff = -100000;
+		
+		for (var i in vertices)
+		{
+			var cell = vertices[i];
+
+			if (cell != null && model.isVertex(cell) && this.graph.isCellVisible(cell) && model.isAncestor(parent, cell))
+			{
+				var conns = this.getEdges(cell);
+				var fanOut = 0;
+				var fanIn = 0;
+
+				for (var k = 0; k < conns.length; k++)
+				{
+					var src = this.getVisibleTerminal(conns[k], true);
+
+					if (src == cell)
+					{
+						// Only count connection within this swimlane
+						var other = this.getVisibleTerminal(conns[k], false);
+						
+						if (model.isAncestor(parent, other))
+						{
+							fanOut++;
+						}
+					}
+					else if (model.isAncestor(parent, src))
+					{
+						fanIn++;
+					}
+				}
+
+				if (fanIn == 0 && fanOut > 0)
+				{
+					roots.push(cell);
+				}
+
+				var diff = fanOut - fanIn;
+
+				if (diff > maxDiff)
+				{
+					maxDiff = diff;
+					best = cell;
+				}
+			}
+		}
+		
+		if (roots.length == 0 && best != null)
+		{
+			roots.push(best);
+		}
+	}
+	
+	return roots;
+};
+
+/**
+ * Function: getEdges
+ * 
+ * Returns the connected edges for the given cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose edges should be returned.
+ */
+mxSwimlaneLayout.prototype.getEdges = function(cell)
+{
+	var cachedEdges = this.edgesCache.get(cell);
+	
+	if (cachedEdges != null)
+	{
+		return cachedEdges;
+	}
+
+	var model = this.graph.model;
+	var edges = [];
+	var isCollapsed = this.graph.isCellCollapsed(cell);
+	var childCount = model.getChildCount(cell);
+
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = model.getChildAt(cell, i);
+
+		if (this.isPort(child))
+		{
+			edges = edges.concat(model.getEdges(child, true, true));
+		}
+		else if (isCollapsed || !this.graph.isCellVisible(child))
+		{
+			edges = edges.concat(model.getEdges(child, true, true));
+		}
+	}
+
+	edges = edges.concat(model.getEdges(cell, true, true));
+	var result = [];
+	
+	for (var i = 0; i < edges.length; i++)
+	{
+		var source = this.getVisibleTerminal(edges[i], true);
+		var target = this.getVisibleTerminal(edges[i], false);
+		
+		if ((source == target) || ((source != target) && ((target == cell && (this.parent == null || this.graph.isValidAncestor(source, this.parent, this.traverseAncestors))) ||
+			(source == cell && (this.parent == null ||
+					this.graph.isValidAncestor(target, this.parent, this.traverseAncestors))))))
+		{
+			result.push(edges[i]);
+		}
+	}
+
+	this.edgesCache.put(cell, result);
+
+	return result;
+};
+
+/**
+ * Function: getVisibleTerminal
+ * 
+ * Helper function to return visible terminal for edge allowing for ports
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose edges should be returned.
+ * source - Boolean that specifies whether the source or target terminal is to be returned
+ */
+mxSwimlaneLayout.prototype.getVisibleTerminal = function(edge, source)
+{
+	var terminalCache = this.edgesTargetTermCache;
+	
+	if (source)
+	{
+		terminalCache = this.edgeSourceTermCache;
+	}
+
+	var term = terminalCache.get(edge);
+
+	if (term != null)
+	{
+		return term;
+	}
+
+	var state = this.graph.view.getState(edge);
+	
+	var terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
+	
+	if (terminal == null)
+	{
+		terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
+	}
+
+	if (terminal != null)
+	{
+		if (this.isPort(terminal))
+		{
+			terminal = this.graph.model.getParent(terminal);
+		}
+		
+		terminalCache.put(edge, terminal);
+	}
+
+	return terminal;
+};
+
+/**
+ * Function: run
+ * 
+ * The API method used to exercise the layout upon the graph description
+ * and produce a separate description of the vertex position and edge
+ * routing changes made. It runs each stage of the layout that has been
+ * created.
+ */
+mxSwimlaneLayout.prototype.run = function(parent)
+{
+	// Separate out unconnected hierarchies
+	var hierarchyVertices = [];
+	var allVertexSet = [];
+
+	if (this.swimlanes != null && this.swimlanes.length > 0 && parent != null)
+	{
+		var filledVertexSet = Object();
+		
+		for (var i = 0; i < this.swimlanes.length; i++)
+		{
+			this.filterDescendants(this.swimlanes[i], filledVertexSet);
+		}
+
+		this.roots = [];
+		var filledVertexSetEmpty = true;
+
+		// Poor man's isSetEmpty
+		for (var key in filledVertexSet)
+		{
+			if (filledVertexSet[key] != null)
+			{
+				filledVertexSetEmpty = false;
+				break;
+			}
+		}
+
+		// Only test for candidates in each swimlane in order
+		var laneCounter = 0;
+
+		while (!filledVertexSetEmpty && laneCounter < this.swimlanes.length)
+		{
+			var candidateRoots = this.findRoots(this.swimlanes[laneCounter], filledVertexSet);
+			
+			if (candidateRoots.length == 0)
+			{
+				laneCounter++;
+				continue;
+			}
+			
+			// If the candidate root is an unconnected group cell, remove it from
+			// the layout. We may need a custom set that holds such groups and forces
+			// them to be processed for resizing and/or moving.
+			for (var i = 0; i < candidateRoots.length; i++)
+			{
+				var vertexSet = Object();
+				hierarchyVertices.push(vertexSet);
+
+				this.traverse(candidateRoots[i], true, null, allVertexSet, vertexSet,
+						hierarchyVertices, filledVertexSet, laneCounter);
+			}
+
+			for (var i = 0; i < candidateRoots.length; i++)
+			{
+				this.roots.push(candidateRoots[i]);
+			}
+			
+			filledVertexSetEmpty = true;
+			
+			// Poor man's isSetEmpty
+			for (var key in filledVertexSet)
+			{
+				if (filledVertexSet[key] != null)
+				{
+					filledVertexSetEmpty = false;
+					break;
+				}
+			}
+		}
+	}
+	else
+	{
+		// Find vertex set as directed traversal from roots
+
+		for (var i = 0; i < this.roots.length; i++)
+		{
+			var vertexSet = Object();
+			hierarchyVertices.push(vertexSet);
+
+			this.traverse(this.roots[i], true, null, allVertexSet, vertexSet,
+					hierarchyVertices, null);
+		}
+	}
+
+	var tmp = [];
+	
+	for (var key in allVertexSet)
+	{
+		tmp.push(allVertexSet[key]);
+	}
+	
+	this.model = new mxSwimlaneModel(this, tmp, this.roots,
+		parent, this.tightenToSource);
+
+	this.cycleStage(parent);
+	this.layeringStage();
+	
+	this.crossingStage(parent);
+	initialX = this.placementStage(0, parent);
+};
+
+/**
+ * Function: filterDescendants
+ * 
+ * Creates an array of descendant cells
+ */
+mxSwimlaneLayout.prototype.filterDescendants = function(cell, result)
+{
+	var model = this.graph.model;
+
+	if (model.isVertex(cell) && cell != this.parent && model.getParent(cell) != this.parent && this.graph.isCellVisible(cell))
+	{
+		result[mxObjectIdentity.get(cell)] = cell;
+	}
+
+	if (this.traverseAncestors || cell == this.parent
+			&& this.graph.isCellVisible(cell))
+	{
+		var childCount = model.getChildCount(cell);
+
+		for (var i = 0; i < childCount; i++)
+		{
+			var child = model.getChildAt(cell, i);
+			
+			// Ignore ports in the layout vertex list, they are dealt with
+			// in the traversal mechanisms
+			if (!this.isPort(child))
+			{
+				this.filterDescendants(child, result);
+			}
+		}
+	}
+};
+
+/**
+ * Function: isPort
+ * 
+ * Returns true if the given cell is a "port", that is, when connecting to
+ * it, its parent is the connecting vertex in terms of graph traversal
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the port.
+ */
+mxSwimlaneLayout.prototype.isPort = function(cell)
+{
+	if (cell.geometry.relative)
+	{
+		return true;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: getEdgesBetween
+ * 
+ * Returns the edges between the given source and target. This takes into
+ * account collapsed and invisible cells and ports.
+ * 
+ * Parameters:
+ * 
+ * source -
+ * target -
+ * directed -
+ */
+mxSwimlaneLayout.prototype.getEdgesBetween = function(source, target, directed)
+{
+	directed = (directed != null) ? directed : false;
+	var edges = this.getEdges(source);
+	var result = [];
+
+	// Checks if the edge is connected to the correct
+	// cell and returns the first match
+	for (var i = 0; i < edges.length; i++)
+	{
+		var src = this.getVisibleTerminal(edges[i], true);
+		var trg = this.getVisibleTerminal(edges[i], false);
+
+		if ((src == source && trg == target) || (!directed && src == target && trg == source))
+		{
+			result.push(edges[i]);
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Traverses the (directed) graph invoking the given function for each
+ * visited vertex and edge. The function is invoked with the current vertex
+ * and the incoming edge as a parameter. This implementation makes sure
+ * each vertex is only visited once. The function may return false if the
+ * traversal should stop at the given vertex.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> that represents the vertex where the traversal starts.
+ * directed - boolean indicating if edges should only be traversed
+ * from source to target. Default is true.
+ * edge - Optional <mxCell> that represents the incoming edge. This is
+ * null for the first step of the traversal.
+ * allVertices - Array of cell paths for the visited cells.
+ * swimlaneIndex - the laid out order index of the swimlane vertex is contained in
+ */
+mxSwimlaneLayout.prototype.traverse = function(vertex, directed, edge, allVertices, currentComp,
+											hierarchyVertices, filledVertexSet, swimlaneIndex)
+{
+	if (vertex != null && allVertices != null)
+	{
+		// Has this vertex been seen before in any traversal
+		// And if the filled vertex set is populated, only 
+		// process vertices in that it contains
+		var vertexID = mxObjectIdentity.get(vertex);
+		
+		if ((allVertices[vertexID] == null)
+				&& (filledVertexSet == null ? true : filledVertexSet[vertexID] != null))
+		{
+			if (currentComp[vertexID] == null)
+			{
+				currentComp[vertexID] = vertex;
+			}
+			if (allVertices[vertexID] == null)
+			{
+				allVertices[vertexID] = vertex;
+			}
+
+			if (filledVertexSet !== null)
+			{
+				delete filledVertexSet[vertexID];
+			}
+
+			var edges = this.getEdges(vertex);
+			var model = this.graph.model;
+
+			for (var i = 0; i < edges.length; i++)
+			{
+				var otherVertex = this.getVisibleTerminal(edges[i], true);
+				var isSource = otherVertex == vertex;
+				
+				if (isSource)
+				{
+					otherVertex = this.getVisibleTerminal(edges[i], false);
+				}
+
+				var otherIndex = 0;
+				// Get the swimlane index of the other terminal
+				for (otherIndex = 0; otherIndex < this.swimlanes.length; otherIndex++)
+				{
+					if (model.isAncestor(this.swimlanes[otherIndex], otherVertex))
+					{
+						break;
+					}
+				}
+				
+				if (otherIndex >= this.swimlanes.length)
+				{
+					continue;
+				}
+
+				// Traverse if the other vertex is within the same swimlane as
+				// as the current vertex, or if the swimlane index of the other
+				// vertex is greater than that of this vertex
+				if ((otherIndex > swimlaneIndex) ||
+						((!directed || isSource) && otherIndex == swimlaneIndex))
+				{
+					currentComp = this.traverse(otherVertex, directed, edges[i], allVertices,
+							currentComp, hierarchyVertices,
+							filledVertexSet, otherIndex);
+				}
+			}
+		}
+		else
+		{
+			if (currentComp[vertexID] == null)
+			{
+				// We've seen this vertex before, but not in the current component
+				// This component and the one it's in need to be merged
+				for (var i = 0; i < hierarchyVertices.length; i++)
+				{
+					var comp = hierarchyVertices[i];
+
+					if (comp[vertexID] != null)
+					{
+						for (var key in comp)
+						{
+							currentComp[key] = comp[key];
+						}
+						
+						// Remove the current component from the hierarchy set
+						hierarchyVertices.splice(i, 1);
+						return currentComp;
+					}
+				}
+			}
+		}
+	}
+	
+	return currentComp;
+};
+
+/**
+ * Function: cycleStage
+ * 
+ * Executes the cycle stage using mxMinimumCycleRemover.
+ */
+mxSwimlaneLayout.prototype.cycleStage = function(parent)
+{
+	var cycleStage = new mxSwimlaneOrdering(this);
+	cycleStage.execute(parent);
+};
+
+/**
+ * Function: layeringStage
+ * 
+ * Implements first stage of a Sugiyama layout.
+ */
+mxSwimlaneLayout.prototype.layeringStage = function()
+{
+	this.model.initialRank();
+	this.model.fixRanks();
+};
+
+/**
+ * Function: crossingStage
+ * 
+ * Executes the crossing stage using mxMedianHybridCrossingReduction.
+ */
+mxSwimlaneLayout.prototype.crossingStage = function(parent)
+{
+	var crossingStage = new mxMedianHybridCrossingReduction(this);
+	crossingStage.execute(parent);
+};
+
+/**
+ * Function: placementStage
+ * 
+ * Executes the placement stage using mxCoordinateAssignment.
+ */
+mxSwimlaneLayout.prototype.placementStage = function(initialX, parent)
+{
+	var placementStage = new mxCoordinateAssignment(this, this.intraCellSpacing,
+			this.interRankCellSpacing, this.orientation, initialX,
+			this.parallelEdgeSpacing);
+	placementStage.fineTuning = this.fineTuning;
+	placementStage.execute(parent);
+	
+	return placementStage.limitX + this.interHierarchySpacing;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphModel
+ * 
+ * Extends <mxEventSource> to implement a graph model. The graph model acts as
+ * a wrapper around the cells which are in charge of storing the actual graph
+ * datastructure. The model acts as a transactional wrapper with event
+ * notification for all changes, whereas the cells contain the atomic
+ * operations for updating the actual datastructure.
+ * 
+ * Layers:
+ * 
+ * The cell hierarchy in the model must have a top-level root cell which
+ * contains the layers (typically one default layer), which in turn contain the
+ * top-level cells of the layers. This means each cell is contained in a layer.
+ * If no layers are required, then all new cells should be added to the default
+ * layer.
+ * 
+ * Layers are useful for hiding and showing groups of cells, or for placing
+ * groups of cells on top of other cells in the display. To identify a layer,
+ * the <isLayer> function is used. It returns true if the parent of the given
+ * cell is the root of the model.
+ * 
+ * Events:
+ * 
+ * See events section for more details. There is a new set of events for
+ * tracking transactional changes as they happen. The events are called
+ * startEdit for the initial beginUpdate, executed for each executed change
+ * and endEdit for the terminal endUpdate. The executed event contains a
+ * property called change which represents the change after execution.
+ * 
+ * Encoding the model:
+ * 
+ * To encode a graph model, use the following code:
+ * 
+ * (code)
+ * var enc = new mxCodec();
+ * var node = enc.encode(graph.getModel());
+ * (end)
+ * 
+ * This will create an XML node that contains all the model information.
+ * 
+ * Encoding and decoding changes:
+ * 
+ * For the encoding of changes, a graph model listener is required that encodes
+ * each change from the given array of changes.
+ * 
+ * (code)
+ * model.addListener(mxEvent.CHANGE, function(sender, evt)
+ * {
+ *   var changes = evt.getProperty('edit').changes;
+ *   var nodes = [];
+ *   var codec = new mxCodec();
+ * 
+ *   for (var i = 0; i < changes.length; i++)
+ *   {
+ *     nodes.push(codec.encode(changes[i]));
+ *   }
+ *   // do something with the nodes
+ * });
+ * (end)
+ * 
+ * For the decoding and execution of changes, the codec needs a lookup function
+ * that allows it to resolve cell IDs as follows:
+ * 
+ * (code)
+ * var codec = new mxCodec();
+ * codec.lookup = function(id)
+ * {
+ *   return model.getCell(id);
+ * }
+ * (end)
+ * 
+ * For each encoded change (represented by a node), the following code can be
+ * used to carry out the decoding and create a change object.
+ * 
+ * (code)
+ * var changes = [];
+ * var change = codec.decode(node);
+ * change.model = model;
+ * change.execute();
+ * changes.push(change);
+ * (end)
+ * 
+ * The changes can then be dispatched using the model as follows.
+ * 
+ * (code)
+ * var edit = new mxUndoableEdit(model, false);
+ * edit.changes = changes;
+ * 
+ * edit.notify = function()
+ * {
+ *   edit.source.fireEvent(new mxEventObject(mxEvent.CHANGE,
+ *   	'edit', edit, 'changes', edit.changes));
+ *   edit.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,
+ *   	'edit', edit, 'changes', edit.changes));
+ * }
+ * 
+ * model.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', edit));
+ * model.fireEvent(new mxEventObject(mxEvent.CHANGE,
+ * 		'edit', edit, 'changes', changes));
+ * (end)
+ *
+ * Event: mxEvent.CHANGE
+ *
+ * Fires when an undoable edit is dispatched. The <code>edit</code> property
+ * contains the <mxUndoableEdit>. The <code>changes</code> property contains
+ * the array of atomic changes inside the undoable edit. The changes property
+ * is <strong>deprecated</strong>, please use edit.changes instead.
+ *
+ * Example:
+ * 
+ * For finding newly inserted cells, the following code can be used:
+ * 
+ * (code)
+ * graph.model.addListener(mxEvent.CHANGE, function(sender, evt)
+ * {
+ *   var changes = evt.getProperty('edit').changes;
+ * 
+ *   for (var i = 0; i < changes.length; i++)
+ *   {
+ *     var change = changes[i];
+ *     
+ *     if (change instanceof mxChildChange &&
+ *       change.change.previous == null)
+ *     {
+ *       graph.startEditingAtCell(change.child);
+ *       break;
+ *     }
+ *   }
+ * });
+ * (end)
+ * 
+ * 
+ * Event: mxEvent.NOTIFY
+ *
+ * Same as <mxEvent.CHANGE>, this event can be used for classes that need to
+ * implement a sync mechanism between this model and, say, a remote model. In
+ * such a setup, only local changes should trigger a notify event and all
+ * changes should trigger a change event.
+ * 
+ * Event: mxEvent.EXECUTE
+ * 
+ * Fires between begin- and endUpdate and after an atomic change was executed
+ * in the model. The <code>change</code> property contains the atomic change
+ * that was executed.
+ * 
+ * Event: mxEvent.EXECUTED
+ * 
+ * Fires between START_EDIT and END_EDIT after an atomic change was executed.
+ * The <code>change</code> property contains the change that was executed.
+ *
+ * Event: mxEvent.BEGIN_UPDATE
+ *
+ * Fires after the <updateLevel> was incremented in <beginUpdate>. This event
+ * contains no properties.
+ * 
+ * Event: mxEvent.START_EDIT
+ *
+ * Fires after the <updateLevel> was changed from 0 to 1. This event
+ * contains no properties.
+ * 
+ * Event: mxEvent.END_UPDATE
+ * 
+ * Fires after the <updateLevel> was decreased in <endUpdate> but before any
+ * notification or change dispatching. The <code>edit</code> property contains
+ * the <currentEdit>.
+ * 
+ * Event: mxEvent.END_EDIT
+ *
+ * Fires after the <updateLevel> was changed from 1 to 0. This event
+ * contains no properties.
+ * 
+ * Event: mxEvent.BEFORE_UNDO
+ * 
+ * Fires before the change is dispatched after the update level has reached 0
+ * in <endUpdate>. The <code>edit</code> property contains the <curreneEdit>.
+ * 
+ * Event: mxEvent.UNDO
+ * 
+ * Fires after the change was dispatched in <endUpdate>. The <code>edit</code>
+ * property contains the <currentEdit>.
+ * 
+ * Constructor: mxGraphModel
+ * 
+ * Constructs a new graph model. If no root is specified then a new root
+ * <mxCell> with a default layer is created.
+ * 
+ * Parameters:
+ * 
+ * root - <mxCell> that represents the root cell.
+ */
+function mxGraphModel(root)
+{
+	this.currentEdit = this.createUndoableEdit();
+	
+	if (root != null)
+	{
+		this.setRoot(root);
+	}
+	else
+	{
+		this.clear();
+	}
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxGraphModel.prototype = new mxEventSource();
+mxGraphModel.prototype.constructor = mxGraphModel;
+
+/**
+ * Variable: root
+ * 
+ * Holds the root cell, which in turn contains the cells that represent the
+ * layers of the diagram as child cells. That is, the actual elements of the
+ * diagram are supposed to live in the third generation of cells and below.
+ */
+mxGraphModel.prototype.root = null;
+
+/**
+ * Variable: cells
+ * 
+ * Maps from Ids to cells.
+ */
+mxGraphModel.prototype.cells = null;
+
+/**
+ * Variable: maintainEdgeParent
+ * 
+ * Specifies if edges should automatically be moved into the nearest common
+ * ancestor of their terminals. Default is true.
+ */
+mxGraphModel.prototype.maintainEdgeParent = true;
+
+/**
+ * Variable: ignoreRelativeEdgeParent
+ * 
+ * Specifies if relative edge parents should be ignored for finding the nearest
+ * common ancestors of an edge's terminals. Default is true.
+ */
+mxGraphModel.prototype.ignoreRelativeEdgeParent = true;
+
+/**
+ * Variable: createIds
+ * 
+ * Specifies if the model should automatically create Ids for new cells.
+ * Default is true.
+ */
+mxGraphModel.prototype.createIds = true;
+
+/**
+ * Variable: prefix
+ * 
+ * Defines the prefix of new Ids. Default is an empty string.
+ */
+mxGraphModel.prototype.prefix = '';
+
+/**
+ * Variable: postfix
+ * 
+ * Defines the postfix of new Ids. Default is an empty string.
+ */
+mxGraphModel.prototype.postfix = '';
+
+/**
+ * Variable: nextId
+ * 
+ * Specifies the next Id to be created. Initial value is 0.
+ */
+mxGraphModel.prototype.nextId = 0;
+
+/**
+ * Variable: currentEdit
+ * 
+ * Holds the changes for the current transaction. If the transaction is
+ * closed then a new object is created for this variable using
+ * <createUndoableEdit>.
+ */
+mxGraphModel.prototype.currentEdit = null;
+
+/**
+ * Variable: updateLevel
+ * 
+ * Counter for the depth of nested transactions. Each call to <beginUpdate>
+ * will increment this number and each call to <endUpdate> will decrement
+ * it. When the counter reaches 0, the transaction is closed and the
+ * respective events are fired. Initial value is 0.
+ */
+mxGraphModel.prototype.updateLevel = 0;
+
+/**
+ * Variable: endingUpdate
+ * 
+ * True if the program flow is currently inside endUpdate.
+ */
+mxGraphModel.prototype.endingUpdate = false;
+
+/**
+ * Function: clear
+ *
+ * Sets a new root using <createRoot>.
+ */
+mxGraphModel.prototype.clear = function()
+{
+	this.setRoot(this.createRoot());
+};
+
+/**
+ * Function: isCreateIds
+ *
+ * Returns <createIds>.
+ */
+mxGraphModel.prototype.isCreateIds = function()
+{
+	return this.createIds;
+};
+
+/**
+ * Function: setCreateIds
+ *
+ * Sets <createIds>.
+ */
+mxGraphModel.prototype.setCreateIds = function(value)
+{
+	this.createIds = value;
+};
+
+/**
+ * Function: createRoot
+ *
+ * Creates a new root cell with a default layer (child 0).
+ */
+mxGraphModel.prototype.createRoot = function()
+{
+	var cell = new mxCell();
+	cell.insert(new mxCell());
+	
+	return cell;
+};
+
+/**
+ * Function: getCell
+ *
+ * Returns the <mxCell> for the specified Id or null if no cell can be
+ * found for the given Id.
+ *
+ * Parameters:
+ * 
+ * id - A string representing the Id of the cell.
+ */
+mxGraphModel.prototype.getCell = function(id)
+{
+	return (this.cells != null) ? this.cells[id] : null;
+};
+
+/**
+ * Function: filterCells
+ * 
+ * Returns the cells from the given array where the given filter function
+ * returns true.
+ */
+mxGraphModel.prototype.filterCells = function(cells, filter)
+{
+	var result = null;
+	
+	if (cells != null)
+	{
+		result = [];
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (filter(cells[i]))
+			{
+				result.push(cells[i]);
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getDescendants
+ * 
+ * Returns all descendants of the given cell and the cell itself in an array.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose descendants should be returned.
+ */
+mxGraphModel.prototype.getDescendants = function(parent)
+{
+	return this.filterDescendants(null, parent);
+};
+
+/**
+ * Function: filterDescendants
+ * 
+ * Visits all cells recursively and applies the specified filter function
+ * to each cell. If the function returns true then the cell is added
+ * to the resulting array. The parent and result paramters are optional.
+ * If parent is not specified then the recursion starts at <root>.
+ * 
+ * Example:
+ * The following example extracts all vertices from a given model:
+ * (code)
+ * var filter = function(cell)
+ * {
+ * 	return model.isVertex(cell);
+ * }
+ * var vertices = model.filterDescendants(filter);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * filter - JavaScript function that takes an <mxCell> as an argument
+ * and returns a boolean.
+ * parent - Optional <mxCell> that is used as the root of the recursion.
+ */
+mxGraphModel.prototype.filterDescendants = function(filter, parent)
+{
+	// Creates a new array for storing the result
+	var result = [];
+
+	// Recursion starts at the root of the model
+	parent = parent || this.getRoot();
+	
+	// Checks if the filter returns true for the cell
+	// and adds it to the result array
+	if (filter == null || filter(parent))
+	{
+		result.push(parent);
+	}
+	
+	// Visits the children of the cell
+	var childCount = this.getChildCount(parent);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = this.getChildAt(parent, i);
+		result = result.concat(this.filterDescendants(filter, child));
+	}
+
+	return result;
+};
+
+/**
+ * Function: getRoot
+ * 
+ * Returns the root of the model or the topmost parent of the given cell.
+ *
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> that specifies the child.
+ */
+mxGraphModel.prototype.getRoot = function(cell)
+{
+	var root = cell || this.root;
+	
+	if (cell != null)
+	{
+		while (cell != null)
+		{
+			root = cell;
+			cell = this.getParent(cell);
+		}
+	}
+	
+	return root;
+};
+
+/**
+ * Function: setRoot
+ * 
+ * Sets the <root> of the model using <mxRootChange> and adds the change to
+ * the current transaction. This resets all datastructures in the model and
+ * is the preferred way of clearing an existing model. Returns the new
+ * root.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var root = new mxCell();
+ * root.insert(new mxCell());
+ * model.setRoot(root);
+ * (end)
+ *
+ * Parameters:
+ * 
+ * root - <mxCell> that specifies the new root.
+ */
+mxGraphModel.prototype.setRoot = function(root)
+{
+	this.execute(new mxRootChange(this, root));
+	
+	return root;
+};
+
+/**
+ * Function: rootChanged
+ * 
+ * Inner callback to change the root of the model and update the internal
+ * datastructures, such as <cells> and <nextId>. Returns the previous root.
+ *
+ * Parameters:
+ * 
+ * root - <mxCell> that specifies the new root.
+ */
+mxGraphModel.prototype.rootChanged = function(root)
+{
+	var oldRoot = this.root;
+	this.root = root;
+	
+	// Resets counters and datastructures
+	this.nextId = 0;
+	this.cells = null;
+	this.cellAdded(root);
+	
+	return oldRoot;
+};
+
+/**
+ * Function: isRoot
+ * 
+ * Returns true if the given cell is the root of the model and a non-null
+ * value.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the possible root.
+ */
+mxGraphModel.prototype.isRoot = function(cell)
+{
+	return cell != null && this.root == cell;
+};
+
+/**
+ * Function: isLayer
+ * 
+ * Returns true if <isRoot> returns true for the parent of the given cell.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the possible layer.
+ */
+mxGraphModel.prototype.isLayer = function(cell)
+{
+	return this.isRoot(this.getParent(cell));
+};
+
+/**
+ * Function: isAncestor
+ * 
+ * Returns true if the given parent is an ancestor of the given child.
+ *
+ * Parameters:
+ * 
+ * parent - <mxCell> that specifies the parent.
+ * child - <mxCell> that specifies the child.
+ */
+mxGraphModel.prototype.isAncestor = function(parent, child)
+{
+	while (child != null && child != parent)
+	{
+		child = this.getParent(child);
+	}
+	
+	return child == parent;
+};
+
+/**
+ * Function: contains
+ * 
+ * Returns true if the model contains the given <mxCell>.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that specifies the cell.
+ */
+mxGraphModel.prototype.contains = function(cell)
+{
+	return this.isAncestor(this.root, cell);
+};
+
+/**
+ * Function: getParent
+ * 
+ * Returns the parent of the given cell.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose parent should be returned.
+ */
+mxGraphModel.prototype.getParent = function(cell)
+{
+	return (cell != null) ? cell.getParent() : null;
+};
+
+/**
+ * Function: add
+ * 
+ * Adds the specified child to the parent at the given index using
+ * <mxChildChange> and adds the change to the current transaction. If no
+ * index is specified then the child is appended to the parent's array of
+ * children. Returns the inserted child.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> that specifies the parent to contain the child.
+ * child - <mxCell> that specifies the child to be inserted.
+ * index - Optional integer that specifies the index of the child.
+ */
+mxGraphModel.prototype.add = function(parent, child, index)
+{
+	if (child != parent && parent != null && child != null)
+	{	
+		// Appends the child if no index was specified
+		if (index == null)
+		{
+			index = this.getChildCount(parent);
+		}
+		
+		var parentChanged = parent != this.getParent(child);
+		this.execute(new mxChildChange(this, parent, child, index));
+
+		// Maintains the edges parents by moving the edges
+		// into the nearest common ancestor of its
+		// terminals
+		if (this.maintainEdgeParent && parentChanged)
+		{
+			this.updateEdgeParents(child);
+		}
+	}
+	
+	return child;
+};
+
+/**
+ * Function: cellAdded
+ * 
+ * Inner callback to update <cells> when a cell has been added. This
+ * implementation resolves collisions by creating new Ids. To change the
+ * ID of a cell after it was inserted into the model, use the following
+ * code:
+ * 
+ * (code
+ * delete model.cells[cell.getId()];
+ * cell.setId(newId);
+ * model.cells[cell.getId()] = cell;
+ * (end)
+ *
+ * If the change of the ID should be part of the command history, then the
+ * cell should be removed from the model and a clone with the new ID should
+ * be reinserted into the model instead.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that specifies the cell that has been added.
+ */
+mxGraphModel.prototype.cellAdded = function(cell)
+{
+	if (cell != null)
+	{
+		// Creates an Id for the cell if not Id exists
+		if (cell.getId() == null && this.createIds)
+		{
+			cell.setId(this.createId(cell));
+		}
+		
+		if (cell.getId() != null)
+		{
+			var collision = this.getCell(cell.getId());
+			
+			if (collision != cell)
+			{	
+				// Creates new Id for the cell
+				// as long as there is a collision
+				while (collision != null)
+				{
+					cell.setId(this.createId(cell));
+					collision = this.getCell(cell.getId());
+				}
+				
+				// Lazily creates the cells dictionary
+				if (this.cells == null)
+				{
+					this.cells = new Object();
+				}
+				
+				this.cells[cell.getId()] = cell;
+			}
+		}
+		
+		// Makes sure IDs of deleted cells are not reused
+		if (mxUtils.isNumeric(cell.getId()))
+		{
+			this.nextId = Math.max(this.nextId, cell.getId());
+		}
+		
+		// Recursively processes child cells
+		var childCount = this.getChildCount(cell);
+		
+		for (var i=0; i<childCount; i++)
+		{
+			this.cellAdded(this.getChildAt(cell, i));
+		}
+	}
+};
+
+/**
+ * Function: createId
+ * 
+ * Hook method to create an Id for the specified cell. This implementation
+ * concatenates <prefix>, id and <postfix> to create the Id and increments
+ * <nextId>. The cell is ignored by this implementation, but can be used in
+ * overridden methods to prefix the Ids with eg. the cell type.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> to create the Id for.
+ */
+mxGraphModel.prototype.createId = function(cell)
+{
+	var id = this.nextId;
+	this.nextId++;
+	
+	return this.prefix + id + this.postfix;
+};
+
+/**
+ * Function: updateEdgeParents
+ * 
+ * Updates the parent for all edges that are connected to cell or one of
+ * its descendants using <updateEdgeParent>.
+ */
+mxGraphModel.prototype.updateEdgeParents = function(cell, root)
+{
+	// Gets the topmost node of the hierarchy
+	root = root || this.getRoot(cell);
+	
+	// Updates edges on children first
+	var childCount = this.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = this.getChildAt(cell, i);
+		this.updateEdgeParents(child, root);
+	}
+	
+	// Updates the parents of all connected edges
+	var edgeCount = this.getEdgeCount(cell);
+	var edges = [];
+
+	for (var i = 0; i < edgeCount; i++)
+	{
+		edges.push(this.getEdgeAt(cell, i));
+	}
+	
+	for (var i = 0; i < edges.length; i++)
+	{
+		var edge = edges[i];
+		
+		// Updates edge parent if edge and child have
+		// a common root node (does not need to be the
+		// model root node)
+		if (this.isAncestor(root, edge))
+		{
+			this.updateEdgeParent(edge, root);
+		}
+	}
+};
+
+/**
+ * Function: updateEdgeParent
+ *
+ * Inner callback to update the parent of the specified <mxCell> to the
+ * nearest-common-ancestor of its two terminals.
+ *
+ * Parameters:
+ * 
+ * edge - <mxCell> that specifies the edge.
+ * root - <mxCell> that represents the current root of the model.
+ */
+mxGraphModel.prototype.updateEdgeParent = function(edge, root)
+{
+	var source = this.getTerminal(edge, true);
+	var target = this.getTerminal(edge, false);
+	var cell = null;
+	
+	// Uses the first non-relative descendants of the source terminal
+	while (source != null && !this.isEdge(source) &&
+		source.geometry != null && source.geometry.relative)
+	{
+		source = this.getParent(source);
+	}
+	
+	// Uses the first non-relative descendants of the target terminal
+	while (target != null && this.ignoreRelativeEdgeParent &&
+		!this.isEdge(target) && target.geometry != null && 
+		target.geometry.relative)
+	{
+		target = this.getParent(target);
+	}
+	
+	if (this.isAncestor(root, source) && this.isAncestor(root, target))
+	{
+		if (source == target)
+		{
+			cell = this.getParent(source);
+		}
+		else
+		{
+			cell = this.getNearestCommonAncestor(source, target);
+		}
+
+		if (cell != null && (this.getParent(cell) != this.root ||
+			this.isAncestor(cell, edge)) && this.getParent(edge) != cell)
+		{
+			var geo = this.getGeometry(edge);
+			
+			if (geo != null)
+			{
+				var origin1 = this.getOrigin(this.getParent(edge));
+				var origin2 = this.getOrigin(cell);
+				
+				var dx = origin2.x - origin1.x;
+				var dy = origin2.y - origin1.y;
+				
+				geo = geo.clone();
+				geo.translate(-dx, -dy);
+				this.setGeometry(edge, geo);
+			}
+
+			this.add(cell, edge, this.getChildCount(cell));
+		}
+	}
+};
+
+/**
+ * Function: getOrigin
+ * 
+ * Returns the absolute, accumulated origin for the children inside the
+ * given parent as an <mxPoint>.
+ */
+mxGraphModel.prototype.getOrigin = function(cell)
+{
+	var result = null;
+	
+	if (cell != null)
+	{
+		result = this.getOrigin(this.getParent(cell));
+		
+		if (!this.isEdge(cell))
+		{
+			var geo = this.getGeometry(cell);
+			
+			if (geo != null)
+			{
+				result.x += geo.x;
+				result.y += geo.y;
+			}
+		}
+	}
+	else
+	{
+		result = new mxPoint();
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getNearestCommonAncestor
+ * 
+ * Returns the nearest common ancestor for the specified cells.
+ *
+ * Parameters:
+ * 
+ * cell1 - <mxCell> that specifies the first cell in the tree.
+ * cell2 - <mxCell> that specifies the second cell in the tree.
+ */
+mxGraphModel.prototype.getNearestCommonAncestor = function(cell1, cell2)
+{
+	if (cell1 != null && cell2 != null)
+	{		
+		// Creates the cell path for the second cell
+		var path = mxCellPath.create(cell2);
+
+		if (path != null && path.length > 0)
+		{
+			// Bubbles through the ancestors of the first
+			// cell to find the nearest common ancestor.
+			var cell = cell1;
+			var current = mxCellPath.create(cell);
+			
+			// Inverts arguments
+			if (path.length < current.length)
+			{
+				cell = cell2;
+				var tmp = current;
+				current = path;
+				path = tmp;
+			}
+			
+			while (cell != null)
+			{
+				var parent = this.getParent(cell);
+				
+				// Checks if the cell path is equal to the beginning of the given cell path
+				if (path.indexOf(current + mxCellPath.PATH_SEPARATOR) == 0 && parent != null)
+				{
+					return cell;
+				}
+				
+				current = mxCellPath.getParentPath(current);
+				cell = parent;
+			}
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: remove
+ * 
+ * Removes the specified cell from the model using <mxChildChange> and adds
+ * the change to the current transaction. This operation will remove the
+ * cell and all of its children from the model. Returns the removed cell.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that should be removed.
+ */
+mxGraphModel.prototype.remove = function(cell)
+{
+	if (cell == this.root)
+	{
+		this.setRoot(null);
+	}
+	else if (this.getParent(cell) != null)
+	{
+		this.execute(new mxChildChange(this, null, cell));
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: cellRemoved
+ * 
+ * Inner callback to update <cells> when a cell has been removed.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that specifies the cell that has been removed.
+ */
+mxGraphModel.prototype.cellRemoved = function(cell)
+{
+	if (cell != null && this.cells != null)
+	{
+		// Recursively processes child cells
+		var childCount = this.getChildCount(cell);
+		
+		for (var i = childCount - 1; i >= 0; i--)
+		{
+			this.cellRemoved(this.getChildAt(cell, i));
+		}
+		
+		// Removes the dictionary entry for the cell
+		if (this.cells != null && cell.getId() != null)
+		{
+			delete this.cells[cell.getId()];
+		}
+	}
+};
+
+/**
+ * Function: parentForCellChanged
+ * 
+ * Inner callback to update the parent of a cell using <mxCell.insert>
+ * on the parent and return the previous parent.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> to update the parent for.
+ * parent - <mxCell> that specifies the new parent of the cell.
+ * index - Optional integer that defines the index of the child
+ * in the parent's child array.
+ */
+mxGraphModel.prototype.parentForCellChanged = function(cell, parent, index)
+{
+	var previous = this.getParent(cell);
+	
+	if (parent != null)
+	{
+		if (parent != previous || previous.getIndex(cell) != index)
+		{
+			parent.insert(cell, index);
+		}
+	}
+	else if (previous != null)
+	{
+		var oldIndex = previous.getIndex(cell);
+		previous.remove(oldIndex);
+	}
+	
+	// Checks if the previous parent was already in the
+	// model and avoids calling cellAdded if it was.
+	if (!this.contains(previous) && parent != null)
+	{
+		this.cellAdded(cell);
+	}
+	else if (parent == null)
+	{
+		this.cellRemoved(cell);
+	}
+	
+	return previous;
+};
+
+/**
+ * Function: getChildCount
+ *
+ * Returns the number of children in the given cell.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose number of children should be returned.
+ */
+mxGraphModel.prototype.getChildCount = function(cell)
+{
+	return (cell != null) ? cell.getChildCount() : 0;
+};
+
+/**
+ * Function: getChildAt
+ *
+ * Returns the child of the given <mxCell> at the given index.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the parent.
+ * index - Integer that specifies the index of the child to be returned.
+ */
+mxGraphModel.prototype.getChildAt = function(cell, index)
+{
+	return (cell != null) ? cell.getChildAt(index) : null;
+};
+
+/**
+ * Function: getChildren
+ * 
+ * Returns all children of the given <mxCell> as an array of <mxCells>. The
+ * return value should be only be read.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> the represents the parent.
+ */
+mxGraphModel.prototype.getChildren = function(cell)
+{
+	return (cell != null) ? cell.children : null;
+};
+	
+/**
+ * Function: getChildVertices
+ * 
+ * Returns the child vertices of the given parent.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose child vertices should be returned.
+ */
+mxGraphModel.prototype.getChildVertices = function(parent)
+{
+	return this.getChildCells(parent, true, false);
+};
+		
+/**
+ * Function: getChildEdges
+ * 
+ * Returns the child edges of the given parent.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose child edges should be returned.
+ */
+mxGraphModel.prototype.getChildEdges = function(parent)
+{
+	return this.getChildCells(parent, false, true);
+};
+
+/**
+ * Function: getChildCells
+ * 
+ * Returns the children of the given cell that are vertices and/or edges
+ * depending on the arguments.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> the represents the parent.
+ * vertices - Boolean indicating if child vertices should be returned.
+ * Default is false.
+ * edges - Boolean indicating if child edges should be returned.
+ * Default is false.
+ */
+mxGraphModel.prototype.getChildCells = function(parent, vertices, edges)
+{
+	vertices = (vertices != null) ? vertices : false;
+	edges = (edges != null) ? edges : false;
+	
+	var childCount = this.getChildCount(parent);
+	var result = [];
+
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = this.getChildAt(parent, i);
+
+		if ((!edges && !vertices) || (edges && this.isEdge(child)) ||
+			(vertices && this.isVertex(child)))
+		{
+			result.push(child);
+		}
+	}
+
+	return result;
+};
+		
+/**
+ * Function: getTerminal
+ * 
+ * Returns the source or target <mxCell> of the given edge depending on the
+ * value of the boolean parameter.
+ *
+ * Parameters:
+ * 
+ * edge - <mxCell> that specifies the edge.
+ * isSource - Boolean indicating which end of the edge should be returned.
+ */
+mxGraphModel.prototype.getTerminal = function(edge, isSource)
+{
+	return (edge != null) ? edge.getTerminal(isSource) : null;
+};
+
+/**
+ * Function: setTerminal
+ * 
+ * Sets the source or target terminal of the given <mxCell> using
+ * <mxTerminalChange> and adds the change to the current transaction.
+ * This implementation updates the parent of the edge using <updateEdgeParent>
+ * if required.
+ *
+ * Parameters:
+ * 
+ * edge - <mxCell> that specifies the edge.
+ * terminal - <mxCell> that specifies the new terminal.
+ * isSource - Boolean indicating if the terminal is the new source or
+ * target terminal of the edge.
+ */
+mxGraphModel.prototype.setTerminal = function(edge, terminal, isSource)
+{
+	var terminalChanged = terminal != this.getTerminal(edge, isSource);
+	this.execute(new mxTerminalChange(this, edge, terminal, isSource));
+	
+	if (this.maintainEdgeParent && terminalChanged)
+	{
+		this.updateEdgeParent(edge, this.getRoot());
+	}
+	
+	return terminal;
+};
+	
+/**
+ * Function: setTerminals
+ * 
+ * Sets the source and target <mxCell> of the given <mxCell> in a single
+ * transaction using <setTerminal> for each end of the edge.
+ *
+ * Parameters:
+ * 
+ * edge - <mxCell> that specifies the edge.
+ * source - <mxCell> that specifies the new source terminal.
+ * target - <mxCell> that specifies the new target terminal.
+ */
+mxGraphModel.prototype.setTerminals = function(edge, source, target)
+{
+	this.beginUpdate();
+	try
+	{
+		this.setTerminal(edge, source, true);
+		this.setTerminal(edge, target, false);
+	}
+	finally
+	{
+		this.endUpdate();
+	}
+};
+
+/**
+ * Function: terminalForCellChanged
+ * 
+ * Inner helper function to update the terminal of the edge using
+ * <mxCell.insertEdge> and return the previous terminal.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> that specifies the edge to be updated.
+ * terminal - <mxCell> that specifies the new terminal.
+ * isSource - Boolean indicating if the terminal is the new source or
+ * target terminal of the edge.
+ */
+mxGraphModel.prototype.terminalForCellChanged = function(edge, terminal, isSource)
+{
+	var previous = this.getTerminal(edge, isSource);
+	
+	if (terminal != null)
+	{
+		terminal.insertEdge(edge, isSource);
+	}
+	else if (previous != null)
+	{
+		previous.removeEdge(edge, isSource);
+	}
+	
+	return previous;
+};
+
+/**
+ * Function: getEdgeCount
+ * 
+ * Returns the number of distinct edges connected to the given cell.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the vertex.
+ */
+mxGraphModel.prototype.getEdgeCount = function(cell)
+{
+	return (cell != null) ? cell.getEdgeCount() : 0;
+};
+
+/**
+ * Function: getEdgeAt
+ * 
+ * Returns the edge of cell at the given index.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that specifies the vertex.
+ * index - Integer that specifies the index of the edge
+ * to return.
+ */
+mxGraphModel.prototype.getEdgeAt = function(cell, index)
+{
+	return (cell != null) ? cell.getEdgeAt(index) : null;
+};
+	
+/**
+ * Function: getDirectedEdgeCount
+ * 
+ * Returns the number of incoming or outgoing edges, ignoring the given
+ * edge.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose edge count should be returned.
+ * outgoing - Boolean that specifies if the number of outgoing or
+ * incoming edges should be returned.
+ * ignoredEdge - <mxCell> that represents an edge to be ignored.
+ */
+mxGraphModel.prototype.getDirectedEdgeCount = function(cell, outgoing, ignoredEdge)
+{
+	var count = 0;
+	var edgeCount = this.getEdgeCount(cell);
+
+	for (var i = 0; i < edgeCount; i++)
+	{
+		var edge = this.getEdgeAt(cell, i);
+
+		if (edge != ignoredEdge && this.getTerminal(edge, outgoing) == cell)
+		{
+			count++;
+		}
+	}
+
+	return count;
+};
+
+/**
+ * Function: getConnections
+ * 
+ * Returns all edges of the given cell without loops.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose edges should be returned.
+ * 
+ */
+mxGraphModel.prototype.getConnections = function(cell)
+{
+	return this.getEdges(cell, true, true, false);
+};
+
+/**
+ * Function: getIncomingEdges
+ * 
+ * Returns the incoming edges of the given cell without loops.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose incoming edges should be returned.
+ * 
+ */
+mxGraphModel.prototype.getIncomingEdges = function(cell)
+{
+	return this.getEdges(cell, true, false, false);
+};
+
+/**
+ * Function: getOutgoingEdges
+ * 
+ * Returns the outgoing edges of the given cell without loops.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose outgoing edges should be returned.
+ * 
+ */
+mxGraphModel.prototype.getOutgoingEdges = function(cell)
+{
+	return this.getEdges(cell, false, true, false);
+};
+
+/**
+ * Function: getEdges
+ * 
+ * Returns all distinct edges connected to this cell as a new array of
+ * <mxCells>. If at least one of incoming or outgoing is true, then loops
+ * are ignored, otherwise if both are false, then all edges connected to
+ * the given cell are returned including loops.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that specifies the cell.
+ * incoming - Optional boolean that specifies if incoming edges should be
+ * returned. Default is true.
+ * outgoing - Optional boolean that specifies if outgoing edges should be
+ * returned. Default is true.
+ * includeLoops - Optional boolean that specifies if loops should be returned.
+ * Default is true. 
+ */
+mxGraphModel.prototype.getEdges = function(cell, incoming, outgoing, includeLoops)
+{
+	incoming = (incoming != null) ? incoming : true;
+	outgoing = (outgoing != null) ? outgoing : true;
+	includeLoops = (includeLoops != null) ? includeLoops : true;
+	
+	var edgeCount = this.getEdgeCount(cell);
+	var result = [];
+
+	for (var i = 0; i < edgeCount; i++)
+	{
+		var edge = this.getEdgeAt(cell, i);
+		var so